From ccab03f937be8b0e90b55fa3fdae3f69bcb6fbb6 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Mon, 9 Sep 2024 11:02:59 +0200 Subject: [PATCH] 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 | 26 ++++++++++++++++++++++++++ test/tables/cff.spec.mjs | 8 ++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/tables/cff.mjs b/src/tables/cff.mjs index b189c289..7f4d3d0d 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,21 @@ 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; + } + console.log("OUTCOMING", value); + } 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);