fix(pdf): give a section heading with an icon its custom text styles

With a section icon the heading renders as a row holding the icon and a
separate title text, and that text only took the heading's color from
Custom Styles. Font size, weight, style, letter spacing, line height,
alignment, decoration and text transform written for section-heading now
reach the title too, as they do without an icon.
This commit is contained in:
Amruth Pillai
2026-09-29 16:57:10 +02:00
parent c8aead4ff0
commit 677ff17c1f
2 changed files with 44 additions and 11 deletions
@@ -31,21 +31,24 @@ const renderHeading = async (css: string, hideSectionIcons = false) => {
const page = await document.getPage(1);
const operators = await page.getOperatorList();
let fill = "";
let size = 0;
const text: { value: string; fill: string }[] = [];
const sizes: { value: string; size: number }[] = [];
const colors: string[] = [];
for (const [index, fn] of operators.fnArray.entries()) {
const args = operators.argsArray[index];
if (fn === OPS.setFillRGBColor) fill = args[0];
if (fn === OPS.setFont) size = args[1];
if (fn === OPS.setFillRGBColor || fn === OPS.setStrokeRGBColor) colors.push(args[0]);
if (fn === OPS.showText)
text.push({
value: args[0]
.map((glyph: { unicode?: string } | number) => (typeof glyph === "number" ? "" : (glyph.unicode ?? "")))
.join(""),
fill,
});
if (fn === OPS.showText) {
const value = args[0]
.map((glyph: { unicode?: string } | number) => (typeof glyph === "number" ? "" : (glyph.unicode ?? "")))
.join("");
text.push({ value, fill });
sizes.push({ value, size });
}
}
return { text, colors };
return { text, colors, sizes };
} finally {
await loadingTask.destroy();
}
@@ -72,4 +75,8 @@ describe("Semantic section heading colors (#3348)", () => {
expect(text).toContainEqual({ value: "HEADING", fill: "#000000" });
expect(colors).toContain("#dc2626");
});
it.each([false, true])("gives heading text its text styles with hideSectionIcons=%s", async (hidden) => {
const { sizes } = await renderHeading("section-heading { font-size: 21pt; text-transform: lowercase; }", hidden);
expect(sizes).toContainEqual({ value: "heading", size: 21 });
});
});
+29 -3
View File
@@ -185,6 +185,34 @@ const defaultSectionHeadingContainerStyle = {
columnGap: 4,
} satisfies Style;
const sectionHeadingTextProperties = new Set<string>([
"color",
"direction",
"fontSize",
"fontStyle",
"fontWeight",
"letterSpacing",
"lineHeight",
"textAlign",
"textDecoration",
"textDecorationColor",
"textDecorationStyle",
"textIndent",
"textTransform",
]);
/**
* With an icon, the heading node is the icon row and its title is a separate text that carries the template's own
* heading font, so the heading's text styles have to be handed to that text or they never reach it.
*/
const sectionHeadingTextOf = (style: Style | undefined): Style | undefined =>
style &&
(Object.fromEntries(
Object.entries(style).filter(
([property, value]) => value !== undefined && sectionHeadingTextProperties.has(property),
),
) as Style);
export const getSectionHeadingTextStyle = (...styles: StyleInput[]): Style[] => {
const textStyles = composeStyles(...styles).map(
({
@@ -380,9 +408,7 @@ const SectionShell = ({ sectionId, title, showHeading = true, children }: Sectio
bindSemanticNode={false}
style={getSectionHeadingTextStyle(
sectionHeadingStyle,
sectionHeadingResolved.style?.color === undefined
? undefined
: { color: sectionHeadingResolved.style.color },
sectionHeadingTextOf(sectionHeadingResolved.style),
)}
>
{sectionTitle}