From 8ea505545a56dcd551c344d962d3fa74c349bf70 Mon Sep 17 00:00:00 2001 From: Jani Pehkonen Date: Tue, 10 Apr 2018 16:44:42 +0300 Subject: [PATCH] Use FDSelect and FDArray when converting CFF CID font to paths --- src/core/font_renderer.js | 66 ++++++++++++++++++++++++++------ test/pdfs/.gitignore | 1 + test/pdfs/text_clip_cff_cid.pdf | Bin 0 -> 7393 bytes test/test_manifest.json | 6 +++ 4 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 test/pdfs/text_clip_cff_cid.pdf 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 0000000000000000000000000000000000000000..241588f53492728f9d9e855edf56e40b78eb8133 GIT binary patch literal 7393 zcmai(cT|&Gm&O4BArt}WHPS&Kp@rT9(owqfAfb0c?;R;3MVb`pJ#<7mkpQ9;k&Yq= zDo9hL*9qS1jCbz0X0leY*1NOMKKnfTy#G8uj=NA5enA0I0LQ15v3CG*AQ(EQhhfASBD2~HgKRI_#IzdHR@F)xHC93!aeLq1I2%|j{+y2Q{hqS=9mbD|xj z;eNcTRUk0zY<&?F1GOXXnKZ*)-w}J__<%a{pYV z)MD2>r8Oq(RMxP*>lJ;pr#mMi8<@k*_r�jR5FK#~*v>veljLeJ9zs1S_xQ_}%#O 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