From bc99fc0678b36fcae7d0e2397a50cf602c5f95d6 Mon Sep 17 00:00:00 2001 From: calixteman Date: Tue, 16 Jun 2026 19:30:42 +0200 Subject: [PATCH] Don't clamp BlueScale up when a font genuinely has small zones The lower BlueScale clamp from #21343 guarded foundry fonts via `blueScale < DEFAULT_BLUE_SCALE`, but that lets a near-default value (e.g. 0.037) with small zones get raised to `0.5 / maxZoneHeight`. On macOS' Core Text rasterizer this collapses the overshooting glyphs, so most text disappears (not reproducible on Linux/Windows). --- src/core/cff_parser.js | 11 +++++++- test/pdfs/.gitignore | 1 + test/pdfs/cff_bluescale_small_zones.pdf | Bin 0 -> 9708 bytes test/unit/cff_parser_spec.js | 32 ++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 test/pdfs/cff_bluescale_small_zones.pdf diff --git a/src/core/cff_parser.js b/src/core/cff_parser.js index 42502f5a6..9fbd85305 100644 --- a/src/core/cff_parser.js +++ b/src/core/cff_parser.js @@ -894,8 +894,17 @@ class CFFParser { } } if (maxZoneHeight > 0) { + // The lower bound of AFDKO's valid window is `0.5 / maxZoneHeight`. + // When that bound is itself above the default BlueScale the font simply + // has small zones (e.g. Eurostile LT Std, or the SofiaPro fonts shipped + // with a near-default 0.037): even the default 0.039625 would be + // flagged as out-of-range, so this is the rendered intent and forcing + // 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. + const lowerBound = 0.5 / maxZoneHeight; const minBlueScale = - blueScale < DEFAULT_BLUE_SCALE ? 0.5 / maxZoneHeight : -Infinity; + lowerBound <= DEFAULT_BLUE_SCALE ? lowerBound : -Infinity; const maxBlueScale = 1 / maxZoneHeight; const clamped = MathClamp(blueScale, minBlueScale, maxBlueScale); if (clamped !== blueScale) { diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 9d265fbf6..87b54309b 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -937,3 +937,4 @@ !checkbox_no_appearance.pdf !opt_demo.pdf !bug1873345.pdf +!cff_bluescale_small_zones.pdf diff --git a/test/pdfs/cff_bluescale_small_zones.pdf b/test/pdfs/cff_bluescale_small_zones.pdf new file mode 100644 index 0000000000000000000000000000000000000000..178ba2089269aea87c0d0472be394f14fed61996 GIT binary patch literal 9708 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�d@AstnEnTJ2NBHh zokm0^Mq(mn(wH=L(6p(Y|Jh?zFl0tHoTqD=X4N^=>NGZ8VZjmzbfIK9R$Yb&w zc|x9&XXH5nqL2#mC!tzS%WE}SK`UyJ7Hf%CtF5Dzv^uSWwyxGutJl`o8ng|xA80?+ zHq<(6U9^p~veu|IY0cWk+9q08Z7XeSt-IEuwQ4=Io?4sMOY5!m(fVrrwC#1Sa;ex# z(iyr)v>_E*s|gVZt)L-Gu)VvSef;Rc*_*d6c5Bl*xSI-_d?C|@W*8v9a)pL>OLyPc zZVXt9hKwCJuCGgnuw5lnOv?L8eYteuk)79Et{ffO;X9M#LrlMCr+XXrU?J8+d=^3w zANdqV>+3*RI*K zWlMU(9Ahbd;J34*TlYSl1NOvU%`%>zgtkmul|9NiepE_$-%%U3=NjANHecNuQ0l^9 zvO`JT*QH653H9h1(QIJ9H3w6SfpN%h@3rAKU7+355*SyVLKq59uim@E)bA)tPKlWi z?cyF-@aWN{v-k3LPDoy3I(!uE8PRi1kPCIDb*dy+rxiFeF|q5})oo|a>>Jd{NEesE zG6EmW3A#$PwUXAJ5=#p&9UaoubQi;Wt*_9!)WO_=>LQnUL5oqwJDEs#SOQlnKCT8R z;5QZ_E(Ko}P#&!~qu=r;YT(TT>xWqkHkalqb6L@ciTDnSgHJKSag_tmH4LfBfYX&r znTW5*gRKE~SvvZ6ZbZ*1KOrlF&6K4WE-7I<}+|`-qDv&onJ+;Sf;g+&R2211$VFq{APg8`23#!ojb)2Xkntws&Z=Bp=Oe$ zEEi|N^HlYxzfmEvTysd!$3DYNr^kkG4t43?XLMACNuPI-0W3G3{JQJcle?li+Lbkh z{Jtx{B-dE5YhWi+jrMw8rFClIpdl>WY@({XfT&|xP=;zm~uXm@&y~oFyf?|>P zj*?!0T)_G9U1$h;&th87E)5-vjt-ST{&*GjEN8jKQZ&-6@W*3tWl=G-Z2W}QH%?lIrA3Llf{$__G>&q%ySFL1-@`rMU<-w}IMGoCmn9z>i zmrE*cUv&`GQc_JZt0KgXvA}J)TzL*7SvyM*p<@+j^aGt%49!`VRYT66254IL12n<8CE@n8rA5whO>E7G&?Ec~vpXIEmMwA$aXRAWV zYRzgdQU|+*u>`vsEP}n2nTAWp@-JNqKOPv+H!LvlXvF`D>AXARZz0ENopn0hnw6V3 zrL0hUV^h{{+qP!i_HFBu;^LB$vun_QjG!i2-!-EnP)Om9D@qrxXTUgB7LxzAEer>i_lE!cVxXI)?LIRK>hu zL+Xk|=HANlQWf26RHTEmW$#QKkRMTGJT1nitl3`0KGVJGEb0|-=IOKI6IEs1Z8%fw zh=+U`#@8~EkJ!;}C%nwSpXw1c50<@Dp`kSm?74;|;3@1`W`por&ANYnW-}ozo_*J2 z_LJCu?yE1ex0e_DDr2PiO`DZ{jH6!Wl)u}&*{4JAv{~7ABk#6(*w~jC@6g#S|D;pD z^>~-g&xd`1^Y&P))vwmrjI