/** * 样式与坐标转换的共享工具(HTML / SVG 共用)。 * * 布局结果内部统一使用 mm;SVG/HTML 输出按需换算为 px。 * 样式统一转成 CSS 声明,保证各 Renderer 视觉一致(§15)。 */ import type { ResolvedStyle } from "@report/core" import { mmToPx } from "@report/layout" import type { RenderOptions } from "./types.js" /** 把 mm 值按目标单位换算为数值(SVG/HTML 里直接写数字)。 */ export function toUnit(value: number, options: RenderOptions): number { return options.unit === "px" ? mmToPx(value) : value } /** 单位后缀。 */ export function unitSuffix(options: RenderOptions): string { return options.unit === "px" ? "px" : "mm" } /** * 把已解析样式转成 CSS 声明字符串。 * * 字号在文档中是 pt,HTML/SVG 里也以 pt 表达,保证与布局测量一致。 * 边框只输出四边统一值;逐边覆盖的边框由 Renderer 按需处理。 */ export function styleToCss(style: ResolvedStyle): string { const parts: string[] = [] parts.push(`font-family:${style.fontFamily}`) parts.push(`font-size:${style.fontSize}pt`) parts.push(`font-weight:${style.fontWeight}`) parts.push(`font-style:${style.fontStyle}`) parts.push(`line-height:${style.lineHeight}`) parts.push(`color:${style.color}`) if (style.background && style.background !== "transparent") { parts.push(`background:${style.background}`) } parts.push(`text-align:${style.align}`) parts.push(`text-decoration:${style.textDecoration}`) if (style.border) { const width = style.border.width ?? 0.1 const color = style.border.color ?? "#000000" const lineStyle = style.border.style ?? "solid" parts.push(`border:${width}mm ${lineStyle} ${color}`) } return parts.join(";") } /** 把已解析样式转成 SVG 文本属性(fill / font-size 等)。 */ export function styleToSvgTextAttrs(style: ResolvedStyle): Record { const attrs: Record = { fill: style.color, "font-family": style.fontFamily, "font-size": `${style.fontSize}pt`, "font-weight": String(style.fontWeight), "font-style": style.fontStyle, } if (style.textDecoration === "underline") attrs["text-decoration"] = "underline" if (style.textDecoration === "line-through") attrs["text-decoration"] = "line-through" return attrs }