From 32f9cabf8283df91887cf2fc26563cc7068ec2af Mon Sep 17 00:00:00 2001 From: Takashi Tamura Date: Tue, 28 Jan 2020 16:16:39 +0900 Subject: [PATCH] Support the vertical writing mode with SVG backend. --- src/display/svg.js | 73 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/src/display/svg.js b/src/display/svg.js index 0afd55ca3..5fd9295ee 100644 --- a/src/display/svg.js +++ b/src/display/svg.js @@ -744,6 +744,7 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { current.y = current.lineY = 0; current.xcoords = []; + current.ycoords = []; current.tspan = this.svgFactory.createElement("svg:tspan"); current.tspan.setAttributeNS(null, "font-family", current.fontFamily); current.tspan.setAttributeNS( @@ -768,6 +769,7 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { current.txtElement = this.svgFactory.createElement("svg:text"); current.txtgrp = this.svgFactory.createElement("svg:g"); current.xcoords = []; + current.ycoords = []; } moveText(x, y) { @@ -776,6 +778,7 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { current.y = current.lineY += y; current.xcoords = []; + current.ycoords = []; current.tspan = this.svgFactory.createElement("svg:tspan"); current.tspan.setAttributeNS(null, "font-family", current.fontFamily); current.tspan.setAttributeNS( @@ -794,11 +797,14 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { return; } + const fontSizeScale = current.fontSizeScale; const charSpacing = current.charSpacing; const wordSpacing = current.wordSpacing; const fontDirection = current.fontDirection; const textHScale = current.textHScale * fontDirection; const vertical = font.vertical; + const spacingDir = vertical ? 1 : -1; + const defaultVMetrics = font.defaultVMetrics; const widthAdvanceScale = fontSize * current.fontMatrix[0]; let x = 0; @@ -808,39 +814,72 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { x += fontDirection * wordSpacing; continue; } else if (isNum(glyph)) { - x += -glyph * fontSize * 0.001; + x += (spacingDir * glyph * fontSize) / 1000; continue; } - const width = glyph.width; - const character = glyph.fontChar; const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing; - const charWidth = width * widthAdvanceScale + spacing * fontDirection; + const character = glyph.fontChar; + let scaledX, scaledY; + let width = glyph.width; + if (vertical) { + let vx; + const vmetric = glyph.vmetric || defaultVMetrics; + vx = glyph.vmetric ? vmetric[1] : width * 0.5; + vx = -vx * widthAdvanceScale; + const vy = vmetric[2] * widthAdvanceScale; - if (!glyph.isInFont && !font.missingFile) { - x += charWidth; + width = vmetric ? -vmetric[0] : width; + scaledX = vx / fontSizeScale; + scaledY = (x + vy) / fontSizeScale; + } else { + scaledX = x / fontSizeScale; + scaledY = 0; + } + + if (glyph.isInFont || font.missingFile) { + current.xcoords.push(current.x + scaledX); + if (vertical) { + current.ycoords.push(-current.y + scaledY); + } + current.tspan.textContent += character; + } else { // TODO: To assist with text selection, we should replace the missing // character with a space character if charWidth is not zero. // But we cannot just do "character = ' '", because the ' ' character // might actually map to a different glyph. - continue; } - current.xcoords.push(current.x + x); - current.tspan.textContent += character; + + let charWidth; + if (vertical) { + charWidth = width * widthAdvanceScale - spacing * fontDirection; + } else { + charWidth = width * widthAdvanceScale + spacing * fontDirection; + } + x += charWidth; } - if (vertical) { - current.y -= x * textHScale; - } else { - current.x += x * textHScale; - } - current.tspan.setAttributeNS( null, "x", current.xcoords.map(pf).join(" ") ); - current.tspan.setAttributeNS(null, "y", pf(-current.y)); + if (vertical) { + current.tspan.setAttributeNS( + null, + "y", + current.ycoords.map(pf).join(" ") + ); + } else { + current.tspan.setAttributeNS(null, "y", pf(-current.y)); + } + + if (vertical) { + current.y -= x; + } else { + current.x += x * textHScale; + } + current.tspan.setAttributeNS(null, "font-family", current.fontFamily); current.tspan.setAttributeNS( null, @@ -966,6 +1005,7 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { current.tspan = this.svgFactory.createElement("svg:tspan"); current.tspan.setAttributeNS(null, "y", pf(-current.y)); current.xcoords = []; + current.ycoords = []; } endText() { @@ -1017,6 +1057,7 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { this.current.fillColor = Util.makeCssRgb(r, g, b); this.current.tspan = this.svgFactory.createElement("svg:tspan"); this.current.xcoords = []; + this.current.ycoords = []; } setStrokeColorN(args) {