diff --git a/config/dompdf.php b/config/dompdf.php index 9e7bcf43..f53a5833 100644 --- a/config/dompdf.php +++ b/config/dompdf.php @@ -245,8 +245,25 @@ return [ /** * A ratio applied to the fonts height to be more like browsers' line height + * + * dompdf does not use a declared `line-height` directly: it scales it by + * the font's own height, so the rendered line box is + * + * declared × (ascent + descent) / unitsPerEm × font_height_ratio + * + * The bundled Noto Sans reports 1.362 for that middle term, so at the + * stock 1.1 every line-height in every PDF came out 1.4985× what the CSS + * asked for. Chromium honours the declared value exactly, which is where + * the two drivers disagreed vertically on every document. + * + * 1 / 1.362 cancels the font term, so a declared line-height means what + * it says and both drivers agree. Measured: a declared 15px (11.25pt) + * renders at 16.86pt here at 1.1, 15.32pt at 1.0, and 11.25pt at this + * value -- identical to Chromium. Calibrated against Noto Sans, the + * default face; PdfLineHeightTest pins it so a font or dompdf upgrade + * that moves it fails rather than drifting. */ - 'font_height_ratio' => 1.1, + 'font_height_ratio' => 0.7342, /** * Use the more-than-experimental HTML5 Lib parser diff --git a/tests/Unit/PdfLineHeightTest.php b/tests/Unit/PdfLineHeightTest.php new file mode 100644 index 00000000..513775e5 --- /dev/null +++ b/tests/Unit/PdfLineHeightTest.php @@ -0,0 +1,53 @@ +getDomPDF()->getFontMetrics(); + $font = $metrics->getFont('NotoSans', 'normal'); + + expect($font)->not->toBeFalsy('NotoSans should resolve; it is the bundled default face'); + + return $metrics->getFontHeight($font, $size); +} + +test('a declared line-height is rendered at its declared size', function (float $size) { + expect(notoSansHeightAt($size))->toEqualWithDelta($size, 0.01); +})->with([9.0, 12.0, 15.0, 18.0, 24.0]); + +test('the configured ratio is the one that cancels the font metrics', function () { + // Guards the value itself, so a well-meaning "restore the dompdf default" + // shows up as a failing test rather than as documents silently growing. + expect(config('dompdf.defines.font_height_ratio'))->toEqualWithDelta(0.7342, 0.0005); +}); + +/** + * The stock ratio is what the drift looked like: 1.1 × 1.362 ≈ 1.4985, which is + * within a rounding error of the 1.5 that a hand-tuned compensation shim had + * arrived at empirically. + */ +test('dompdf stock ratio would inflate every line by about half again', function () { + $natural = notoSansHeightAt(12.0) / config('dompdf.defines.font_height_ratio'); + + expect($natural * 1.1 / 12.0)->toEqualWithDelta(1.4985, 0.005); +});