From f4db38aadfd3a4f13cb72736de28acfaa15d648b Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 5 Aug 2018 10:33:56 +0200 Subject: [PATCH 1/3] Update the TrueType font file detection to also recognize the Mac specific header 'true' Please refer to the TrueType specification: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html#ScalerTypeNote --- src/core/fonts.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/fonts.js b/src/core/fonts.js index aff4ac513..a94cc510a 100644 --- a/src/core/fonts.js +++ b/src/core/fonts.js @@ -680,7 +680,8 @@ var Font = (function FontClosure() { function isTrueTypeFile(file) { var header = file.peekBytes(4); - return readUint32(header, 0) === 0x00010000; + return (readUint32(header, 0) === 0x00010000 || + bytesToString(header) === 'true'); } function isTrueTypeCollectionFile(file) { From 9bbca0457994b913808dfb639f19eab8a2f2542a Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 5 Aug 2018 10:34:06 +0200 Subject: [PATCH 2/3] Add a (basic) `isCFFFile` helper function to detect CFF font files Compared to most other font formats, the CFF doesn't have a constant header which makes is slightly more difficult to detect such font files. Please refer to the Compact Font Format specification: https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf#G3.32094 --- src/core/fonts.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/core/fonts.js b/src/core/fonts.js index a94cc510a..b32f0b7cb 100644 --- a/src/core/fonts.js +++ b/src/core/fonts.js @@ -708,6 +708,22 @@ var Font = (function FontClosure() { return false; } + /** + * Compared to other font formats, the header in CFF files is not constant + * but contains version numbers. To reduce the possibility of misclassifying + * font files as CFF, it's recommended to check for other font formats first. + */ + function isCFFFile(file) { + const header = file.peekBytes(4); + if (/* major version, [1, 255] */ header[0] >= 1 && + /* minor version, [0, 255]; header[1] */ + /* header size, [0, 255]; header[2] */ + /* offset(0) size, [1, 4] */ (header[3] >= 1 && header[3] <= 4)) { + return true; + } + return false; + } + function buildToFontChar(encoding, glyphsUnicodeMap, differences) { var toFontChar = [], unicode; for (var i = 0, ii = encoding.length; i < ii; i++) { From 3177f6aa55b71d93e73a214ed10312018c971895 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 5 Aug 2018 10:34:14 +0200 Subject: [PATCH 3/3] Parse the font file to determine the correct type/subtype, rather than relying on the (often incorrect) data in the font dictionary The current font type/subtype detection code is quite inconsistent/unwieldy. In some cases it will simply assume that the font dictionary is correct, in others it will somewhat "arbitrarily" check the actual font file (more of these cases have been added over the years to fix specific bugs). As is evident from e.g. issue 9949, the font type/subtype detection code is continuing to cause issues. In an attempt to get rid of these hacks once and for all, this patch instead re-factors the type/subtype detection to *always* parse the font file. Please note that, as far as I can tell, we still appear to need to rely on the composite font detection based on the font dictionary. However, even if the composite/non-composite detection would get it wrong, that shouldn't really matter too much given that there's basically only two different code-paths (for "TrueType-like" vs "Type1-like" fonts). --- src/core/fonts.js | 78 +++++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/src/core/fonts.js b/src/core/fonts.js index b32f0b7cb..a703931a6 100644 --- a/src/core/fonts.js +++ b/src/core/fonts.js @@ -550,36 +550,14 @@ var Font = (function FontClosure() { return; } - // Some fonts might use wrong font types for Type1C or CIDFontType0C - if (subtype === 'Type1C') { - if (type !== 'Type1' && type !== 'MMType1') { - // Some TrueType fonts by mistake claim Type1C - if (isTrueTypeFile(file)) { - subtype = 'TrueType'; - } else { - type = 'Type1'; - } - } else if (isOpenTypeFile(file)) { - // Sometimes the type/subtype can be a complete lie (see issue7598.pdf). - subtype = 'OpenType'; - } - } - if (subtype === 'CIDFontType0C' && type !== 'CIDFontType0') { - type = 'CIDFontType0'; - } - // Some CIDFontType0C fonts by mistake claim CIDFontType0. - if (type === 'CIDFontType0') { - if (isType1File(file)) { - subtype = 'CIDFontType0'; - } else if (isOpenTypeFile(file)) { - // Sometimes the type/subtype can be a complete lie (see issue6782.pdf). - subtype = 'OpenType'; - } else { - subtype = 'CIDFontType0C'; - } - } - if (subtype === 'OpenType' && type !== 'OpenType') { - type = 'OpenType'; + // Parse the font file to determine the correct type/subtype, rather than + // relying on the (often incorrect) data in the font dictionary; (see e.g. + // issue6782.pdf, issue7598.pdf, and issue9949.pdf). + [type, subtype] = getFontFileType(file, properties); + + if (type !== this.type || subtype !== this.subtype) { + info('Inconsistent font file Type/SubType, expected: ' + + `${this.type}/${this.subtype} but found: ${type}/${subtype}.`); } try { @@ -724,6 +702,46 @@ var Font = (function FontClosure() { return false; } + function getFontFileType(file, { type, subtype, composite, }) { + let fileType, fileSubtype; + + if (isTrueTypeFile(file) || isTrueTypeCollectionFile(file)) { + if (composite) { + fileType = 'CIDFontType2'; + } else { + fileType = 'TrueType'; + } + } else if (isOpenTypeFile(file)) { + if (composite) { + fileType = 'CIDFontType2'; + } else { + fileType = 'OpenType'; + } + } else if (isType1File(file)) { + if (composite) { + fileType = 'CIDFontType0'; + } else if (type === 'MMType1') { + fileType = 'MMType1'; + } else { + fileType = 'Type1'; + } + } else if (isCFFFile(file)) { + if (composite) { + fileType = 'CIDFontType0'; + fileSubtype = 'CIDFontType0C'; + } else { + fileType = 'Type1'; + fileSubtype = 'Type1C'; + } + } else { + warn('getFontFileType: Unable to detect correct font file Type/Subtype.'); + fileType = type; + fileSubtype = subtype; + } + + return [fileType, fileSubtype]; + } + function buildToFontChar(encoding, glyphsUnicodeMap, differences) { var toFontChar = [], unicode; for (var i = 0, ii = encoding.length; i < ii; i++) {