diff --git a/src/canvas.js b/src/canvas.js index b9d8bf3f8..088e6c236 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -159,7 +159,7 @@ var CanvasExtraState = (function CanvasExtraStateClosure() { this.fontSize = 0; this.fontSizeScale = 1; this.textMatrix = IDENTITY_MATRIX; - this.fontMatrix = IDENTITY_MATRIX; + this.fontMatrix = FONT_IDENTITY_MATRIX; this.leading = 0; // Current point (in user coordinates) this.x = 0; @@ -768,11 +768,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { if (!fontObj) error('Can\'t find font for ' + fontRefName); - // Slice-clone matrix so we can manipulate it without affecting original - if (fontObj.fontMatrix) - current.fontMatrix = fontObj.fontMatrix.slice(0); - else - current.fontMatrix = IDENTITY_MATRIX.slice(0); + current.fontMatrix = fontObj.fontMatrix ? fontObj.fontMatrix : + FONT_IDENTITY_MATRIX; // A valid matrix needs all main diagonal elements to be non-zero // This also ensures we bypass FF bugzilla bug #719844. @@ -783,11 +780,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { // The spec for Tf (setFont) says that 'size' specifies the font 'scale', // and in some docs this can be negative (inverted x-y axes). - // We implement this condition with fontMatrix. if (size < 0) { size = -size; - current.fontMatrix[0] *= -1; - current.fontMatrix[3] *= -1; + current.fontDirection = -1; + } else { + current.fontDirection = 1; } this.current.font = fontObj; @@ -840,14 +837,13 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { applyTextTransforms: function CanvasGraphics_applyTextTransforms() { var ctx = this.ctx; var current = this.current; - var textHScale = current.textHScale; - var fontMatrix = current.fontMatrix || IDENTITY_MATRIX; - ctx.transform.apply(ctx, current.textMatrix); - ctx.scale(1, -1); - ctx.translate(current.x, -current.y - current.textRise); - ctx.transform.apply(ctx, fontMatrix); - ctx.scale(textHScale, 1); + ctx.translate(current.x, current.y + current.textRise); + if (current.fontDirection > 0) { + ctx.scale(current.textHScale, -1); + } else { + ctx.scale(-current.textHScale, 1); + } }, createTextGeometry: function CanvasGraphics_createTextGeometry() { var geometry = {}; @@ -878,9 +874,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var fontSizeScale = current.fontSizeScale; var charSpacing = current.charSpacing; var wordSpacing = current.wordSpacing; - var textHScale = current.textHScale; - var fontMatrix = current.fontMatrix || IDENTITY_MATRIX; - var textHScale2 = textHScale * fontMatrix[0]; + var textHScale = current.textHScale * current.fontDirection; + var fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX; var glyphsLength = glyphs.length; var textLayer = this.textLayer; var geom; @@ -919,8 +914,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.restore(); var transformed = Util.applyTransform([glyph.width, 0], fontMatrix); - var width = transformed[0] * fontSize + - Util.sign(current.fontMatrix[0]) * charSpacing; + var width = (transformed[0] * fontSize + charSpacing) * + current.fontDirection; ctx.translate(width, 0); current.x += width * textHScale; @@ -934,8 +929,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var lineWidth = current.lineWidth; var a1 = current.textMatrix[0], b1 = current.textMatrix[1]; - var a2 = fontMatrix[0], b2 = fontMatrix[1]; - var scale = Math.sqrt((a1 * a1 + b1 * b1) * (a2 * a2 + b2 * b2)); + var scale = Math.sqrt(a1 * a1 + b1 * b1); if (scale == 0 || lineWidth == 0) lineWidth = this.getSinglePixelWidth(); else @@ -956,13 +950,13 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var glyph = glyphs[i]; if (glyph === null) { // word break - x += Util.sign(current.fontMatrix[0]) * wordSpacing; + x += current.fontDirection * wordSpacing; continue; } var character = glyph.fontChar; - var charWidth = glyph.width * fontSize * 0.001 + - Util.sign(current.fontMatrix[0]) * charSpacing; + var charWidth = glyph.width * fontSize * current.fontMatrix[0] + + charSpacing * current.fontDirection; if (!glyph.disabled) { var scaledX = x / fontSizeScale; @@ -995,7 +989,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { canvasWidth += charWidth; } - current.x += x * textHScale2; + current.x += x * textHScale; ctx.restore(); } @@ -1011,9 +1005,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var current = this.current; var font = current.font; var fontSize = current.fontSize; - var textHScale = current.textHScale; - if (!font.coded) - textHScale *= (current.fontMatrix || IDENTITY_MATRIX)[0]; + var textHScale = current.textHScale * (current.fontMatrix && !font.coded ? + current.fontMatrix[0] : FONT_IDENTITY_MATRIX[0]) * + current.fontDirection; var arrLength = arr.length; var textLayer = this.textLayer; var geom; @@ -1022,14 +1016,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { if (textSelection) { ctx.save(); - // Type3 fonts - each glyph is a "mini-PDF" (see also showText) - if (font.coded) { - ctx.transform.apply(ctx, current.textMatrix); - ctx.scale(1, -1); - ctx.translate(current.x, -1 * current.y); - ctx.scale(textHScale, 1); - } else - this.applyTextTransforms(); + this.applyTextTransforms(); geom = this.createTextGeometry(); ctx.restore(); } @@ -1037,7 +1024,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { for (var i = 0; i < arrLength; ++i) { var e = arr[i]; if (isNum(e)) { - var spacingLength = -e * 0.001 * fontSize * textHScale; + var spacingLength = -e * fontSize * textHScale; current.x += spacingLength; if (textSelection) diff --git a/src/evaluator.js b/src/evaluator.js index a8b3092a7..7e75ea904 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -1171,7 +1171,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { composite: composite, wideChars: composite, fixedPitch: false, - fontMatrix: dict.get('FontMatrix') || IDENTITY_MATRIX, + fontMatrix: dict.get('FontMatrix') || FONT_IDENTITY_MATRIX, firstChar: firstChar || 0, lastChar: lastChar || maxCharIndex, bbox: descriptor.get('FontBBox'), diff --git a/src/fonts.js b/src/fonts.js index 283bc54f7..1e03c2eff 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -29,6 +29,8 @@ var PDF_GLYPH_SPACE_UNITS = 1000; // Until hinting is fully supported this constant can be used var HINTING_ENABLED = false; +var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; + var FontFlags = { FixedPitch: 1, Serif: 2, @@ -2214,6 +2216,19 @@ function fontCharsToUnicode(charCodes, font) { return result; } +function adjustWidths(properties) { + if (properties.fontMatrix[0] === FONT_IDENTITY_MATRIX[0]) { + return; + } + // adjusting width to fontMatrix scale + var scale = 0.001 / properties.fontMatrix[0]; + var glyphsWidths = properties.widths; + for (var glyph in glyphsWidths) { + glyphsWidths[glyph] *= scale; + } + properties.defaultWidth *= scale; +} + /** * 'Font' is the class the outside world should use, it encapsulate all the font * decoding logics whatever type it is (assuming the font type is supported). @@ -2260,7 +2275,6 @@ var Font = (function FontClosure() { this.hasEncoding = properties.hasEncoding; this.fontMatrix = properties.fontMatrix; - this.widthMultiplier = 1.0; if (properties.type == 'Type3') { this.encoding = properties.baseEncoding; return; @@ -2318,6 +2332,8 @@ var Font = (function FontClosure() { var cff = (subtype == 'Type1C' || subtype == 'CIDFontType0C') ? new CFFFont(file, properties) : new Type1Font(name, file, properties); + adjustWidths(properties); + // Wrap the CFF data inside an OTF font file data = this.convert(name, cff, properties); break; @@ -2337,10 +2353,13 @@ var Font = (function FontClosure() { } this.data = data; + + // Transfer some properties again that could change during font conversion this.fontMatrix = properties.fontMatrix; - this.widthMultiplier = !properties.fontMatrix ? 1.0 : - 1.0 / properties.fontMatrix[0]; + this.widths = properties.widths; + this.defaultWidth = properties.defaultWidth; this.encoding = properties.baseEncoding; + this.loading = true; }; @@ -3931,7 +3950,7 @@ var Font = (function FontClosure() { } this.toFontChar = toFontChar; } - var unitsPerEm = properties.unitsPerEm || 1000; // defaulting to 1000 + var unitsPerEm = 1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0]; var fields = { // PostScript Font Program @@ -4170,7 +4189,7 @@ var Font = (function FontClosure() { if (width) break; // the non-zero width found } - width = (width || this.defaultWidth) * this.widthMultiplier; + width = width || this.defaultWidth; // Do not shadow the property here. See discussion: // https://github.com/mozilla/pdf.js/pull/2127#discussion_r1662280 this._shadowWidth = width; @@ -4251,7 +4270,7 @@ var Font = (function FontClosure() { if (typeof unicodeChars === 'number') unicodeChars = String.fromCharCode(unicodeChars); - width = (isNum(width) ? width : this.defaultWidth) * this.widthMultiplier; + width = isNum(width) ? width : this.defaultWidth; disabled = this.unicodeIsEnabled ? !this.unicodeIsEnabled[fontCharCode] : false; @@ -4443,6 +4462,7 @@ var Type1Parser = function type1Parser() { // Type1 only command with command not (yet) built-in ,throw an error '7': -1, // sbw + '10': 'add', '11': 'sub', '12': 'div', @@ -4507,16 +4527,6 @@ var Type1Parser = function type1Parser() { return args; } - // Remove the same number of args from the stack that are in the args - // parameter. Args should be built from breakUpArgs(). - function popArgs(stack, args) { - for (var i = 0, ii = args.length; i < ii; i++) { - for (var j = 0, jj = args[i].arg.length; j < jj; j++) { - stack.pop(); - } - } - } - function decodeCharString(array) { var charstring = []; var lsb = 0; @@ -4617,25 +4627,32 @@ var Type1Parser = function type1Parser() { break; case 0: var flexArgs = breakUpArgs(charstring, 17); - popArgs(charstring, flexArgs); - charstring.push( - flexArgs[2].value + flexArgs[0].value, // bcp1x + rpx - flexArgs[3].value + flexArgs[1].value, // bcp1y + rpy - flexArgs[4].value, // bcp2x - flexArgs[5].value, // bcp2y - flexArgs[6].value, // p2x - flexArgs[7].value, // p2y - flexArgs[8].value, // bcp3x - flexArgs[9].value, // bcp3y - flexArgs[10].value, // bcp4x - flexArgs[11].value, // bcp4y - flexArgs[12].value, // p3x - flexArgs[13].value, // p3y - flexArgs[14].value, // flexDepth + // removing all flex arguments from the stack + charstring.splice(flexArgs[0].offset, + charstring.length - flexArgs[0].offset); + + charstring = charstring.concat( + flexArgs[0].arg, // bcp1x + + flexArgs[2].arg, // rpx + ['add'], + flexArgs[1].arg, // bcp1y + + flexArgs[3].arg, // rpy + ['add'], + flexArgs[4].arg, // bcp2x + flexArgs[5].arg, // bcp2y + flexArgs[6].arg, // p2x + flexArgs[7].arg, // p2y + flexArgs[8].arg, // bcp3x + flexArgs[9].arg, // bcp3y + flexArgs[10].arg, // bcp4x + flexArgs[11].arg, // bcp4y + flexArgs[12].arg, // p3x + flexArgs[13].arg, // p3y + flexArgs[14].arg, // flexDepth // 15 = finalx unused by flex // 16 = finaly unused by flex - 'flex' + ['flex'] ); flexing = false; @@ -4650,14 +4667,9 @@ var Type1Parser = function type1Parser() { charstring.push(0); continue; // ignoring hmoveto } else if (value == 4 && flexing) { // vmoveto - // Add the dx for flex and but also swap the values so they are the - // right order. - var vArgs = breakUpArgs(charstring, 1); - popArgs(charstring, vArgs); - charstring.push(0); - for (var t = 0, tt = vArgs[0].arg.length; t < tt; t++) { - charstring.push(vArgs[0].arg[t]); - } + // Insert the dx for flex before dy. + var flexArgs = breakUpArgs(charstring, 1); + charstring.splice(flexArgs[0].offset, 0, 0); continue; // ignoring vmoveto } else if (!HINTING_ENABLED && (value == 1 || value == 3)) { charstring.push('drop', 'drop'); @@ -4909,14 +4921,6 @@ var Type1Parser = function type1Parser() { switch (token) { case '/FontMatrix': var matrix = readNumberArray(headerString, i + 1); - - // The FontMatrix is in unitPerEm, so make it pixels - for (var j = 0, jj = matrix.length; j < jj; j++) - matrix[j] *= 1000; - - // Make the angle into the right direction - matrix[2] *= -1; - properties.fontMatrix = matrix; break; case '/Encoding': @@ -5124,6 +5128,7 @@ Type1Font.prototype = { 'rrcurveto': 8, 'callsubr': 10, 'return': 11, + 'add': [12, 10], 'sub': [12, 11], 'div': [12, 12], 'exch': [12, 28], @@ -5191,6 +5196,7 @@ Type1Font.prototype = { topDict.setByName('FamilyName', 3); topDict.setByName('Weight', 4); topDict.setByName('Encoding', null); // placeholder + topDict.setByName('FontMatrix', properties.fontMatrix); topDict.setByName('FontBBox', properties.bbox); topDict.setByName('charset', null); // placeholder topDict.setByName('CharStrings', null); // placeholder @@ -5465,8 +5471,7 @@ var CFFParser = (function CFFParserClosure() { var fontMatrix = topDict.getByName('FontMatrix'); if (fontMatrix) { - // estimating unitsPerEM for the font - properties.unitsPerEm = 1 / fontMatrix[0]; + properties.fontMatrix = fontMatrix; } var fontBBox = topDict.getByName('FontBBox'); @@ -6391,37 +6396,26 @@ var CFFCompiler = (function CFFCompilerClosure() { else return this.encodeFloat(value); }, - encodeFloat: function CFFCompiler_encodeFloat(value) { - value = value.toString(); - // Strip off the any leading zeros. - if (value.substr(0, 2) === '0.') - value = value.substr(1); - else if (value.substr(0, 3) === '-0.') - value = '-' + value.substr(2); - var nibbles = []; + encodeFloat: function CFFCompiler_encodeFloat(num) { + var value = num.toString(); + var nibbles = ''; for (var i = 0, ii = value.length; i < ii; ++i) { - var a = value.charAt(i), b = value.charAt(i + 1); - var nibble; - if (a === 'e' && b === '-') { - nibble = 0xc; - ++i; + var a = value[i]; + if (a === 'e') { + nibbles += value[++i] === '-' ? 'c' : 'b'; } else if (a === '.') { - nibble = 0xa; - } else if (a === 'E') { - nibble = 0xb; + nibbles += 'a'; } else if (a === '-') { - nibble = 0xe; + nibbles += 'e'; } else { - nibble = a; + nibbles += a; } - nibbles.push(nibble); } - nibbles.push(0xf); - if (nibbles.length % 2) - nibbles.push(0xf); + nibbles += (nibbles.length & 1) ? 'f' : 'ff'; var out = [30]; - for (var i = 0, ii = nibbles.length; i < ii; i += 2) - out.push(nibbles[i] << 4 | nibbles[i + 1]); + for (var i = 0, ii = nibbles.length; i < ii; i += 2) { + out.push(parseInt(nibbles.substr(i, 2), 16)); + } return out; }, encodeInteger: function CFFCompiler_encodeInteger(value) {