From 7937adb405e7031fcac48f615f2edd263ae442a6 Mon Sep 17 00:00:00 2001 From: Andreas Lind Date: Sun, 24 Mar 2024 09:11:17 +0100 Subject: [PATCH] Support reducing the variation space of individual axes (partial instancing) --- index.js | 52 +++++++++++++++++++++++++++++++++++++++++++++------ test/index.js | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 2b8c626..b6a1dab 100644 --- a/index.js +++ b/index.js @@ -84,12 +84,52 @@ async function subsetFont( if (variationAxes) { for (const [axisName, value] of Object.entries(variationAxes)) { - exports.hb_subset_input_pin_axis_location( - input, - face, - HB_TAG(axisName), - value - ); + if (typeof value === 'number') { + // Simple case: Pin/instance the variation axis to a single value + if ( + !exports.hb_subset_input_pin_axis_location( + input, + face, + HB_TAG(axisName), + value + ) + ) { + exports.hb_face_destroy(face); + exports.free(fontBuffer); + throw new Error( + `hb_subset_input_pin_axis_location (harfbuzz) returned zero when pinning ${axisName} and a value of ${value}, indicating failure.` + ); + } + } else if (value && typeof value === 'object') { + // Complex case: Reduce the variation space of the axis + if ( + typeof value.min === 'undefined' || + typeof value.max === 'undefined' + ) { + exports.hb_face_destroy(face); + exports.free(fontBuffer); + throw new Error( + `${axisName}: You must provide both a min and a max value when setting the axis range` + ); + } + if ( + !exports.hb_subset_input_set_axis_range( + input, + face, + HB_TAG(axisName), + value.min, + value.max, + // An explicit NaN makes harfbuzz use the existing default value, clamping to the new range if necessary + value.default ?? NaN + ) + ) { + exports.hb_face_destroy(face); + exports.free(fontBuffer); + throw new Error( + `hb_subset_input_set_axis_range (harfbuzz) returned zero when setting the range of ${axisName} to [${min}; ${max}] and a default value of ${defaultValue}, indicating failure.` + ); + } + } } } diff --git a/test/index.js b/test/index.js index 6177dcc..a7791f4 100644 --- a/test/index.js +++ b/test/index.js @@ -407,6 +407,42 @@ describe('subset-font', function () { slnt: { name: 'slnt', min: -10, default: 0, max: 0 }, }); + // When not instancing the subset font is about 29 KB + expect(result.length, 'to be less than', 26000); + }); + }); + + describe('when reducing the ranges of some variation axes', function () { + it('should perform a partial instancing', async function () { + const result = await subsetFont(this.variableRobotoFont, 'abcd', { + variationAxes: { + GRAD: { min: -50, max: 50, default: 25 }, + slnt: { min: -9, max: 0 }, + YTDE: { min: -100, max: -98 }, + opsz: 14, + XTRA: 468, + XOPQ: 96, + YOPQ: 79, + YTLC: 514, + YTUC: 712, + YTAS: 750, + YTFI: 738, + // Leaving out wght and wdth so that the full variation space is preserved + }, + }); + + expect( + fontkit.create(result).variationAxes, + 'to exhaustively satisfy', + { + GRAD: { name: 'GRAD', min: -50, max: 50, default: 25 }, + slnt: { name: 'slnt', min: -9, max: 0, default: 0 }, + YTDE: { name: 'YTDE', min: -100, max: -98, default: -100 }, + wght: { name: 'wght', min: 100, max: 1000, default: 400 }, + wdth: { name: 'wdth', min: 25, max: 151, default: 100 }, + } + ); + // When not instancing the subset font is about 29 KB expect(result.length, 'to be less than', 25000); });