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) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
current.x += x * textHScale;
|
||||
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;
|
||||
current.x += spacingLength;
|
||||
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…
Reference in New Issue
Block a user