diff --git a/src/core/xfa/xhtml.js b/src/core/xfa/xhtml.js
index 788e8c762ac26..edc8ff630d606 100644
--- a/src/core/xfa/xhtml.js
+++ b/src/core/xfa/xhtml.js
@@ -236,8 +236,7 @@ class XhtmlObject extends XmlObject {
}
}
- [$pushGlyphs](measure, mustPop = true) {
- const xfaFont = Object.create(null);
+ [$pushGlyphs](measure, mustPop = true, xfaFont = Object.create(null)) {
const margin = {
top: NaN,
bottom: NaN,
@@ -374,9 +373,7 @@ class B extends XhtmlObject {
}
[$pushGlyphs](measure) {
- measure.pushFont({ weight: "bold" });
- super[$pushGlyphs](measure);
- measure.popFont();
+ super[$pushGlyphs](measure, /* mustPop = */ true, { weight: "bold" });
}
}
@@ -464,9 +461,7 @@ class I extends XhtmlObject {
}
[$pushGlyphs](measure) {
- measure.pushFont({ posture: "italic" });
- super[$pushGlyphs](measure);
- measure.popFont();
+ super[$pushGlyphs](measure, /* mustPop = */ true, { posture: "italic" });
}
}
diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore
index 65b515800a631..ff657d28d8431 100644
--- a/test/pdfs/.gitignore
+++ b/test/pdfs/.gitignore
@@ -1065,3 +1065,4 @@
!issue19954_reduced.pdf
!issue1936_reduced.pdf
!issue22462.pdf
+!xfa_issue22023.pdf
diff --git a/test/pdfs/xfa_issue22023.pdf b/test/pdfs/xfa_issue22023.pdf
new file mode 100644
index 0000000000000..18cc3c5a1849b
Binary files /dev/null and b/test/pdfs/xfa_issue22023.pdf differ
diff --git a/test/test_manifest.json b/test/test_manifest.json
index ee1be8a54d5c8..032134f59a34d 100644
--- a/test/test_manifest.json
+++ b/test/test_manifest.json
@@ -15343,5 +15343,14 @@
"rounds": 1,
"type": "eq",
"about": "Short loca offsets overflow after duplicating glyph 0."
+ },
+ {
+ "id": "xfa_issue22023",
+ "file": "pdfs/xfa_issue22023.pdf",
+ "md5": "44b75da68e9ccc5c24dc4f9ea05399bf",
+ "rounds": 1,
+ "enableXfa": true,
+ "type": "eq",
+ "about": "XFA rich text with b and i elements."
}
]
diff --git a/test/unit/xfa_tohtml_spec.js b/test/unit/xfa_tohtml_spec.js
index 046c272f1932e..6bd4c33c33d61 100644
--- a/test/unit/xfa_tohtml_spec.js
+++ b/test/unit/xfa_tohtml_spec.js
@@ -689,4 +689,91 @@ describe("XFAFactory", function () {
const p = searchHtmlNode(pages, "name", "p");
expect(p.attributes.style.fontSize).toEqual("13.86px");
});
+
+ it("should measure b and i elements like the equivalent styles", async () => {
+ const draws = {
+ regular: "Hello World",
+ b: "Hello World",
+ boldSpan: `Hello World`,
+ i: "Hello World",
+ italicSpan: `Hello World`,
+ bi: "Hello World",
+ boldItalicSpan: `Hello World`,
+ bInSpan: `Hello
World`,
+ boldSpanInSpan: `Hello
World`,
+ bWithStyle: `Hello`,
+ spanWithStyle: `Hello`,
+ };
+ const xml = `
+
+
+
+
+
+
+
+
+
+
+
+ ${Object.entries(draws)
+ .map(
+ ([name, html]) => `
+
+
+
+
+ ${html}
+
+
+ `
+ )
+ .join("")}
+
+
+
+
+ `;
+ const factory = new XFAFactory(new Map([["xdp:xdp", xml]]));
+ // Glyph widths differ per variant, so the selected font shows in the size.
+ const makeFont = (name, fontWeight, italicAngle, width) => ({
+ name,
+ cssFontInfo: { fontFamily: "Helvetica", fontWeight, italicAngle },
+ lineHeight: 1.2,
+ lineGap: 0.2,
+ defaultWidth: width,
+ encodeString: str => [str],
+ charsToGlyphs: str => Array.from(str, unicode => ({ unicode, width })),
+ });
+ factory.setFonts([
+ makeFont("Helvetica", 400, 0, 500),
+ makeFont("Helvetica-Bold", 700, 0, 600),
+ makeFont("Helvetica-Oblique", 400, -12, 550),
+ makeFont("Helvetica-BoldOblique", 700, -12, 650),
+ ]);
+
+ expect(await factory.getNumPages()).toEqual(1);
+
+ const pages = await factory.getPages();
+ const container = searchHtmlNode(pages, "xfaName", "draws", true);
+ const sizes = new Map(
+ container.children.map(({ attributes, children }) => {
+ const { width, height } = attributes.style;
+ return [children[0].attributes.xfaName, [width, height]];
+ })
+ );
+
+ expect(sizes.size).toEqual(Object.keys(draws).length);
+ expect(sizes.get("b")).not.toEqual(sizes.get("regular"));
+ expect(sizes.get("i")).not.toEqual(sizes.get("regular"));
+ for (const [element, span] of [
+ ["b", "boldSpan"],
+ ["i", "italicSpan"],
+ ["bi", "boldItalicSpan"],
+ ["bInSpan", "boldSpanInSpan"],
+ ["bWithStyle", "spanWithStyle"],
+ ]) {
+ expect(sizes.get(element)).withContext(element).toEqual(sizes.get(span));
+ }
+ });
});
diff --git a/web/xfa_layer_builder.css b/web/xfa_layer_builder.css
index 73560fec34900..0832edb946c12 100644
--- a/web/xfa_layer_builder.css
+++ b/web/xfa_layer_builder.css
@@ -113,6 +113,14 @@
color: blue;
}
+.xfaLayer b {
+ font-weight: bold;
+}
+
+.xfaLayer i {
+ font-style: italic;
+}
+
.xfaRich li {
margin-left: 3em;
}