Apply the appearance-stream scale factor when text is shown, not on setFont

The font size (Tf) and the text matrix (Tm) can appear in any order in an
appearance stream. Applying the scale factor eagerly in setFont missed the
case where Tf precedes Tm (e.g. Skia-generated FreeText), yielding a wrong
guessed font size.
This commit is contained in:
Calixte Denizet 2026-07-02 14:51:10 +02:00
parent 43c29379de
commit d9999dcedd
4 changed files with 49 additions and 1 deletions

View File

@ -154,7 +154,7 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor {
result.fontName = fontName.name;
}
if (typeof fontSize === "number" && fontSize > 0) {
result.fontSize = fontSize * result.scaleFactor;
result.fontSize = fontSize;
}
break;
case OPS.setFillColorSpace:
@ -184,6 +184,10 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor {
case OPS.showSpacedText:
case OPS.nextLineShowText:
case OPS.nextLineSetSpacingShowText:
// The font (Tf) and the text matrix (Tm) can be set in any order,
// so the scale factor is applied here, when text is actually shown
// and both are known to be in effect.
result.fontSize *= result.scaleFactor;
breakLoop = true;
break;
}

View File

@ -1876,6 +1876,49 @@ describe("FreeText Editor", () => {
});
});
describe("FreeText (open existing generated with Skia)", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"issue20504_skia.pdf",
".annotationEditorLayer",
100
);
});
afterEach(async () => {
await closePages(pages);
});
it("must extract the font size when Tf comes before Tm", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
const id = "24R";
await page.waitForSelector(getAnnotationSelector(id), {
visible: true,
});
await switchToFreeText(page);
// The Skia appearance stream sets the font (Tf) before the text
// matrix (Tm), and the text matrix is scaled by 0.5 through a cm
// operator: 20 * 0.5 = 10.
const fontSize = await page.evaluate(() => {
const editorDiv = document.getElementById(
`pdfjs_internal_editor_0-editor`
);
const match = editorDiv?.style.fontSize.match(/calc\((\d+)px/);
return match ? parseInt(match[1], 10) : 0;
});
expect(fontSize)
.withContext(`In ${browserName}, editor 0 fontSize`)
.toEqual(10);
})
);
});
});
describe("Keyboard shortcuts when the editor layer isn't focused", () => {
let pages;

View File

@ -941,3 +941,4 @@
!multimedia_annotations.pdf
!issue17333.pdf
!issue20504.pdf
!issue20504_skia.pdf

Binary file not shown.