import type { Style } from "@react-pdf/types"; import type { ResolvedNodeStyle, ResolvedPageSize } from "@reactive-resume/resume/stylesheet"; type ResolvedPdfPageSize = ResolvedPageSize; export type ResolvedPdfNodePresentation = { style?: Style; size?: ResolvedPdfPageSize; break?: boolean; wrap?: boolean; fixed?: boolean; minPresenceAhead?: number; orphans?: number; widows?: number; }; export type ResolvedPdfFlowProps = Omit; export type ResolvedPdfTextProps = Omit; export const resolvedPdfFlowProps = ({ break: breakBefore, wrap, fixed, minPresenceAhead, }: ResolvedPdfNodePresentation): ResolvedPdfFlowProps => ({ ...(breakBefore === undefined ? {} : { break: breakBefore }), ...(wrap === undefined ? {} : { wrap }), ...(fixed === undefined ? {} : { fixed }), ...(minPresenceAhead === undefined ? {} : { minPresenceAhead }), }); export const resolvedPdfTextProps = ({ orphans, widows, ...presentation }: ResolvedPdfNodePresentation): ResolvedPdfTextProps => ({ ...resolvedPdfFlowProps(presentation), ...(orphans === undefined ? {} : { orphans }), ...(widows === undefined ? {} : { widows }), }); const toReactPdfProperty = (property: string) => { if (property === "-resume-shadow-color") return "shadowColor"; if (property === "-resume-shadow-width") return "shadowWidth"; return property.replace(/-([a-z])/g, (_match, letter: string) => letter.toUpperCase()); }; const styleDelta = (resolved: ResolvedNodeStyle, base: ResolvedNodeStyle["style"] | undefined): Style | undefined => { const specified = new Set(resolved.specifiedStyleProperties); const hostBase = new Set(resolved.hostBaseStyleProperties); const entries: [string, string | number | undefined][] = base === undefined ? Object.entries(resolved.style) : Object.entries(resolved.style).filter(([property]) => !hostBase.has(property) && specified.has(property)); for (const property of specified) { if (!hostBase.has(property) && !(property in resolved.style)) entries.push([property, undefined]); } if (entries.length === 0) return undefined; return Object.freeze( Object.fromEntries(entries.map(([property, value]) => [toReactPdfProperty(property), value])), ) as Style; }; export function adaptResolvedPdfNode( resolved: ResolvedNodeStyle, base?: ResolvedNodeStyle, ): ResolvedPdfNodePresentation { const style = styleDelta(resolved, base?.style); const { structural } = resolved; const breakBefore = structural.breakBefore === "page" ? true : base?.structural.breakBefore === "page" ? false : undefined; const wrap = structural.breakInside === "avoid" ? false : base?.structural.breakInside === "avoid" ? true : undefined; const presentation = { ...(style ? { style } : {}), ...(structural.pageSize === undefined ? {} : { size: structural.pageSize }), ...(breakBefore === undefined ? {} : { break: breakBefore }), ...(wrap === undefined ? {} : { wrap }), ...(structural.fixed === undefined ? {} : { fixed: structural.fixed }), ...(structural.minPresenceAhead === undefined ? {} : { minPresenceAhead: structural.minPresenceAhead }), ...(structural.orphans === undefined ? {} : { orphans: structural.orphans }), ...(structural.widows === undefined ? {} : { widows: structural.widows }), } satisfies ResolvedPdfNodePresentation; return Object.freeze(presentation); }