From 08c751e061eec9f2c7c3226d22c9bc9e453d4b80 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Mon, 9 Sep 2024 09:22:22 +0200 Subject: [PATCH 1/8] cff: Correct private DICT definition [why] FamilyBlues has two Ops, probably a copy and paste leftover. And two Ops are missing. This is now in line with CFF and CFF2 fonts. Signed-off-by: Fini Jastrow --- src/tables/cff.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tables/cff.mjs b/src/tables/cff.mjs index 934589a7..73edbbbd 100755 --- a/src/tables/cff.mjs +++ b/src/tables/cff.mjs @@ -399,7 +399,6 @@ const PRIVATE_DICT_META = [ const PRIVATE_DICT_META_CFF2 = [ {name: 'blueValues', op: 6, type: 'delta'}, {name: 'otherBlues', op: 7, type: 'delta'}, - {name: 'familyBlues', op: 7, type: 'delta'}, {name: 'familyBlues', op: 8, type: 'delta'}, {name: 'familyOtherBlues', op: 9, type: 'delta'}, {name: 'blueScale', op: 1209, type: 'number', value: 0.039625}, @@ -411,8 +410,10 @@ const PRIVATE_DICT_META_CFF2 = [ {name: 'stemSnapV', op: 1213, type: 'number'}, {name: 'languageGroup', op: 1217, type: 'number', value: 0}, {name: 'expansionFactor', op: 1218, type: 'number', value: 0.06}, - {name: 'vsindex', op: 22, type: 'number', value: 0}, {name: 'subrs', op: 19, type: 'offset'}, + {name: 'defaultWidthX', op: 20, type: 'number', value: 0}, + {name: 'nominalWidthX', op: 21, type: 'number', value: 0}, + {name: 'vsindex', op: 22, type: 'number', value: 0}, ]; // https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-10-font-dict-operator-entries From aabf3b88352915e951d83fd6cbb6837d5b6f7f12 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Mon, 9 Sep 2024 09:24:33 +0200 Subject: [PATCH 2/8] cff: Fix private DICT processing [why] Tests (would) fail with FDArrayTest257.otf [how] Some sanity checks have been ommited, add them in and process the structures only if there is anything. Signed-off-by: Fini Jastrow --- src/tables/cff.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tables/cff.mjs b/src/tables/cff.mjs index 73edbbbd..c9c40ef2 100755 --- a/src/tables/cff.mjs +++ b/src/tables/cff.mjs @@ -491,7 +491,7 @@ function gatherCFFTopDicts(data, start, cffIndex, strings, version) { const privateDict = parseCFFPrivateDict(data, privateOffset + start, privateSize, strings, version); topDict._defaultWidthX = privateDict.defaultWidthX; topDict._nominalWidthX = privateDict.nominalWidthX; - if (privateDict.subrs !== 0) { + if (privateDict.subrs !== null && privateDict.subrs !== 0) { const subrOffset = privateOffset + privateDict.subrs; const subrIndex = parseCFFIndex(data, subrOffset + start, undefined, version); topDict._subrs = subrIndex.objects; @@ -1282,7 +1282,7 @@ function parseCFFTable(data, start, font, opt) { topDict._fdSelect = parseCFFFDSelect(data, fdSelectOffset, font.numGlyphs, fdArray.length, header.formatMajor); } - if (header.formatMajor < 2) { + if (header.formatMajor < 2 && topDict.private[0] !== 0) { const privateDictOffset = start + topDict.private[1]; const privateDict = parseCFFPrivateDict(data, privateDictOffset, topDict.private[0], stringIndex.objects, header.formatMajor); font.defaultWidthX = privateDict.defaultWidthX; From 07e6e67bb8d0036b87f1f12ad0c708c579183723 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Mon, 9 Sep 2024 09:26:25 +0200 Subject: [PATCH 3/8] cff: Implement private DICT reading and writing for CFF and CFF2 [why] The hinting of CFF (1 and 2) Open Type fonts is usually entirely in their private DICT data. To preserve the hinting - which is needed to make the font rendering look good and as expected - the private DICT data needs to be read and written. [how] Parts of the needed code seem to be added already in preparation for CFF2. I guess there was a misunderstanding or a mixture of versions, but the private DICT did not change between CFF1 and CFF2, and we need to always use the PRIVATE_DICT_META_CFF2, the link given in its definition is the same as given with link [1] for CFF1. See also [2]. So firstly we always refer to 'version 2', as the previous code did also but only in one case. The OtherBlues and FamilyOtherBlues operators must occur after the BlueValues and FamilyBlues operators, respectively. This is implicitely guaranteed by the way the private DICT is set up and finally Object.keys() works. For writing the delta values the encoding function is missing and so that is added. encode.delta() just calls encode.number() repeatedly, figuratively speaking. Last but not least the correct private DICT length has to be set. [1] https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf [2] https://learn.microsoft.com/en-us/typography/opentype/otspec180/cff2#appendixD Signed-off-by: Fini Jastrow --- src/tables/cff.mjs | 12 ++++++------ src/types.mjs | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/tables/cff.mjs b/src/tables/cff.mjs index c9c40ef2..b189c289 100755 --- a/src/tables/cff.mjs +++ b/src/tables/cff.mjs @@ -488,7 +488,7 @@ function gatherCFFTopDicts(data, start, cffIndex, strings, version) { const privateSize = version < 2 ? topDict.private[0] : 0; const privateOffset = version < 2 ? topDict.private[1] : 0; if (privateSize !== 0 && privateOffset !== 0) { - const privateDict = parseCFFPrivateDict(data, privateOffset + start, privateSize, strings, version); + const privateDict = parseCFFPrivateDict(data, privateOffset + start, privateSize, strings, 2); topDict._defaultWidthX = privateDict.defaultWidthX; topDict._nominalWidthX = privateDict.nominalWidthX; if (privateDict.subrs !== null && privateDict.subrs !== 0) { @@ -1284,7 +1284,7 @@ function parseCFFTable(data, start, font, opt) { if (header.formatMajor < 2 && topDict.private[0] !== 0) { const privateDictOffset = start + topDict.private[1]; - const privateDict = parseCFFPrivateDict(data, privateDictOffset, topDict.private[0], stringIndex.objects, header.formatMajor); + const privateDict = parseCFFPrivateDict(data, privateDictOffset, topDict.private[0], stringIndex.objects, 2); font.defaultWidthX = privateDict.defaultWidthX; font.nominalWidthX = privateDict.nominalWidthX; @@ -1562,9 +1562,8 @@ function makeCharStringsIndex(glyphs, version) { function makePrivateDict(attrs, strings, version) { const t = new table.Record('Private DICT', [ - {name: 'dict', type: 'DICT', value: {}} + {name: 'dict', type: 'DICT', value: makeDict(version > 1 ? PRIVATE_DICT_META_CFF2 : PRIVATE_DICT_META, attrs, strings)} ]); - t.dict = makeDict(version > 1 ? PRIVATE_DICT_META_CFF2 : PRIVATE_DICT_META, attrs, strings); return t; } @@ -1608,7 +1607,7 @@ function makeCFFTable(glyphs, options) { attrs.strokeWidth = topDictOptions.strokeWidth || 0; } - const privateAttrs = {}; + const privateAttrs = topDictOptions._privateDict || {}; const glyphNames = []; let glyph; @@ -1628,7 +1627,7 @@ function makeCFFTable(glyphs, options) { t.globalSubrIndex = makeGlobalSubrIndex(); t.charsets = makeCharsets(glyphNames, strings); t.charStringsIndex = makeCharStringsIndex(glyphs, cffVersion); - t.privateDict = makePrivateDict(privateAttrs, strings); + t.privateDict = makePrivateDict(privateAttrs, strings, 2); // Needs to come at the end, to encode all custom strings used in the font. t.stringIndex = makeStringIndex(strings); @@ -1644,6 +1643,7 @@ function makeCFFTable(glyphs, options) { attrs.encoding = 0; attrs.charStrings = attrs.charset + t.charsets.sizeOf(); attrs.private[1] = attrs.charStrings + t.charStringsIndex.sizeOf(); + attrs.private[0] = t.privateDict.sizeOf(); // Recreate the Top DICT INDEX with the correct offsets. topDict = makeTopDict(attrs, strings); diff --git a/src/types.mjs b/src/types.mjs index 6c1b241a..84ce30f5 100644 --- a/src/types.mjs +++ b/src/types.mjs @@ -855,6 +855,13 @@ encode.OPERAND = function(v, type) { for (let j = 0; j < enc1.length; j++) { d.push(enc1[j]); } + } else if (type === 'delta') { + for (let i = 0; i < v.length; i++) { + const enc1 = encode.NUMBER(v[i]); + for (let j = 0; j < enc1.length; j++) { + d.push(enc1[j]); + } + } } else { throw new Error('Unknown operand type ' + type); // FIXME Add support for booleans From ffcfd38460a2da163616bc23ff28bcfc945ad5ac Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Mon, 9 Sep 2024 11:02:59 +0200 Subject: [PATCH 4/8] cff: Humanify user facing way "delta"s are handled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [why] At the moment the delta values are not de- or encoded. The delta format is a packed format to have the smallest possible values in the array of numbers (i.e. relative to the previous number). But usually users do not face this but are shown absolute values; in all other font specific applications. For example BlueValues [ 500, 550 ] // User sees the BlueZone is from 500 to 550 Encoded as [ 500, 50 ] opentype.js at the moment does not translate these deltas in any way and users must know this and use the inconvenient 'packed' encoding format. [how] Convert the read relative delta values to absolute coordinates in the blueValues (and other) properties, that users can interact with. On font encoding time the absolute coordinates are converted back to relative ones and encoded in the font file. [note] https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf The second and subsequent numbers in a delta are encoded as the difference between successive values. For example, an array a0, a1, ..., an would be encoded as: a0 (a1–a0) (a2–a1) ..., (an–a(n–1)) This is done because small numbers can be encoded with fewer bytes and the total aim is to reduce the size. Signed-off-by: Fini Jastrow --- src/tables/cff.mjs | 25 +++++++++++++++++++++++++ test/tables/cff.spec.mjs | 8 ++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/tables/cff.mjs b/src/tables/cff.mjs index b189c289..39e800ba 100755 --- a/src/tables/cff.mjs +++ b/src/tables/cff.mjs @@ -308,6 +308,17 @@ function interpretDict(dict, meta, strings) { if (m.type === 'SID') { value = getCFFString(strings, value); } + if (m.type === 'delta' && value !== null) { + if (!Array.isArray(value) || value.length % 2 != 0) { + throw new Error('Read delta data invalid'); + } + // Convert delta array to human readable version + let current = 0; + for(let i = 0; i < value.length; i++) { + value[i] = value[i] + current; + current = value[i]; + } + } newDict[m.name] = value; } } @@ -1416,6 +1427,20 @@ function makeDict(meta, attrs, strings) { if (entry.type === 'SID') { value = encodeString(value, strings); } + if (entry.type === 'delta' && value !== null) { + if (!Array.isArray(value) || value.length % 2 != 0) { + throw new Error('Provided delta data invalid'); + } + // Convert human readable delta array to DICT version + // See https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf + // Private DICT data > Table 6 Operand Types > delta + let current = 0; + for(let i = 0; i < value.length; i++) { + let nextcurrent = value[i]; + value[i] = value[i] - current; + current = nextcurrent; + } + } m[entry.op] = {name: entry.name, type: entry.type, value: value}; } diff --git a/test/tables/cff.spec.mjs b/test/tables/cff.spec.mjs index ab46f19e..b4e4690a 100644 --- a/test/tables/cff.spec.mjs +++ b/test/tables/cff.spec.mjs @@ -93,10 +93,10 @@ describe('tables/cff.mjs', function () { assert.equal(topDict.vstore, 16); assert.equal(topDict.fdSelect, null); - assert.deepEqual(privateDict1.blueValues, [-20, 20, 472, 18, 35, 15, 105, 15, 10, 20, 40, 20]); - assert.deepEqual(privateDict1.otherBlues, [-250, 10]); - assert.deepEqual(privateDict1.familyBlues, [-20, 20, 473, 18, 34, 15, 104, 15, 10, 20, 40, 20]); - assert.deepEqual(privateDict1.familyOtherBlues, [ -249, 10 ]); + assert.deepEqual(privateDict1.blueValues, [-20, 0, 472, 490, 525, 540, 645, 660, 670, 690, 730, 750]); + assert.deepEqual(privateDict1.otherBlues, [-250, -240]); + assert.deepEqual(privateDict1.familyBlues, [-20, 0, 473, 491, 525, 540, 644, 659, 669, 689, 729, 749]); + assert.deepEqual(privateDict1.familyOtherBlues, [ -249, -239 ]); assert.equal(privateDict1.blueScale, 0.0375); assert.equal(privateDict1.blueShift, 7); assert.equal(privateDict1.blueFuzz, 0); From 37a8e2136877148bb78694dd1161ce360da1dbe6 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Mon, 9 Sep 2024 12:22:30 +0200 Subject: [PATCH 5/8] ccf: Fix wrong type of private DICT StemSnap* [why] The StemSnapH and StemSnapV values are deltas, according to [1]. Also noticed that HERE we have deltas with an odd number of values, so the check for even number of elements is wrong. [how] Change data type and remove check on read/write. Adapt test. [1] https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf (CFF version 1.0, Table 23 'Private DICT Operators') Signed-off-by: Fini Jastrow --- src/tables/cff.mjs | 8 ++++---- test/tables/cff.spec.mjs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tables/cff.mjs b/src/tables/cff.mjs index 39e800ba..58797cc0 100755 --- a/src/tables/cff.mjs +++ b/src/tables/cff.mjs @@ -309,7 +309,7 @@ function interpretDict(dict, meta, strings) { value = getCFFString(strings, value); } if (m.type === 'delta' && value !== null) { - if (!Array.isArray(value) || value.length % 2 != 0) { + if (!Array.isArray(value)) { throw new Error('Read delta data invalid'); } // Convert delta array to human readable version @@ -417,8 +417,8 @@ const PRIVATE_DICT_META_CFF2 = [ {name: 'blueFuzz', op: 1211, type: 'number', value: 1}, {name: 'stdHW', op: 10, type: 'number'}, {name: 'stdVW', op: 11, type: 'number'}, - {name: 'stemSnapH', op: 1212, type: 'number'}, - {name: 'stemSnapV', op: 1213, type: 'number'}, + {name: 'stemSnapH', op: 1212, type: 'delta'}, + {name: 'stemSnapV', op: 1213, type: 'delta'}, {name: 'languageGroup', op: 1217, type: 'number', value: 0}, {name: 'expansionFactor', op: 1218, type: 'number', value: 0.06}, {name: 'subrs', op: 19, type: 'offset'}, @@ -1428,7 +1428,7 @@ function makeDict(meta, attrs, strings) { value = encodeString(value, strings); } if (entry.type === 'delta' && value !== null) { - if (!Array.isArray(value) || value.length % 2 != 0) { + if (!Array.isArray(value)) { throw new Error('Provided delta data invalid'); } // Convert human readable delta array to DICT version diff --git a/test/tables/cff.spec.mjs b/test/tables/cff.spec.mjs index b4e4690a..880c53c7 100644 --- a/test/tables/cff.spec.mjs +++ b/test/tables/cff.spec.mjs @@ -102,8 +102,8 @@ describe('tables/cff.mjs', function () { assert.equal(privateDict1.blueFuzz, 0); assert.equal(privateDict1.stdHW, 55); assert.equal(privateDict1.stdVW, 80); - assert.deepEqual(privateDict1.stemSnapH, [40, 15]); - assert.deepEqual(privateDict1.stemSnapV, [80, 10]); + assert.deepEqual(privateDict1.stemSnapH, [40, 55]); + assert.deepEqual(privateDict1.stemSnapV, [80, 90]); assert.equal(privateDict1.languageGroup, 0); assert.equal(privateDict1.expansionFactor, 0.06); assert.deepEqual(privateDict1.vsindex, 0); From 392a6b72af8d1904b82a528afc9df830c7684475 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Mon, 9 Sep 2024 17:18:52 +0200 Subject: [PATCH 6/8] cff: Fix not-handling subrs [why] The code does not handle subrs, but it includes a previously existing subrs operator in the private DICT which throws all the offsets off. [how] Either include the subrs subtable and keep the subrs operator, or drop both. This commits just drops the operator (for simplicity). Proper subrs handling can be added later on. Signed-off-by: Fini Jastrow --- src/tables/cff.mjs | 7 ++++++- src/types.mjs | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tables/cff.mjs b/src/tables/cff.mjs index 58797cc0..9c52be2a 100755 --- a/src/tables/cff.mjs +++ b/src/tables/cff.mjs @@ -1299,7 +1299,7 @@ function parseCFFTable(data, start, font, opt) { font.defaultWidthX = privateDict.defaultWidthX; font.nominalWidthX = privateDict.nominalWidthX; - if (privateDict.subrs !== 0) { + if (privateDict.subrs !== null && privateDict.subrs !== 0) { const subrOffset = privateDictOffset + privateDict.subrs; const subrIndex = parseCFFIndex(data, subrOffset); font.subrs = subrIndex.objects; @@ -1586,6 +1586,11 @@ function makeCharStringsIndex(glyphs, version) { } function makePrivateDict(attrs, strings, version) { + // we do not handle (include) subrs, so we must not create the operator + if ('subrs' in attrs) { + attrs = new Object(attrs); + delete attrs['subrs']; + } const t = new table.Record('Private DICT', [ {name: 'dict', type: 'DICT', value: makeDict(version > 1 ? PRIVATE_DICT_META_CFF2 : PRIVATE_DICT_META, attrs, strings)} ]); diff --git a/src/types.mjs b/src/types.mjs index 84ce30f5..7e2f51cf 100644 --- a/src/types.mjs +++ b/src/types.mjs @@ -783,6 +783,9 @@ encode.DICT = function(m) { // Object.keys() return string keys, but our keys are always numeric. const k = parseInt(keys[i], 0); const v = m[k]; + if (v.value === null) { + continue; + } // Value comes before the key. const enc1 = encode.OPERAND(v.value, v.type); const enc2 = encode.OPERATOR(k); From dad7f7493d331c87c6732e8830012d4b1bc0f609 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Mon, 9 Sep 2024 17:21:49 +0200 Subject: [PATCH 7/8] cff: test: Roundtrip check of private DICT [why] Writing of the private DICT has been added together with the translation of delta values from transport encoding to readable and back. But there is no automated test to see if something breaks. [how] Take one of the fonts in test/fonts/ and run in through ttx to see its private DICT data analyzed by someone else. Add a test that also reads that fond and checks some of the private DICT values. Write the font into a buffer and read it back in; repeat the check on the read back font. This also checks if writing is correct. Signed-off-by: Fini Jastrow --- test/tables/cff.spec.mjs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/tables/cff.spec.mjs b/test/tables/cff.spec.mjs index 880c53c7..34bada1a 100644 --- a/test/tables/cff.spec.mjs +++ b/test/tables/cff.spec.mjs @@ -215,4 +215,32 @@ describe('tables/cff.mjs', function () { transformedPoints ); }); + + it('does round trip CFF private DICT', function() { + const font = loadSync('./test/fonts/AbrilFatface-Regular.otf'); + const checkerFunktion = function(inputFont) { + // from ttx: + // + // + // + // + // + // + // + // + // + // + // + const privateDict = inputFont.tables.cff.topDict._privateDict; + assert.deepEqual(privateDict.blueValues, [-10, 0, 476, 486, 700, 711]); + assert.deepEqual(privateDict.otherBlues, [-250, -238]); + assert.equal(privateDict.stdHW, 18); + assert.deepEqual(privateDict.stemSnapH, [16, 18, 21]); + assert.equal(privateDict.nominalWidthX, 590); + }; + checkerFunktion(font); + let buffer = font.toArrayBuffer() + let font2 = parse(buffer); + checkerFunktion(font2); + }); }); From 332c8f20a51a1f2f485b1f1fdde1ae911b07f61e Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Mon, 9 Sep 2024 17:57:40 +0200 Subject: [PATCH 8/8] cff: Revert to use different private DICTs for CFF and CFF2 [why] While CFF has no `vsindex` operator in the private DICT it is a defaulted operator in CFF2. This can not be really disentangled with the current code and the opertor ends up erroreously written to CFF fonts (where it is usually ignored). [how] It took a bit time to understand the CFF version code (that I believe is not finished), but reinstate that code with different tables to be used. Sorry I messed with the code in the first place; I had overlooked the `vsindex` operator and how a defaulted operator in the tables turn out in the written files. I just reverted the version 'detection' code as it was, not checking anything. Note that there still is one hard-coded `2` somewhere, but that was already there when I started. Signed-off-by: Fini Jastrow --- src/tables/cff.mjs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/tables/cff.mjs b/src/tables/cff.mjs index 9c52be2a..1492a395 100755 --- a/src/tables/cff.mjs +++ b/src/tables/cff.mjs @@ -401,13 +401,6 @@ const TOP_DICT_META_CFF2 = [ ]; const PRIVATE_DICT_META = [ - {name: 'subrs', op: 19, type: 'offset', value: 0}, - {name: 'defaultWidthX', op: 20, type: 'number', value: 0}, - {name: 'nominalWidthX', op: 21, type: 'number', value: 0} -]; - -// https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-16-private-dict-operators -const PRIVATE_DICT_META_CFF2 = [ {name: 'blueValues', op: 6, type: 'delta'}, {name: 'otherBlues', op: 7, type: 'delta'}, {name: 'familyBlues', op: 8, type: 'delta'}, @@ -424,9 +417,16 @@ const PRIVATE_DICT_META_CFF2 = [ {name: 'subrs', op: 19, type: 'offset'}, {name: 'defaultWidthX', op: 20, type: 'number', value: 0}, {name: 'nominalWidthX', op: 21, type: 'number', value: 0}, - {name: 'vsindex', op: 22, type: 'number', value: 0}, + {name: 'subrs', op: 19, type: 'offset', value: 0}, + {name: 'defaultWidthX', op: 20, type: 'number', value: 0}, + {name: 'nominalWidthX', op: 21, type: 'number', value: 0} ]; +// https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-16-private-dict-operators +const PRIVATE_DICT_META_CFF2 = PRIVATE_DICT_META.concat([ + {name: 'vsindex', op: 22, type: 'number', value: 0}, +]); + // https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-10-font-dict-operator-entries const FONT_DICT_META = [ {name: 'private', op: 18, type: ['number', 'offset'], value: [0, 0]} @@ -499,7 +499,7 @@ function gatherCFFTopDicts(data, start, cffIndex, strings, version) { const privateSize = version < 2 ? topDict.private[0] : 0; const privateOffset = version < 2 ? topDict.private[1] : 0; if (privateSize !== 0 && privateOffset !== 0) { - const privateDict = parseCFFPrivateDict(data, privateOffset + start, privateSize, strings, 2); + const privateDict = parseCFFPrivateDict(data, privateOffset + start, privateSize, strings, version); topDict._defaultWidthX = privateDict.defaultWidthX; topDict._nominalWidthX = privateDict.nominalWidthX; if (privateDict.subrs !== null && privateDict.subrs !== 0) { @@ -1295,7 +1295,7 @@ function parseCFFTable(data, start, font, opt) { if (header.formatMajor < 2 && topDict.private[0] !== 0) { const privateDictOffset = start + topDict.private[1]; - const privateDict = parseCFFPrivateDict(data, privateDictOffset, topDict.private[0], stringIndex.objects, 2); + const privateDict = parseCFFPrivateDict(data, privateDictOffset, topDict.private[0], stringIndex.objects, header.formatMajor); font.defaultWidthX = privateDict.defaultWidthX; font.nominalWidthX = privateDict.nominalWidthX; @@ -1657,7 +1657,7 @@ function makeCFFTable(glyphs, options) { t.globalSubrIndex = makeGlobalSubrIndex(); t.charsets = makeCharsets(glyphNames, strings); t.charStringsIndex = makeCharStringsIndex(glyphs, cffVersion); - t.privateDict = makePrivateDict(privateAttrs, strings, 2); + t.privateDict = makePrivateDict(privateAttrs, strings, cffVersion); // Needs to come at the end, to encode all custom strings used in the font. t.stringIndex = makeStringIndex(strings);