From 8a596ef5d5347f9342cfbde1b1174d38359cbd1c Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Tue, 26 Feb 2019 15:57:46 -0800 Subject: [PATCH] Add unique glyph names for CFF fonts. Printing on MacOS was broken with the previous approach of just mapping all the glyphs to notdef. --- src/core/cff_parser.js | 65 ++++++++++++++++++++++++++++++------ src/core/fonts.js | 10 ++---- test/unit/cff_parser_spec.js | 32 +++++++++++++++++- 3 files changed, 89 insertions(+), 18 deletions(-) diff --git a/src/core/cff_parser.js b/src/core/cff_parser.js index 6a47d16be9173..51baa07191531 100644 --- a/src/core/cff_parser.js +++ b/src/core/cff_parser.js @@ -96,6 +96,8 @@ var CFFStandardStrings = [ 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', 'Semibold' ]; +const NUM_STANDARD_CFF_STRINGS = 391; + var CFFParser = (function CFFParserClosure() { var CharstringValidationData = [ null, @@ -931,16 +933,27 @@ var CFFStrings = (function CFFStringsClosure() { } CFFStrings.prototype = { get: function CFFStrings_get(index) { - if (index >= 0 && index <= 390) { + if (index >= 0 && index <= (NUM_STANDARD_CFF_STRINGS - 1)) { return CFFStandardStrings[index]; } - if (index - 391 <= this.strings.length) { - return this.strings[index - 391]; + if (index - NUM_STANDARD_CFF_STRINGS <= this.strings.length) { + return this.strings[index - NUM_STANDARD_CFF_STRINGS]; } return CFFStandardStrings[0]; }, + getSID: function CFFStrings_getSID(str) { + let index = CFFStandardStrings.indexOf(str); + if (index !== -1) { + return index; + } + index = this.strings.indexOf(str); + if (index !== -1) { + return index + NUM_STANDARD_CFF_STRINGS; + } + return -1; + }, add: function CFFStrings_add(value) { - this.strings.push(value); + return this.strings.push(value) + NUM_STANDARD_CFF_STRINGS - 1; }, get count() { return this.strings.length; @@ -1312,7 +1325,8 @@ var CFFCompiler = (function CFFCompilerClosure() { output.add(encoding); } } - var charset = this.compileCharset(cff.charset); + var charset = this.compileCharset(cff.charset, cff.charStrings.count, + cff.strings, cff.isCIDFont); topDictTracker.setEntryLocation('charset', [output.length], output); output.add(charset); @@ -1580,11 +1594,42 @@ var CFFCompiler = (function CFFCompilerClosure() { } return this.compileIndex(charStringsIndex); }, - compileCharset: function CFFCompiler_compileCharset(charset) { - let length = 1 + (this.cff.charStrings.count - 1) * 2; - // The contents of the charset doesn't matter, it's just there to make - // freetype happy. - let out = new Uint8Array(length); + compileCharset: function CFFCompiler_compileCharset(charset, numGlyphs, + strings, isCIDFont) { + // Freetype requires the number of charset strings be correct and MacOS + // requires a valid mapping for printing. + let out; + let numGlyphsLessNotDef = numGlyphs - 1; + if (isCIDFont) { + // In a CID font, the charset is a mapping of CIDs not SIDs so just + // create an identity mapping. + out = new Uint8Array([ + 2, // format + 0, // first CID upper byte + 0, // first CID lower byte + (numGlyphsLessNotDef >> 8) & 0xFF, + numGlyphsLessNotDef & 0xFF, + ]); + } else { + let length = 1 + numGlyphsLessNotDef * 2; + out = new Uint8Array(length); + out[0] = 0; // format 0 + let charsetIndex = 0; + let numCharsets = charset.charset.length; + for (let i = 1; i < out.length; i += 2) { + let sid = 0; + if (charsetIndex < numCharsets) { + let name = charset.charset[charsetIndex++]; + sid = strings.getSID(name); + if (sid === -1) { + sid = 0; + warn(`Couldn't find ${name} in CFF strings`); + } + } + out[i] = (sid >> 8) & 0xFF; + out[i + 1] = sid & 0xFF; + } + } return this.compileTypedArray(out); }, compileEncoding: function CFFCompiler_compileEncoding(encoding) { diff --git a/src/core/fonts.js b/src/core/fonts.js index 8d618a8c9ec3c..3156282758bc6 100644 --- a/src/core/fonts.js +++ b/src/core/fonts.js @@ -3333,16 +3333,12 @@ var Type1Font = (function Type1FontClosure() { var i, ii; for (i = 0; i < count; i++) { var index = CFFStandardStrings.indexOf(charstrings[i].glyphName); - // TODO: Insert the string and correctly map it. Previously it was - // thought mapping names that aren't in the standard strings to .notdef - // was fine, however in issue818 when mapping them all to .notdef the - // adieresis glyph no longer worked. if (index === -1) { - index = 0; + index = strings.add(charstrings[i].glyphName); } - charsetArray.push((index >> 8) & 0xff, index & 0xff); + charsetArray.push(index); } - cff.charset = new CFFCharset(false, 0, [], charsetArray); + cff.charset = new CFFCharset(false, 0, charsetArray); var charStringsIndex = new CFFIndex(); charStringsIndex.add([0x8B, 0x0E]); // .notdef diff --git a/test/unit/cff_parser_spec.js b/test/unit/cff_parser_spec.js index c2621e67e68af..7443b3f968b47 100644 --- a/test/unit/cff_parser_spec.js +++ b/test/unit/cff_parser_spec.js @@ -14,7 +14,7 @@ */ import { - CFFCompiler, CFFFDSelect, CFFParser, CFFStrings + CFFCharset, CFFCompiler, CFFFDSelect, CFFParser, CFFStrings } from '../../src/core/cff_parser'; import { SEAC_ANALYSIS_ENABLED } from '../../src/core/fonts'; import { Stream } from '../../src/core/stream'; @@ -446,5 +446,35 @@ describe('CFFCompiler', function() { ]); }); + it('compiles charset of CID font', function() { + var charset = new CFFCharset(); + var c = new CFFCompiler(); + var numGlyphs = 7; + var out = c.compileCharset(charset, numGlyphs, new CFFStrings(), true); + // All CID charsets get turned into a simple format 2. + expect(out).toEqual([ + 2, // format + 0, // cid (high) + 0, // cid (low) + 0, // nLeft (high) + numGlyphs - 1, // nLeft (low) + ]); + }); + + it('compiles charset of non CID font', function() { + var charset = new CFFCharset(false, 0, ['space', 'exclam']); + var c = new CFFCompiler(); + var numGlyphs = 3; + var out = c.compileCharset(charset, numGlyphs, new CFFStrings(), false); + // All non-CID fonts use a format 0 charset. + expect(out).toEqual([ + 0, // format + 0, // sid of 'space' (high) + 1, // sid of 'space' (low) + 0, // sid of 'exclam' (high) + 2, // sid of 'exclam' (low) + ]); + }); + // TODO a lot more compiler tests });