diff --git a/src/core/font_renderer.js b/src/core/font_renderer.js index 4cf3902d0..366037787 100644 --- a/src/core/font_renderer.js +++ b/src/core/font_renderer.js @@ -14,7 +14,7 @@ */ import { - bytesToString, FormatError, unreachable, Util + bytesToString, FONT_IDENTITY_MATRIX, FormatError, unreachable, Util, warn } from '../shared/util'; import { CFFParser } from './cff_parser'; import { getGlyphsUnicode } from './glyphlist'; @@ -91,6 +91,9 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { subrs: (cff.topDict.privateDict && cff.topDict.privateDict.subrsIndex && cff.topDict.privateDict.subrsIndex.objects), gsubrs: cff.globalSubrIndex && cff.globalSubrIndex.objects, + isCFFCIDFont: cff.isCIDFont, + fdSelect: cff.fdSelect, + fdArray: cff.fdArray, }; } @@ -293,7 +296,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { } } - function compileCharString(code, cmds, font) { + function compileCharString(code, cmds, font, glyphId) { var stack = []; var x = 0, y = 0; var stems = 0; @@ -366,8 +369,28 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { } break; case 10: // callsubr - n = stack.pop() + font.subrsBias; - subrCode = font.subrs[n]; + n = stack.pop(); + subrCode = null; + if (font.isCFFCIDFont) { + let fdIndex = font.fdSelect.getFDIndex(glyphId); + if (fdIndex >= 0 && fdIndex < font.fdArray.length) { + let fontDict = font.fdArray[fdIndex], subrs; + if (fontDict.privateDict && fontDict.privateDict.subrsIndex) { + subrs = fontDict.privateDict.subrsIndex.objects; + } + if (subrs) { + let numSubrs = subrs.length; + // Add subroutine bias. + n += numSubrs < 1240 ? 107 : + (numSubrs < 33900 ? 1131 : 32768); + subrCode = subrs[n]; + } + } else { + warn('Invalid fd index for glyph index.'); + } + } else { + subrCode = font.subrs[n + font.subrsBias]; + } if (subrCode) { parse(subrCode); } @@ -438,12 +461,14 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { cmds.push({ cmd: 'translate', args: [x, y], }); var cmap = lookupCmap(font.cmap, String.fromCharCode( font.glyphNameMap[StandardEncoding[achar]])); - compileCharString(font.glyphs[cmap.glyphId], cmds, font); + compileCharString(font.glyphs[cmap.glyphId], cmds, font, + cmap.glyphId); cmds.push({ cmd: 'restore', }); cmap = lookupCmap(font.cmap, String.fromCharCode( font.glyphNameMap[StandardEncoding[bchar]])); - compileCharString(font.glyphs[cmap.glyphId], cmds, font); + compileCharString(font.glyphs[cmap.glyphId], cmds, font, + cmap.glyphId); } return; case 18: // hstemhm @@ -603,7 +628,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { var cmap = lookupCmap(this.cmap, unicode); var fn = this.compiledGlyphs[cmap.glyphId]; if (!fn) { - fn = this.compileGlyph(this.glyphs[cmap.glyphId]); + fn = this.compileGlyph(this.glyphs[cmap.glyphId], cmap.glyphId); this.compiledGlyphs[cmap.glyphId] = fn; } if (this.compiledCharCodeToGlyphId[cmap.charCode] === undefined) { @@ -612,17 +637,30 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { return fn; }, - compileGlyph(code) { + compileGlyph(code, glyphId) { if (!code || code.length === 0 || code[0] === 14) { return noop; } + let fontMatrix = this.fontMatrix; + if (this.isCFFCIDFont) { + // Top DICT's FontMatrix can be ignored because CFFCompiler always + // removes it and copies to FDArray DICTs. + let fdIndex = this.fdSelect.getFDIndex(glyphId); + if (fdIndex >= 0 && fdIndex < this.fdArray.length) { + let fontDict = this.fdArray[fdIndex]; + fontMatrix = fontDict.getByName('FontMatrix') || FONT_IDENTITY_MATRIX; + } else { + warn('Invalid fd index for glyph index.'); + } + } + var cmds = []; cmds.push({ cmd: 'save', }); - cmds.push({ cmd: 'transform', args: this.fontMatrix.slice(), }); + cmds.push({ cmd: 'transform', args: fontMatrix.slice(), }); cmds.push({ cmd: 'scale', args: ['size', '-size'], }); - this.compileGlyphImpl(code, cmds); + this.compileGlyphImpl(code, cmds, glyphId); cmds.push({ cmd: 'restore', }); @@ -668,11 +706,15 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { 107 : (this.gsubrs.length < 33900 ? 1131 : 32768)); this.subrsBias = (this.subrs.length < 1240 ? 107 : (this.subrs.length < 33900 ? 1131 : 32768)); + + this.isCFFCIDFont = cffInfo.isCFFCIDFont; + this.fdSelect = cffInfo.fdSelect; + this.fdArray = cffInfo.fdArray; } Util.inherit(Type2Compiled, CompiledFont, { - compileGlyphImpl(code, cmds) { - compileCharString(code, cmds, this); + compileGlyphImpl(code, cmds, glyphId) { + compileCharString(code, cmds, this, glyphId); }, }); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 3246480b9..80905620c 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -262,6 +262,7 @@ !issue4573.pdf !issue4722.pdf !issue4800.pdf +!text_clip_cff_cid.pdf !issue4801.pdf !issue5334.pdf !bug1186827.pdf diff --git a/test/pdfs/text_clip_cff_cid.pdf b/test/pdfs/text_clip_cff_cid.pdf new file mode 100644 index 000000000..241588f53 Binary files /dev/null and b/test/pdfs/text_clip_cff_cid.pdf differ diff --git a/test/test_manifest.json b/test/test_manifest.json index a17b05e82..cf1553953 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -2261,6 +2261,12 @@ "link": true, "type": "eq" }, + { "id": "text_clip_cff_cid", + "file": "pdfs/text_clip_cff_cid.pdf", + "md5": "92d4920586f177cc0e83326e5b5d2ee1", + "rounds": 1, + "type": "eq" + }, { "id": "preistabelle", "file": "pdfs/preistabelle.pdf", "md5": "d2f0b2086160d4f3d325c79a5dc1fb4d",