Implements vertical writing
This commit is contained in:
		
							parent
							
								
									4247339d28
								
							
						
					
					
						commit
						c5b8ee6a91
					
				
							
								
								
									
										10
									
								
								src/bidi.js
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/bidi.js
									
									
									
									
									
								
							| @ -139,16 +139,16 @@ var bidi = PDFJS.bidi = (function bidiClosure() { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function BidiResult(str, isLTR) { |   function BidiResult(str, isLTR, vertical) { | ||||||
|     this.str = str; |     this.str = str; | ||||||
|     this.ltr = isLTR; |     this.dir = vertical ? 'ttb' : isLTR ? 'ltr' : 'rtl'; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function bidi(str, startLevel) { |   function bidi(str, startLevel, vertical) { | ||||||
|     var isLTR = true; |     var isLTR = true; | ||||||
|     var strLength = str.length; |     var strLength = str.length; | ||||||
|     if (strLength === 0) |     if (strLength === 0 || vertical) | ||||||
|       return new BidiResult(str, isLTR); |       return new BidiResult(str, isLTR, vertical); | ||||||
| 
 | 
 | ||||||
|     // get types, fill arrays
 |     // get types, fill arrays
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -896,6 +896,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | |||||||
|       var textSelection = textLayer && !skipTextSelection ? true : false; |       var textSelection = textLayer && !skipTextSelection ? true : false; | ||||||
|       var textRenderingMode = current.textRenderingMode; |       var textRenderingMode = current.textRenderingMode; | ||||||
|       var canvasWidth = 0.0; |       var canvasWidth = 0.0; | ||||||
|  |       var vertical = font.vertical; | ||||||
|  |       var defaultVMetrics = font.defaultVMetrics; | ||||||
| 
 | 
 | ||||||
|       // Type3 fonts - each glyph is a "mini-PDF"
 |       // Type3 fonts - each glyph is a "mini-PDF"
 | ||||||
|       if (font.coded) { |       if (font.coded) { | ||||||
| @ -969,25 +971,37 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | |||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|           var character = glyph.fontChar; |           var character = glyph.fontChar; | ||||||
|           var charWidth = glyph.width * fontSize * current.fontMatrix[0] + |           var vmetric = glyph.vmetric || defaultVMetrics; | ||||||
|  |           if (vertical) { | ||||||
|  |             var vx = vmetric[1] * fontSize * current.fontMatrix[0]; | ||||||
|  |             var vy = vmetric[2] * fontSize * current.fontMatrix[0]; | ||||||
|  |           } | ||||||
|  |           var width = vmetric ? -vmetric[0] : glyph.width; | ||||||
|  |           var charWidth = width * fontSize * current.fontMatrix[0] + | ||||||
|                           charSpacing * current.fontDirection; |                           charSpacing * current.fontDirection; | ||||||
| 
 | 
 | ||||||
|           if (!glyph.disabled) { |           if (!glyph.disabled) { | ||||||
|             var scaledX = x / fontSizeScale; |             if (vertical) { | ||||||
|  |               var scaledX = vx / fontSizeScale; | ||||||
|  |               var scaledY = (x + vy) / fontSizeScale; | ||||||
|  |             } else { | ||||||
|  |               var scaledX = x / fontSizeScale; | ||||||
|  |               var scaledY = 0; | ||||||
|  |             } | ||||||
|             switch (textRenderingMode) { |             switch (textRenderingMode) { | ||||||
|               default: // other unsupported rendering modes
 |               default: // other unsupported rendering modes
 | ||||||
|               case TextRenderingMode.FILL: |               case TextRenderingMode.FILL: | ||||||
|               case TextRenderingMode.FILL_ADD_TO_PATH: |               case TextRenderingMode.FILL_ADD_TO_PATH: | ||||||
|                 ctx.fillText(character, scaledX, 0); |                 ctx.fillText(character, scaledX, scaledY); | ||||||
|                 break; |                 break; | ||||||
|               case TextRenderingMode.STROKE: |               case TextRenderingMode.STROKE: | ||||||
|               case TextRenderingMode.STROKE_ADD_TO_PATH: |               case TextRenderingMode.STROKE_ADD_TO_PATH: | ||||||
|                 ctx.strokeText(character, scaledX, 0); |                 ctx.strokeText(character, scaledX, scaledY); | ||||||
|                 break; |                 break; | ||||||
|               case TextRenderingMode.FILL_STROKE: |               case TextRenderingMode.FILL_STROKE: | ||||||
|               case TextRenderingMode.FILL_STROKE_ADD_TO_PATH: |               case TextRenderingMode.FILL_STROKE_ADD_TO_PATH: | ||||||
|                 ctx.fillText(character, scaledX, 0); |                 ctx.fillText(character, scaledX, scaledY); | ||||||
|                 ctx.strokeText(character, scaledX, 0); |                 ctx.strokeText(character, scaledX, scaledY); | ||||||
|                 break; |                 break; | ||||||
|               case TextRenderingMode.INVISIBLE: |               case TextRenderingMode.INVISIBLE: | ||||||
|               case TextRenderingMode.ADD_TO_PATH: |               case TextRenderingMode.ADD_TO_PATH: | ||||||
| @ -995,7 +1009,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | |||||||
|             } |             } | ||||||
|             if (textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG) { |             if (textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG) { | ||||||
|               var clipCtx = this.getCurrentTextClipping(); |               var clipCtx = this.getCurrentTextClipping(); | ||||||
|               clipCtx.fillText(character, scaledX, 0); |               clipCtx.fillText(character, scaledX, scaledY); | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
| @ -1003,12 +1017,23 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | |||||||
| 
 | 
 | ||||||
|           canvasWidth += charWidth; |           canvasWidth += charWidth; | ||||||
|         } |         } | ||||||
|         current.x += x * textHScale; |         if (vertical) { | ||||||
|  |           current.y -= x * textHScale; | ||||||
|  |         } else { | ||||||
|  |           current.x += x * textHScale; | ||||||
|  |         } | ||||||
|         ctx.restore(); |         ctx.restore(); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (textSelection) { |       if (textSelection) { | ||||||
|         geom.canvasWidth = canvasWidth; |         geom.canvasWidth = canvasWidth; | ||||||
|  |         if (vertical) { | ||||||
|  |           var vmetric = font.defaultVMetrics; | ||||||
|  |           geom.x -= vmetric[1] * fontSize * current.fontMatrix[0] / | ||||||
|  |                     fontSizeScale * geom.hScale; | ||||||
|  |           geom.y += vmetric[2] * fontSize * current.fontMatrix[0] / | ||||||
|  |                     fontSizeScale * geom.vScale; | ||||||
|  |         } | ||||||
|         this.textLayer.appendText(geom); |         this.textLayer.appendText(geom); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
| @ -1027,6 +1052,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | |||||||
|       var geom; |       var geom; | ||||||
|       var canvasWidth = 0.0; |       var canvasWidth = 0.0; | ||||||
|       var textSelection = textLayer ? true : false; |       var textSelection = textLayer ? true : false; | ||||||
|  |       var vertical = font.vertical; | ||||||
| 
 | 
 | ||||||
|       if (textSelection) { |       if (textSelection) { | ||||||
|         ctx.save(); |         ctx.save(); | ||||||
| @ -1039,7 +1065,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | |||||||
|         var e = arr[i]; |         var e = arr[i]; | ||||||
|         if (isNum(e)) { |         if (isNum(e)) { | ||||||
|           var spacingLength = -e * fontSize * textHScale; |           var spacingLength = -e * fontSize * textHScale; | ||||||
|           current.x += spacingLength; |           if (vertical) { | ||||||
|  |             current.y += spacingLength; | ||||||
|  |           } else { | ||||||
|  |             current.x += spacingLength; | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|           if (textSelection) |           if (textSelection) | ||||||
|             canvasWidth += spacingLength; |             canvasWidth += spacingLength; | ||||||
| @ -1055,6 +1085,14 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | |||||||
| 
 | 
 | ||||||
|       if (textSelection) { |       if (textSelection) { | ||||||
|         geom.canvasWidth = canvasWidth; |         geom.canvasWidth = canvasWidth; | ||||||
|  |         if (vertical) { | ||||||
|  |           var fontSizeScale = current.fontSizeScale; | ||||||
|  |           var vmetric = font.defaultVMetrics; | ||||||
|  |           geom.x -= vmetric[1] * fontSize * current.fontMatrix[0] / | ||||||
|  |                     fontSizeScale * geom.hScale; | ||||||
|  |           geom.y += vmetric[2] * fontSize * current.fontMatrix[0] / | ||||||
|  |                     fontSizeScale * geom.vScale; | ||||||
|  |         } | ||||||
|         this.textLayer.appendText(geom); |         this.textLayer.appendText(geom); | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  | |||||||
| @ -796,7 +796,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { | |||||||
|           } // switch
 |           } // switch
 | ||||||
| 
 | 
 | ||||||
|           if (chunk !== '') { |           if (chunk !== '') { | ||||||
|             bidiTexts.push(PDFJS.bidi(chunk, -1)); |             var bidiText = PDFJS.bidi(chunk, -1, font.vertical); | ||||||
|  |             bidiTexts.push(bidiText); | ||||||
| 
 | 
 | ||||||
|             chunk = ''; |             chunk = ''; | ||||||
|           } |           } | ||||||
| @ -831,10 +832,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { | |||||||
|           }; |           }; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         var cidEncoding = baseDict.get('Encoding'); |  | ||||||
|         if (isName(cidEncoding)) |  | ||||||
|           properties.cidEncoding = cidEncoding.name; |  | ||||||
| 
 |  | ||||||
|         var cidToGidMap = dict.get('CIDToGIDMap'); |         var cidToGidMap = dict.get('CIDToGIDMap'); | ||||||
|         if (isStream(cidToGidMap)) |         if (isStream(cidToGidMap)) | ||||||
|           properties.cidToGidMap = this.readCidToGidMap(cidToGidMap); |           properties.cidToGidMap = this.readCidToGidMap(cidToGidMap); | ||||||
| @ -1031,6 +1028,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { | |||||||
|                                                    properties) { |                                                    properties) { | ||||||
|       var glyphsWidths = []; |       var glyphsWidths = []; | ||||||
|       var defaultWidth = 0; |       var defaultWidth = 0; | ||||||
|  |       var glyphsVMetrics = []; | ||||||
|  |       var defaultVMetrics; | ||||||
|       if (properties.composite) { |       if (properties.composite) { | ||||||
|         defaultWidth = dict.get('DW') || 1000; |         defaultWidth = dict.get('DW') || 1000; | ||||||
| 
 | 
 | ||||||
| @ -1049,6 +1048,26 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { | |||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         if (properties.vertical) { | ||||||
|  |           var vmetrics = dict.get('DW2') || [880, -1000]; | ||||||
|  |           defaultVMetrics = [vmetrics[1], vmetrics[1] / 2, vmetrics[0]]; | ||||||
|  |           vmetrics = dict.get('W2'); | ||||||
|  |           if (vmetrics) { | ||||||
|  |             for (var i = 0, ii = vmetrics.length; i < ii; i++) { | ||||||
|  |               var start = vmetrics[i++]; | ||||||
|  |               var code = xref.fetchIfRef(vmetrics[i]); | ||||||
|  |               if (isArray(code)) { | ||||||
|  |                 for (var j = 0, jj = code.length; j < jj; j++) | ||||||
|  |                   glyphsVMetrics[start++] = [code[j++], code[j++], code[j]]; | ||||||
|  |               } else { | ||||||
|  |                 var vmetric = [vmetrics[++i], vmetrics[++i], vmetrics[++i]]; | ||||||
|  |                 for (var j = start; j <= code; j++) | ||||||
|  |                   glyphsVMetrics[j] = vmetric; | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|       } else { |       } else { | ||||||
|         var firstChar = properties.firstChar; |         var firstChar = properties.firstChar; | ||||||
|         var widths = dict.get('Widths'); |         var widths = dict.get('Widths'); | ||||||
| @ -1089,6 +1108,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { | |||||||
| 
 | 
 | ||||||
|       properties.defaultWidth = defaultWidth; |       properties.defaultWidth = defaultWidth; | ||||||
|       properties.widths = glyphsWidths; |       properties.widths = glyphsWidths; | ||||||
|  |       properties.defaultVMetrics = defaultVMetrics; | ||||||
|  |       properties.vmetrics = glyphsVMetrics; | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     isSerifFont: function PartialEvaluator_isSerifFont(baseFontName) { |     isSerifFont: function PartialEvaluator_isSerifFont(baseFontName) { | ||||||
| @ -1260,6 +1281,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { | |||||||
|         italicAngle: descriptor.get('ItalicAngle'), |         italicAngle: descriptor.get('ItalicAngle'), | ||||||
|         coded: false |         coded: false | ||||||
|       }; |       }; | ||||||
|  | 
 | ||||||
|  |       if (composite) { | ||||||
|  |         var cidEncoding = baseDict.get('Encoding'); | ||||||
|  |         if (isName(cidEncoding)) { | ||||||
|  |           properties.cidEncoding = cidEncoding.name; | ||||||
|  |           properties.vertical = /-V$/.test(cidEncoding.name); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|       this.extractWidths(dict, xref, descriptor, properties); |       this.extractWidths(dict, xref, descriptor, properties); | ||||||
|       this.extractDataStructures(dict, baseDict, xref, properties); |       this.extractDataStructures(dict, baseDict, xref, properties); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								src/fonts.js
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								src/fonts.js
									
									
									
									
									
								
							| @ -2363,6 +2363,11 @@ var Font = (function FontClosure() { | |||||||
|     // Trying to fix encoding using glyph CIDSystemInfo.
 |     // Trying to fix encoding using glyph CIDSystemInfo.
 | ||||||
|     this.loadCidToUnicode(properties); |     this.loadCidToUnicode(properties); | ||||||
|     this.cidEncoding = properties.cidEncoding; |     this.cidEncoding = properties.cidEncoding; | ||||||
|  |     this.vertical = properties.vertical; | ||||||
|  |     if (this.vertical) { | ||||||
|  |       this.vmetrics = properties.vmetrics; | ||||||
|  |       this.defaultVMetrics = properties.defaultVMetrics; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if (properties.toUnicode) |     if (properties.toUnicode) | ||||||
|       this.toUnicode = properties.toUnicode; |       this.toUnicode = properties.toUnicode; | ||||||
| @ -4449,17 +4454,15 @@ var Font = (function FontClosure() { | |||||||
|       var fontCharCode, width, operatorList, disabled; |       var fontCharCode, width, operatorList, disabled; | ||||||
| 
 | 
 | ||||||
|       var width = this.widths[charcode]; |       var width = this.widths[charcode]; | ||||||
|  |       var vmetric = this.vmetrics && this.vmetrics[charcode]; | ||||||
| 
 | 
 | ||||||
|       switch (this.type) { |       switch (this.type) { | ||||||
|         case 'CIDFontType0': |         case 'CIDFontType0': | ||||||
|           if (this.noUnicodeAdaptation) { |  | ||||||
|             width = this.widths[this.unicodeToCID[charcode] || charcode]; |  | ||||||
|           } |  | ||||||
|           fontCharCode = this.toFontChar[charcode] || charcode; |  | ||||||
|           break; |  | ||||||
|         case 'CIDFontType2': |         case 'CIDFontType2': | ||||||
|  |           var cid = this.unicodeToCID[charcode] || charcode; | ||||||
|           if (this.noUnicodeAdaptation) { |           if (this.noUnicodeAdaptation) { | ||||||
|             width = this.widths[this.unicodeToCID[charcode] || charcode]; |             width = this.widths[cid]; | ||||||
|  |             vmetric = this.vmetrics && this.vmetrics[cid]; | ||||||
|           } |           } | ||||||
|           fontCharCode = this.toFontChar[charcode] || charcode; |           fontCharCode = this.toFontChar[charcode] || charcode; | ||||||
|           break; |           break; | ||||||
| @ -4523,6 +4526,7 @@ var Font = (function FontClosure() { | |||||||
|         fontChar: String.fromCharCode(fontCharCode), |         fontChar: String.fromCharCode(fontCharCode), | ||||||
|         unicode: unicodeChars, |         unicode: unicodeChars, | ||||||
|         width: width, |         width: width, | ||||||
|  |         vmetric: vmetric, | ||||||
|         disabled: disabled, |         disabled: disabled, | ||||||
|         operatorList: operatorList |         operatorList: operatorList | ||||||
|       }; |       }; | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								test/pdfs/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -44,4 +44,5 @@ | |||||||
| !noembed-jis7.pdf | !noembed-jis7.pdf | ||||||
| !noembed-eucjp.pdf | !noembed-eucjp.pdf | ||||||
| !noembed-sjis.pdf | !noembed-sjis.pdf | ||||||
|  | !vertical.pdf | ||||||
| !issue2099-1.pdf | !issue2099-1.pdf | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								test/pdfs/vertical.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/pdfs/vertical.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -906,6 +906,12 @@ | |||||||
|       "rounds": 1, |       "rounds": 1, | ||||||
|       "type": "eq" |       "type": "eq" | ||||||
|     }, |     }, | ||||||
|  |     {  "id": "vertical", | ||||||
|  |       "file": "pdfs/vertical.pdf", | ||||||
|  |       "md5": "8a74d33504701edcefeef2afd022765e", | ||||||
|  |       "rounds": 1, | ||||||
|  |       "type": "eq" | ||||||
|  |     }, | ||||||
|     {  "id": "issue2099-1", |     {  "id": "issue2099-1", | ||||||
|       "file": "pdfs/issue2099-1.pdf", |       "file": "pdfs/issue2099-1.pdf", | ||||||
|       "md5": "c7eca682d70a976dfc4b7e64d3e9f1ce", |       "md5": "c7eca682d70a976dfc4b7e64d3e9f1ce", | ||||||
|  | |||||||
| @ -1171,7 +1171,7 @@ canvas { | |||||||
| .textLayer > div { | .textLayer > div { | ||||||
|   color: transparent; |   color: transparent; | ||||||
|   position: absolute; |   position: absolute; | ||||||
|   line-height:1.3; |   line-height:1; | ||||||
|   white-space:pre; |   white-space:pre; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2515,6 +2515,7 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) { | |||||||
|   this.renderLayer = function textLayerBuilderRenderLayer() { |   this.renderLayer = function textLayerBuilderRenderLayer() { | ||||||
|     var self = this; |     var self = this; | ||||||
|     var textDivs = this.textDivs; |     var textDivs = this.textDivs; | ||||||
|  |     var bidiTexts = this.textContent.bidiTexts; | ||||||
|     var textLayerDiv = this.textLayerDiv; |     var textLayerDiv = this.textLayerDiv; | ||||||
|     var canvas = document.createElement('canvas'); |     var canvas = document.createElement('canvas'); | ||||||
|     var ctx = canvas.getContext('2d'); |     var ctx = canvas.getContext('2d'); | ||||||
| @ -2535,8 +2536,11 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) { | |||||||
|       if (width > 0) { |       if (width > 0) { | ||||||
|         var textScale = textDiv.dataset.canvasWidth / width; |         var textScale = textDiv.dataset.canvasWidth / width; | ||||||
| 
 | 
 | ||||||
|         CustomStyle.setProp('transform' , textDiv, |         var transform = 'scale(' + textScale + ', 1)'; | ||||||
|           'scale(' + textScale + ', 1)'); |         if (bidiTexts[i].dir === 'ttb') { | ||||||
|  |           transform = 'rotate(90deg) ' + transform; | ||||||
|  |         } | ||||||
|  |         CustomStyle.setProp('transform' , textDiv, transform); | ||||||
|         CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%'); |         CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%'); | ||||||
| 
 | 
 | ||||||
|         textLayerDiv.appendChild(textDiv); |         textLayerDiv.appendChild(textDiv); | ||||||
| @ -2601,7 +2605,8 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) { | |||||||
|       var textDiv = textDivs[i]; |       var textDiv = textDivs[i]; | ||||||
| 
 | 
 | ||||||
|       textDiv.textContent = bidiText.str; |       textDiv.textContent = bidiText.str; | ||||||
|       textDiv.dir = bidiText.ltr ? 'ltr' : 'rtl'; |       // bidiText.dir may be 'ttb' for vertical texts.
 | ||||||
|  |       textDiv.dir = bidiText.dir === 'rtl' ? 'rtl' : 'ltr'; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     this.setupRenderLayoutTimer(); |     this.setupRenderLayoutTimer(); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user