Files
Reactive-Resume/patches/@react-pdf__textkit@6.4.0.patch
T
Amruth Pillai b4f245a38e fix(deps): restore @react-pdf/textkit patch dropped by the 4.6.0 bump
The dependency-update commit bumped @react-pdf/renderer 4.5.1->4.6.0 (textkit
6.3.0->6.4.0) and silently dropped the pnpm patch that overrides font vertical
metrics to prefer OS/2 sTypo* over inflated hhea values, matching browser line
boxes. 6.4.0 did not upstream it, so the semantic-CSS template visual snapshots
(baselined with the patch, maxDiffPixelRatio 0) no longer matched and the E2E
job failed. Re-create the patch for textkit@6.4.0 and re-register it in
patchedDependencies; remove the orphaned 6.3.0 patch.

Claude-Session: https://claude.ai/code/session_01ULhhLQ24DvnYwzP4afDuye
2026-08-09 14:41:01 +02:00

76 lines
2.9 KiB
Diff

diff --git a/lib/textkit.js b/lib/textkit.js
index 40814303e013f68675712caaeb3ca21617cfc9e7..7d337ad45eab263af25e9b724dd288509ab8bb82 100644
--- a/lib/textkit.js
+++ b/lib/textkit.js
@@ -709,6 +709,31 @@ const omit = (value, run) => {
return Object.assign({}, run, { attributes });
};
+/**
+ * Resolve a fontkit Font's vertical metrics, preferring the OS/2
+ * `sTypoAscender / sTypoDescender / sTypoLineGap` triple when available
+ * and falling back to hhea. This matches Chromium's font-metrics
+ * resolution and is what browsers (and the v5.0.x Puppeteer renderer)
+ * use, giving compact, predictable line boxes for fonts whose hhea
+ * values are inflated for Windows GDI compatibility (notably Source
+ * Han Sans/Serif a.k.a. Noto Sans/Serif CJK).
+ *
+ * `f['OS/2']` is exposed by fontkit when the font carries an OS/2 table.
+ * Standard PDF fonts (Helvetica/Courier/Times) and our internal
+ * EmbeddedFont don't, so they keep their existing hhea-based metrics.
+ */
+const resolveTypoMetrics = (font) => {
+ const os2 = font?.['OS/2'];
+ if (!os2 || typeof os2.typoAscender !== 'number' || typeof os2.typoDescender !== 'number') {
+ return { ascent: font?.ascent || 0, descent: font?.descent || 0, lineGap: font?.lineGap || 0 };
+ }
+ return {
+ ascent: os2.typoAscender,
+ descent: os2.typoDescender,
+ lineGap: typeof os2.typoLineGap === 'number' ? os2.typoLineGap : (font.lineGap || 0),
+ };
+};
+
/**
* Get run ascent
*
@@ -718,7 +743,7 @@ const omit = (value, run) => {
const ascent$1 = (run) => {
const { font, attachment } = run.attributes;
const attachmentHeight = attachment?.height || 0;
- const fontAscent = typeof font === 'string' ? 0 : font?.[0]?.ascent || 0;
+ const fontAscent = typeof font === 'string' ? 0 : resolveTypoMetrics(font?.[0]).ascent;
return Math.max(attachmentHeight, fontAscent * scale(run));
};
@@ -730,7 +755,7 @@ const ascent$1 = (run) => {
*/
const descent = (run) => {
const font = run.attributes?.font;
- const fontDescent = typeof font === 'string' ? 0 : font?.[0]?.descent || 0;
+ const fontDescent = typeof font === 'string' ? 0 : resolveTypoMetrics(font?.[0]).descent;
return scale(run) * fontDescent;
};
@@ -742,8 +767,8 @@ const descent = (run) => {
*/
const lineGap = (run) => {
const font = run.attributes?.font;
- const lineGap = typeof font === 'string' ? 0 : font?.[0]?.lineGap || 0;
- return lineGap * scale(run);
+ const fontLineGap = typeof font === 'string' ? 0 : resolveTypoMetrics(font?.[0]).lineGap;
+ return fontLineGap * scale(run);
};
/**
@@ -754,7 +779,8 @@ const lineGap = (run) => {
*/
const height$1 = (run) => {
const lineHeight = run.attributes?.lineHeight;
- return lineHeight || lineGap(run) + ascent$1(run) - descent(run);
+ const intrinsic = lineGap(run) + ascent$1(run) - descent(run);
+ return Math.max(lineHeight || 0, intrinsic);
};
/**