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.ltr = isLTR; | ||||
|     this.dir = vertical ? 'ttb' : isLTR ? 'ltr' : 'rtl'; | ||||
|   } | ||||
| 
 | ||||
|   function bidi(str, startLevel) { | ||||
|   function bidi(str, startLevel, vertical) { | ||||
|     var isLTR = true; | ||||
|     var strLength = str.length; | ||||
|     if (strLength === 0) | ||||
|       return new BidiResult(str, isLTR); | ||||
|     if (strLength === 0 || vertical) | ||||
|       return new BidiResult(str, isLTR, vertical); | ||||
| 
 | ||||
|     // get types, fill arrays
 | ||||
| 
 | ||||
|  | ||||
| @ -896,6 +896,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | ||||
|       var textSelection = textLayer && !skipTextSelection ? true : false; | ||||
|       var textRenderingMode = current.textRenderingMode; | ||||
|       var canvasWidth = 0.0; | ||||
|       var vertical = font.vertical; | ||||
|       var defaultVMetrics = font.defaultVMetrics; | ||||
| 
 | ||||
|       // Type3 fonts - each glyph is a "mini-PDF"
 | ||||
|       if (font.coded) { | ||||
| @ -969,25 +971,37 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | ||||
|           } | ||||
| 
 | ||||
|           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; | ||||
| 
 | ||||
|           if (!glyph.disabled) { | ||||
|             if (vertical) { | ||||
|               var scaledX = vx / fontSizeScale; | ||||
|               var scaledY = (x + vy) / fontSizeScale; | ||||
|             } else { | ||||
|               var scaledX = x / fontSizeScale; | ||||
|               var scaledY = 0; | ||||
|             } | ||||
|             switch (textRenderingMode) { | ||||
|               default: // other unsupported rendering modes
 | ||||
|               case TextRenderingMode.FILL: | ||||
|               case TextRenderingMode.FILL_ADD_TO_PATH: | ||||
|                 ctx.fillText(character, scaledX, 0); | ||||
|                 ctx.fillText(character, scaledX, scaledY); | ||||
|                 break; | ||||
|               case TextRenderingMode.STROKE: | ||||
|               case TextRenderingMode.STROKE_ADD_TO_PATH: | ||||
|                 ctx.strokeText(character, scaledX, 0); | ||||
|                 ctx.strokeText(character, scaledX, scaledY); | ||||
|                 break; | ||||
|               case TextRenderingMode.FILL_STROKE: | ||||
|               case TextRenderingMode.FILL_STROKE_ADD_TO_PATH: | ||||
|                 ctx.fillText(character, scaledX, 0); | ||||
|                 ctx.strokeText(character, scaledX, 0); | ||||
|                 ctx.fillText(character, scaledX, scaledY); | ||||
|                 ctx.strokeText(character, scaledX, scaledY); | ||||
|                 break; | ||||
|               case TextRenderingMode.INVISIBLE: | ||||
|               case TextRenderingMode.ADD_TO_PATH: | ||||
| @ -995,7 +1009,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | ||||
|             } | ||||
|             if (textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG) { | ||||
|               var clipCtx = this.getCurrentTextClipping(); | ||||
|               clipCtx.fillText(character, scaledX, 0); | ||||
|               clipCtx.fillText(character, scaledX, scaledY); | ||||
|             } | ||||
|           } | ||||
| 
 | ||||
| @ -1003,12 +1017,23 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | ||||
| 
 | ||||
|           canvasWidth += charWidth; | ||||
|         } | ||||
|         if (vertical) { | ||||
|           current.y -= x * textHScale; | ||||
|         } else { | ||||
|           current.x += x * textHScale; | ||||
|         } | ||||
|         ctx.restore(); | ||||
|       } | ||||
| 
 | ||||
|       if (textSelection) { | ||||
|         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); | ||||
|       } | ||||
| 
 | ||||
| @ -1027,6 +1052,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | ||||
|       var geom; | ||||
|       var canvasWidth = 0.0; | ||||
|       var textSelection = textLayer ? true : false; | ||||
|       var vertical = font.vertical; | ||||
| 
 | ||||
|       if (textSelection) { | ||||
|         ctx.save(); | ||||
| @ -1039,7 +1065,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | ||||
|         var e = arr[i]; | ||||
|         if (isNum(e)) { | ||||
|           var spacingLength = -e * fontSize * textHScale; | ||||
|           if (vertical) { | ||||
|             current.y += spacingLength; | ||||
|           } else { | ||||
|             current.x += spacingLength; | ||||
|           } | ||||
| 
 | ||||
|           if (textSelection) | ||||
|             canvasWidth += spacingLength; | ||||
| @ -1055,6 +1085,14 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { | ||||
| 
 | ||||
|       if (textSelection) { | ||||
|         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); | ||||
|       } | ||||
|     }, | ||||
|  | ||||
| @ -796,7 +796,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { | ||||
|           } // switch
 | ||||
| 
 | ||||
|           if (chunk !== '') { | ||||
|             bidiTexts.push(PDFJS.bidi(chunk, -1)); | ||||
|             var bidiText = PDFJS.bidi(chunk, -1, font.vertical); | ||||
|             bidiTexts.push(bidiText); | ||||
| 
 | ||||
|             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'); | ||||
|         if (isStream(cidToGidMap)) | ||||
|           properties.cidToGidMap = this.readCidToGidMap(cidToGidMap); | ||||
| @ -1031,6 +1028,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { | ||||
|                                                    properties) { | ||||
|       var glyphsWidths = []; | ||||
|       var defaultWidth = 0; | ||||
|       var glyphsVMetrics = []; | ||||
|       var defaultVMetrics; | ||||
|       if (properties.composite) { | ||||
|         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 { | ||||
|         var firstChar = properties.firstChar; | ||||
|         var widths = dict.get('Widths'); | ||||
| @ -1089,6 +1108,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { | ||||
| 
 | ||||
|       properties.defaultWidth = defaultWidth; | ||||
|       properties.widths = glyphsWidths; | ||||
|       properties.defaultVMetrics = defaultVMetrics; | ||||
|       properties.vmetrics = glyphsVMetrics; | ||||
|     }, | ||||
| 
 | ||||
|     isSerifFont: function PartialEvaluator_isSerifFont(baseFontName) { | ||||
| @ -1260,6 +1281,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { | ||||
|         italicAngle: descriptor.get('ItalicAngle'), | ||||
|         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.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.
 | ||||
|     this.loadCidToUnicode(properties); | ||||
|     this.cidEncoding = properties.cidEncoding; | ||||
|     this.vertical = properties.vertical; | ||||
|     if (this.vertical) { | ||||
|       this.vmetrics = properties.vmetrics; | ||||
|       this.defaultVMetrics = properties.defaultVMetrics; | ||||
|     } | ||||
| 
 | ||||
|     if (properties.toUnicode) | ||||
|       this.toUnicode = properties.toUnicode; | ||||
| @ -4449,17 +4454,15 @@ var Font = (function FontClosure() { | ||||
|       var fontCharCode, width, operatorList, disabled; | ||||
| 
 | ||||
|       var width = this.widths[charcode]; | ||||
|       var vmetric = this.vmetrics && this.vmetrics[charcode]; | ||||
| 
 | ||||
|       switch (this.type) { | ||||
|         case 'CIDFontType0': | ||||
|           if (this.noUnicodeAdaptation) { | ||||
|             width = this.widths[this.unicodeToCID[charcode] || charcode]; | ||||
|           } | ||||
|           fontCharCode = this.toFontChar[charcode] || charcode; | ||||
|           break; | ||||
|         case 'CIDFontType2': | ||||
|           var cid = this.unicodeToCID[charcode] || charcode; | ||||
|           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; | ||||
|           break; | ||||
| @ -4523,6 +4526,7 @@ var Font = (function FontClosure() { | ||||
|         fontChar: String.fromCharCode(fontCharCode), | ||||
|         unicode: unicodeChars, | ||||
|         width: width, | ||||
|         vmetric: vmetric, | ||||
|         disabled: disabled, | ||||
|         operatorList: operatorList | ||||
|       }; | ||||
|  | ||||
							
								
								
									
										1
									
								
								test/pdfs/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -44,4 +44,5 @@ | ||||
| !noembed-jis7.pdf | ||||
| !noembed-eucjp.pdf | ||||
| !noembed-sjis.pdf | ||||
| !vertical.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, | ||||
|       "type": "eq" | ||||
|     }, | ||||
|     {  "id": "vertical", | ||||
|       "file": "pdfs/vertical.pdf", | ||||
|       "md5": "8a74d33504701edcefeef2afd022765e", | ||||
|       "rounds": 1, | ||||
|       "type": "eq" | ||||
|     }, | ||||
|     {  "id": "issue2099-1", | ||||
|       "file": "pdfs/issue2099-1.pdf", | ||||
|       "md5": "c7eca682d70a976dfc4b7e64d3e9f1ce", | ||||
|  | ||||
| @ -1171,7 +1171,7 @@ canvas { | ||||
| .textLayer > div { | ||||
|   color: transparent; | ||||
|   position: absolute; | ||||
|   line-height:1.3; | ||||
|   line-height:1; | ||||
|   white-space:pre; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -2515,6 +2515,7 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) { | ||||
|   this.renderLayer = function textLayerBuilderRenderLayer() { | ||||
|     var self = this; | ||||
|     var textDivs = this.textDivs; | ||||
|     var bidiTexts = this.textContent.bidiTexts; | ||||
|     var textLayerDiv = this.textLayerDiv; | ||||
|     var canvas = document.createElement('canvas'); | ||||
|     var ctx = canvas.getContext('2d'); | ||||
| @ -2535,8 +2536,11 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) { | ||||
|       if (width > 0) { | ||||
|         var textScale = textDiv.dataset.canvasWidth / width; | ||||
| 
 | ||||
|         CustomStyle.setProp('transform' , textDiv, | ||||
|           'scale(' + textScale + ', 1)'); | ||||
|         var transform = 'scale(' + textScale + ', 1)'; | ||||
|         if (bidiTexts[i].dir === 'ttb') { | ||||
|           transform = 'rotate(90deg) ' + transform; | ||||
|         } | ||||
|         CustomStyle.setProp('transform' , textDiv, transform); | ||||
|         CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%'); | ||||
| 
 | ||||
|         textLayerDiv.appendChild(textDiv); | ||||
| @ -2601,7 +2605,8 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) { | ||||
|       var textDiv = textDivs[i]; | ||||
| 
 | ||||
|       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(); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user