diff --git a/src/core/cff_parser.js b/src/core/cff_parser.js index 9fbd85305..2a78c79e5 100644 --- a/src/core/cff_parser.js +++ b/src/core/cff_parser.js @@ -902,10 +902,14 @@ class CFFParser { // BlueScale up only misaligns/collapses overshooting glyphs (notably // with macOS's Core Text rasterizer). Only apply the lower clamp when // its target does not exceed the default. + // Round the bound in order to avoid too long operand (issue 21466). + const PRECISION = 1e5; const lowerBound = 0.5 / maxZoneHeight; const minBlueScale = - lowerBound <= DEFAULT_BLUE_SCALE ? lowerBound : -Infinity; - const maxBlueScale = 1 / maxZoneHeight; + lowerBound <= DEFAULT_BLUE_SCALE + ? Math.ceil(lowerBound * PRECISION) / PRECISION + : -Infinity; + const maxBlueScale = Math.floor(PRECISION / maxZoneHeight) / PRECISION; const clamped = MathClamp(blueScale, minBlueScale, maxBlueScale); if (clamped !== blueScale) { privateDict.setByName("BlueScale", clamped); diff --git a/test/unit/cff_parser_spec.js b/test/unit/cff_parser_spec.js index 80ee917d5..f368d5fa8 100644 --- a/test/unit/cff_parser_spec.js +++ b/test/unit/cff_parser_spec.js @@ -389,6 +389,29 @@ describe("CFFParser", function () { ); }); + it("clamps BlueScale to a short decimal so the recompiled operand stays compact", function () { + // maxZoneHeight = 13 gives lower bound (0.5 / 13 = 0.038461538461538464) + // which is too long (issue 21466). + cff.topDict.privateDict = new CFFPrivateDict(cff.strings); + cff.topDict.privateDict.setByName( + "BlueValues", + [-13, 13, 530, 13, 220, 13, 30, 13] + ); + cff.topDict.privateDict.setByName("BlueScale", 0.01); + cff.topDict.setByName("Private", [0, 0]); + const fontDataShortBlueScale = new CFFCompiler(cff).compile(); + + const reparsedCff = new CFFParser( + new Stream(fontDataShortBlueScale), + {}, + SEAC_ANALYSIS_ENABLED + ).parse(); + + const blueScale = reparsedCff.topDict.privateDict.getByName("BlueScale"); + expect(blueScale).toEqual(0.03847); + expect(new CFFCompiler(cff).encodeFloat(blueScale).length).toBeLessThan(6); + }); + it("refuses to add topDict key with invalid value (bug 1068432)", function () { const topDict = cff.topDict; const defaultValue = topDict.getByName("UnderlinePosition");