@docen/docx 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/dist/converters/docx.d.mts +220 -1
  2. package/dist/converters/docx.mjs +1352 -1
  3. package/dist/converters/html.d.mts +28 -1
  4. package/dist/converters/html.mjs +103 -1
  5. package/dist/converters/markdown.d.mts +12 -1
  6. package/dist/converters/markdown.mjs +17 -1
  7. package/dist/converters/patch.d.mts +52 -1
  8. package/dist/converters/patch.mjs +35 -1
  9. package/dist/converters/prepare.d.mts +62 -1
  10. package/dist/converters/prepare.mjs +121 -1
  11. package/dist/converters/styles.d.mts +91 -2
  12. package/dist/converters/styles.mjs +308 -2
  13. package/dist/core-B8ba_FNi.mjs +717 -0
  14. package/dist/core-DRaLI8nd.d.mts +203 -0
  15. package/dist/core.d.mts +1 -1
  16. package/dist/core.mjs +1 -1
  17. package/dist/document-Cws7XTYL.d.mts +28 -0
  18. package/dist/editor.d.mts +20 -1
  19. package/dist/editor.mjs +20 -1
  20. package/dist/extensions/blockquote.d.mts +1 -1
  21. package/dist/extensions/blockquote.mjs +30 -2
  22. package/dist/extensions/code-block.d.mts +1 -1
  23. package/dist/extensions/code-block.mjs +22 -1
  24. package/dist/extensions/column-break.d.mts +1 -1
  25. package/dist/extensions/column-break.mjs +1 -1
  26. package/dist/extensions/details.d.mts +1 -1
  27. package/dist/extensions/details.mjs +17 -2
  28. package/dist/extensions/document.d.mts +1 -1
  29. package/dist/extensions/document.mjs +45 -1
  30. package/dist/extensions/extensions.d.mts +4 -5
  31. package/dist/extensions/extensions.mjs +13 -14
  32. package/dist/extensions/formatting-marks.d.mts +7 -1
  33. package/dist/extensions/formatting-marks.mjs +1 -1
  34. package/dist/extensions/heading.mjs +187 -1
  35. package/dist/extensions/image.d.mts +1 -1
  36. package/dist/extensions/image.mjs +395 -1
  37. package/dist/extensions/index.d.mts +4 -3
  38. package/dist/extensions/index.mjs +4 -3
  39. package/dist/extensions/mention.d.mts +1 -1
  40. package/dist/extensions/mention.mjs +43 -2
  41. package/dist/extensions/ordered-list.d.mts +1 -1
  42. package/dist/extensions/ordered-list.mjs +40 -2
  43. package/dist/extensions/page-break.d.mts +1 -33
  44. package/dist/extensions/page-break.mjs +1 -1
  45. package/dist/extensions/paragraph.mjs +145 -1
  46. package/dist/extensions/passthrough.d.mts +1 -1
  47. package/dist/extensions/passthrough.mjs +1 -1
  48. package/dist/extensions/section-break.d.mts +19 -2
  49. package/dist/extensions/section-break.mjs +1 -1
  50. package/dist/extensions/strike.mjs +45 -1
  51. package/dist/extensions/table-cell.mjs +106 -1
  52. package/dist/extensions/table-header.mjs +106 -1
  53. package/dist/extensions/table-row.mjs +78 -1
  54. package/dist/extensions/table.mjs +154 -1
  55. package/dist/extensions/task-item.d.mts +1 -1
  56. package/dist/extensions/task-item.mjs +36 -2
  57. package/dist/extensions/text-style.mjs +145 -1
  58. package/dist/extensions/tiptap.d.mts +2 -2
  59. package/dist/extensions/tiptap.mjs +31 -2
  60. package/dist/extensions/toc-field.d.mts +2 -0
  61. package/dist/extensions/toc-field.mjs +2 -0
  62. package/dist/extensions/utils.d.mts +2 -62
  63. package/dist/extensions/utils.mjs +508 -2
  64. package/dist/extensions/wpg-group.d.mts +2 -0
  65. package/dist/extensions/wpg-group.mjs +2 -0
  66. package/dist/extensions/wps-shape.d.mts +2 -0
  67. package/dist/extensions/wps-shape.mjs +2 -0
  68. package/dist/index.d.mts +101 -29
  69. package/dist/index.mjs +13 -10
  70. package/dist/{tiptap-TErPjuNJ.d.mts → tiptap-pZsNPsvV.d.mts} +1 -3
  71. package/dist/utils-BJwDQts7.d.mts +137 -0
  72. package/package.json +39 -40
  73. package/dist/blockquote-Ky9xD_Q-.mjs +0 -31
  74. package/dist/code-block-BgNjhADy.mjs +0 -23
  75. package/dist/core-DEqBHRzE.mjs +0 -406
  76. package/dist/core-fBMCCj4D.d.mts +0 -97
  77. package/dist/details-Dh8luWb2.mjs +0 -18
  78. package/dist/dist-B6B3HnVZ.mjs +0 -1838
  79. package/dist/document-BH1y4qHM.d.mts +0 -6
  80. package/dist/document-CvTym-FM.mjs +0 -50
  81. package/dist/docx-C5wyjI-0.d.mts +0 -200
  82. package/dist/docx-CONyAedl.mjs +0 -1231
  83. package/dist/editor-Bf3CxQnY.mjs +0 -21
  84. package/dist/editor-DReY3Rb9.d.mts +0 -21
  85. package/dist/emoji-BZ1dJD7d.d.mts +0 -17
  86. package/dist/emoji-C2OsTWR7.mjs +0 -27
  87. package/dist/extensions/emoji.d.mts +0 -2
  88. package/dist/extensions/emoji.mjs +0 -2
  89. package/dist/extensions/image-group.d.mts +0 -6
  90. package/dist/extensions/image-group.mjs +0 -2
  91. package/dist/heading-DAubgJJN.mjs +0 -166
  92. package/dist/html-DCC_DSHv.d.mts +0 -18
  93. package/dist/html-DedDWPoO.mjs +0 -66
  94. package/dist/image-CuEIXoZl.mjs +0 -334
  95. package/dist/markdown-BzJmRsBs.mjs +0 -18
  96. package/dist/markdown-D0k0sRYS.d.mts +0 -13
  97. package/dist/mention-C-dTR6MK.mjs +0 -44
  98. package/dist/ordered-list-V-3xFQmV.mjs +0 -41
  99. package/dist/paragraph-BrWH-BOV.mjs +0 -147
  100. package/dist/patch-Cdp5WKJz.d.mts +0 -53
  101. package/dist/patch-IZ13tXTu.mjs +0 -36
  102. package/dist/prepare-Bdk8Cjkf.mjs +0 -122
  103. package/dist/prepare-CPAvcv6q.d.mts +0 -63
  104. package/dist/strike-hWxShOgb.mjs +0 -50
  105. package/dist/styles-QSX3RhxK.mjs +0 -285
  106. package/dist/styles-ugspkrxz.d.mts +0 -85
  107. package/dist/table-DrA12lxX.mjs +0 -159
  108. package/dist/table-cell-DmkfYwDc.mjs +0 -112
  109. package/dist/table-header-Bwr5GKWg.mjs +0 -112
  110. package/dist/table-row-vew1y7WH.mjs +0 -84
  111. package/dist/task-item-BnlXGrIG.mjs +0 -37
  112. package/dist/text-style-tMeBq5XQ.mjs +0 -151
  113. package/dist/tiptap-D7pl4I5U.mjs +0 -33
  114. package/dist/utils-BPT4tjcn.mjs +0 -333
@@ -1,2 +1,508 @@
1
- import { S as twipToCss, _ as shadingFromElement, a as characterSpacingFromCss, b as sizeToCss, c as indentFromElement, d as renderBorderCSS, f as renderParagraphStyles, g as shadingFromCss, h as resolveFontName, i as bordersFromElement, l as lineSpacingToCss, m as renderTableCellStyles, n as alignmentFromElement, o as characterSpacingToCss, p as renderRunStyles, r as alignmentToCss, s as cssToTwip, t as alignmentFromCss, u as normalizeColorToHex, v as shadingToCss, x as spacingFromElement, y as sizeFromCss } from "../utils-BPT4tjcn.mjs";
2
- export { alignmentFromCss, alignmentFromElement, alignmentToCss, bordersFromElement, characterSpacingFromCss, characterSpacingToCss, cssToTwip, indentFromElement, lineSpacingToCss, normalizeColorToHex, renderBorderCSS, renderParagraphStyles, renderRunStyles, renderTableCellStyles, resolveFontName, shadingFromCss, shadingFromElement, shadingToCss, sizeFromCss, sizeToCss, spacingFromElement, twipToCss };
1
+ import { sectionMarginDefaults, sectionPageSizeDefaults } from "@office-open/docx";
2
+ //#region src/extensions/utils.ts
3
+ /** Factory for a Tiptap attr that carries an office-open native value: never
4
+ * parsed from HTML nor rendered to it, defaulting to null (ProseMirror stores
5
+ * every declared attr). Shared by every extension carrying OOXML attrs
6
+ * (paragraph/heading/table/table-cell/…). */
7
+ const attrNative = () => ({
8
+ default: null,
9
+ parseHTML: () => null,
10
+ rendered: false
11
+ });
12
+ /** Common CSS named colors → hex */
13
+ const CSS_COLORS = {
14
+ black: "#000000",
15
+ white: "#FFFFFF",
16
+ red: "#FF0000",
17
+ green: "#008000",
18
+ blue: "#0000FF",
19
+ yellow: "#FFFF00",
20
+ cyan: "#00FFFF",
21
+ magenta: "#FF00FF",
22
+ gray: "#808080",
23
+ grey: "#808080",
24
+ orange: "#FFA500",
25
+ purple: "#800080",
26
+ pink: "#FFC0CB",
27
+ brown: "#A52A2A",
28
+ lime: "#00FF00",
29
+ navy: "#000080",
30
+ teal: "#008080",
31
+ silver: "#C0C0C0",
32
+ maroon: "#800000",
33
+ olive: "#808000",
34
+ aqua: "#00FFFF",
35
+ fuchsia: "#FF00FF",
36
+ indigo: "#4B0082",
37
+ violet: "#EE82EE",
38
+ coral: "#FF7F50",
39
+ gold: "#FFD700",
40
+ salmon: "#FA8072",
41
+ tomato: "#FF6347"
42
+ };
43
+ /** Normalize a CSS color value to hex (e.g., "red" → "#FF0000", "#ff0000" → "#FF0000").
44
+ * Accepts a string (CSS name/hex or bare OOXML hex), or an OOXML ColorOptions
45
+ * object ({ val, themeColor, themeTint, themeShade }) — the object form
46
+ * resolves to its val (the RGB fallback Word stores alongside themeColor) for
47
+ * CSS rendering. The themeColor/tint/shade are preserved verbatim in the attrs
48
+ * and round-trip back to the DOCX (see text-style/paragraph parseDocx), so
49
+ * theme semantics survive even though only val is rendered here. A pure theme
50
+ * reference with no val (rare — Word usually stores both) would need theme.xml
51
+ * to resolve and is left unset. */
52
+ function normalizeColorToHex(color) {
53
+ if (!color) return void 0;
54
+ if (typeof color === "object") {
55
+ const { val } = color;
56
+ return val ? normalizeColorToHex(val) : void 0;
57
+ }
58
+ if (typeof color !== "string") return void 0;
59
+ if (color === "auto") return void 0;
60
+ if (color.startsWith("#")) return color.length === 4 ? `#${color[1]}${color[1]}${color[2]}${color[2]}${color[3]}${color[3]}`.toUpperCase() : color.toUpperCase();
61
+ if (/^[0-9A-Fa-f]{6}$/.test(color)) return `#${color.toUpperCase()}`;
62
+ if (/^[0-9A-Fa-f]{3}$/.test(color)) return `#${color[0]}${color[0]}${color[1]}${color[1]}${color[2]}${color[2]}`.toUpperCase();
63
+ return CSS_COLORS[color.toLowerCase()] ?? void 0;
64
+ }
65
+ /** Resolve a font value (string or OOXML rFonts { ascii, eastAsia, hAnsi, cs }) to a CSS family name. */
66
+ function resolveFontName(font) {
67
+ if (!font) return null;
68
+ if (typeof font === "string") return font;
69
+ if (typeof font === "object") {
70
+ const f = font;
71
+ return f.ascii || f.hAnsi || f.eastAsia || null;
72
+ }
73
+ return null;
74
+ }
75
+ /** Resolve a font value to a CSS font-family list with an eastAsia fallback.
76
+ * Mirrors Word's per-Unicode-range font selection (MS-OE376): Basic Latin uses
77
+ * the ascii font, CJK Ideographs use eastAsia. CSS can't split a run by range,
78
+ * so list ascii first (it carries Basic Latin glyphs) then eastAsia — the
79
+ * browser falls back to eastAsia for CJK chars the ascii font lacks. Without
80
+ * this, CJK text renders in the ascii font (e.g. Times) instead of the
81
+ * document's CJK font (e.g. SimSun). Use this for font-family rendering;
82
+ * resolveFontName (single name) stays for UI/parse. */
83
+ function resolveFontFamilyCss(font) {
84
+ if (!font) return null;
85
+ if (typeof font === "string") return font;
86
+ if (typeof font === "object") {
87
+ const f = font;
88
+ const ascii = f.ascii || f.hAnsi;
89
+ const ea = f.eastAsia;
90
+ if (ascii && ea && ascii !== ea) return `"${ascii}","${ea}"`;
91
+ return ascii || ea || null;
92
+ }
93
+ return null;
94
+ }
95
+ /** CSS value (e.g., "18pt") → twip number. 1 pt = 20 twips, 1 px = 15 twips (96 DPI). */
96
+ function cssToTwip(value) {
97
+ if (!value) return void 0;
98
+ const match = value.match(/^([\d.]+)(pt|px|em|cm|in)?$/);
99
+ if (!match) return void 0;
100
+ const num = parseFloat(match[1]);
101
+ switch (match[2] ?? "pt") {
102
+ case "pt": return Math.round(num * 20);
103
+ case "px": return Math.round(num * 15);
104
+ case "in": return Math.round(num * 1440);
105
+ case "cm": return Math.round(num * 567);
106
+ case "em": return Math.round(num * 240);
107
+ default: return Math.round(num * 20);
108
+ }
109
+ }
110
+ /** Twip value → CSS string (e.g., 360 → "18pt"). */
111
+ function twipToCss(value) {
112
+ if (value == null) return null;
113
+ if (typeof value === "string") return value;
114
+ return `${value / 20}pt`;
115
+ }
116
+ const ALIGNMENT_TO_CSS = {
117
+ left: "left",
118
+ center: "center",
119
+ right: "right",
120
+ start: "start",
121
+ end: "end",
122
+ both: "justify",
123
+ distribute: "justify"
124
+ };
125
+ const CSS_TO_ALIGNMENT = {
126
+ left: "left",
127
+ center: "center",
128
+ right: "right",
129
+ start: "start",
130
+ end: "end",
131
+ justify: "both"
132
+ };
133
+ /** OOXML alignment → CSS text-align. */
134
+ function alignmentToCss(alignment) {
135
+ if (!alignment) return null;
136
+ return ALIGNMENT_TO_CSS[alignment] ?? null;
137
+ }
138
+ /** CSS text-align → OOXML alignment. */
139
+ function alignmentFromCss(css) {
140
+ if (!css) return null;
141
+ return CSS_TO_ALIGNMENT[css] ?? null;
142
+ }
143
+ /** Shading.fill → CSS background-color hex. */
144
+ function shadingToCss(shading) {
145
+ if (!shading?.fill) return null;
146
+ return normalizeColorToHex(shading.fill) ?? null;
147
+ }
148
+ /** CSS background-color → ShadingAttributesProperties (fill normalized to hex). */
149
+ function shadingFromCss(css) {
150
+ const hex = normalizeColorToHex(css ?? void 0);
151
+ return hex ? {
152
+ fill: hex,
153
+ type: "clear"
154
+ } : null;
155
+ }
156
+ function lineSpacingToCss(spacing, snapToGrid = null) {
157
+ if (!spacing?.line) return null;
158
+ const rule = spacing.lineRule;
159
+ if (rule === "exact" || rule === "exactly" || rule === "atLeast") return `${Number(spacing.line) / 20}pt`;
160
+ if (snapToGrid === false) return `calc(var(--docen-font-metric, 1.2) * ${Number((Number(spacing.line) / 240).toFixed(2))} * var(--docen-line-base, 1em))`;
161
+ return `calc(var(--docen-font-metric, 1.2) * var(--docen-line-base, 1em) + var(--docen-line-pitch, 0pt))`;
162
+ }
163
+ /** twips → mm (1 in = 1440 tw = 25.4 mm), 2dp string. */
164
+ function twipsToMm(twips) {
165
+ return `${(twips / 1440 * 25.4).toFixed(2)}mm`;
166
+ }
167
+ /** Resolve a section's printable page dimensions (twips), honoring orientation.
168
+ * A landscape section commonly stores portrait dims (w<h) with
169
+ * `orientation: "landscape"` — swap width/height so width is the larger edge.
170
+ * Falls back to the engine's default page size (@office-open/docx
171
+ * `sectionPageSizeDefaults` = A4) when the size is absent or non-numeric — the
172
+ * engine's `stringifySectionPropertiesXml` fills an empty sectPr the same way,
173
+ * so edit-time geometry matches render/measure/generate/export. */
174
+ function resolvePageSize(size) {
175
+ const fallback = {
176
+ width: sectionPageSizeDefaults.WIDTH,
177
+ height: sectionPageSizeDefaults.HEIGHT
178
+ };
179
+ if (!size || typeof size !== "object") return fallback;
180
+ const s = size;
181
+ const w = typeof s.width === "number" ? s.width : void 0;
182
+ const h = typeof s.height === "number" ? s.height : void 0;
183
+ if (w == null || h == null) return fallback;
184
+ return s.orientation === "landscape" && w < h ? {
185
+ width: h,
186
+ height: w
187
+ } : {
188
+ width: w,
189
+ height: h
190
+ };
191
+ }
192
+ /** Page margin (twips) → CSS padding. Sides absent or non-numeric fall back to
193
+ * the engine's default margins (@office-open/docx `sectionMarginDefaults`:
194
+ * top/bottom 1440tw, left/right 1800tw — MS Office zh-CN "Normal"), matching
195
+ * the page-size fallback above and the engine's empty-sectPr behavior. Margins
196
+ * are left as-is: office-open returns them already rotated for a landscape
197
+ * section. */
198
+ function sectionMarginCss(margin) {
199
+ const def = sectionMarginDefaults;
200
+ const m = margin && typeof margin === "object" ? margin : {};
201
+ const num = (v, d) => typeof v === "number" ? v : d;
202
+ return `padding:${[
203
+ num(m.top, def.TOP),
204
+ num(m.right, def.RIGHT),
205
+ num(m.bottom, def.BOTTOM),
206
+ num(m.left, def.LEFT)
207
+ ].map(twipsToMm).join(" ")}`;
208
+ }
209
+ /** Document grid linePitch (twips) → container CSS line-height.
210
+ * Per ECMA-376 snapToGrid, the grid linePitch is ADDED to each line (single-
211
+ * line paragraphs inherit it). So the container line-height (inherited by
212
+ * paragraphs without their own spacing) is the font's `normal` metric + the
213
+ * grid pitch, set only when the grid type enables line-snapping (not
214
+ * "default"). The --docen-line-pitch var carries the pitch so per-paragraph
215
+ * lineSpacingToCss can add the same pitch; --docen-font-metric is injected per
216
+ * paragraph by the editor; 1.2 is the fallback for static HTML export. */
217
+ function sectionLinePitchCss(grid) {
218
+ if (!grid || typeof grid !== "object") return [];
219
+ const g = grid;
220
+ if (!g.type || g.type === "default" || typeof g.linePitch !== "number" || !g.linePitch) return [];
221
+ const pitch = `${(g.linePitch / 20).toFixed(2)}pt`;
222
+ return [`line-height:calc(var(--docen-font-metric, 1.2) * 1em + ${pitch})`, `--docen-line-pitch:${pitch}`];
223
+ }
224
+ function sizeToCss(size) {
225
+ if (size == null) return null;
226
+ return `${size}pt`;
227
+ }
228
+ function sizeFromCss(css) {
229
+ if (!css) return null;
230
+ const m = css.match(/^([\d.]+)(pt|px)?$/);
231
+ if (!m) return null;
232
+ const num = parseFloat(m[1]);
233
+ return (m[2] ?? "pt") === "px" ? num * .75 : num;
234
+ }
235
+ function characterSpacingToCss(spacing) {
236
+ if (spacing == null) return null;
237
+ return `${spacing / 20}pt`;
238
+ }
239
+ function characterSpacingFromCss(css) {
240
+ if (!css) return null;
241
+ const m = css.match(/^(-?[\d.]+)pt$/);
242
+ return m ? Math.round(parseFloat(m[1]) * 20) : null;
243
+ }
244
+ /** Render a BorderOptions to CSS string. OOXML border.size is in eighths of a point. */
245
+ function renderBorderCSS(border) {
246
+ if (!border || !border.style || border.style === "none" || border.style === "nil") return null;
247
+ const size = border.size != null ? `${border.size / 8}pt` : "1pt";
248
+ const cssStyle = {
249
+ single: "solid",
250
+ dashed: "dashed",
251
+ dotted: "dotted",
252
+ double: "double",
253
+ dotDash: "dashed",
254
+ dotDotDash: "dotted",
255
+ dashSmallGap: "dashed"
256
+ }[border.style || "single"] || "solid";
257
+ const hex = border.color && border.color !== "auto" ? normalizeColorToHex(border.color) : null;
258
+ return hex ? `${cssStyle} ${size} ${hex}` : `${cssStyle} ${size}`;
259
+ }
260
+ const EMU_PER_PX = 9525;
261
+ const EMU_PER_PT = 12700;
262
+ /**
263
+ * Render a drawing's Floating anchor (wp:anchor) to CSS — shared by Image and
264
+ * WpgGroup so anchored drawings render consistently:
265
+ * - wrapNone (type 0): position:absolute at the EMU offset (text does not flow
266
+ * around it — "in front of text" / "behind text"); z-index lifts it above or
267
+ * below the text layer per behindDocument.
268
+ * - topAndBottom (3): float + clear:both.
269
+ * - square/tight/through (1/2/4): with an hPos.offset, float:right pulled to
270
+ * the offset via margin-right so text wraps beside it with no overlap;
271
+ * otherwise float on the wrap side (tight/through add shape-outside, src).
272
+ * Wrap margins (wp:distT/B/L/R) are EMU, rendered as pt.
273
+ */
274
+ function floatingToStyles(floating, src, width) {
275
+ const f = floating;
276
+ if (!f) return [];
277
+ const styles = [];
278
+ styles.push(`z-index:${f.behindDocument ? -1 : f.zIndex ?? 1}`);
279
+ const wrapType = f.wrap?.type ?? 0;
280
+ const hOff = f.horizontalPosition?.offset;
281
+ const vOff = f.verticalPosition?.offset;
282
+ if (wrapType === 0) {
283
+ styles.push("position:absolute");
284
+ if (hOff != null) styles.push(`left:${(hOff / EMU_PER_PX).toFixed(1)}px`);
285
+ if (vOff != null) styles.push(`top:${(vOff / EMU_PER_PX).toFixed(1)}px`);
286
+ } else if (wrapType !== 3 && hOff != null && width != null) {
287
+ const offPx = (hOff / EMU_PER_PX).toFixed(1);
288
+ styles.push("float:right", `margin-right:calc(100% - ${offPx}px - ${width}px)`);
289
+ if (vOff != null) styles.push(`margin-top:${(vOff / EMU_PER_PX).toFixed(1)}px`);
290
+ const m = f.margins;
291
+ if (m?.left) styles.push(`margin-left:${(m.left / EMU_PER_PT).toFixed(1)}pt`);
292
+ } else if (wrapType === 3) styles.push("float:left", "clear:both");
293
+ else {
294
+ const side = f.wrap?.side;
295
+ const floatSide = side === "right" || side === "outside" ? "right" : "left";
296
+ styles.push(`float:${floatSide}`);
297
+ if ((wrapType === 2 || wrapType === 4) && src) styles.push(`shape-outside:url(${src})`);
298
+ }
299
+ if (!(wrapType !== 0 && wrapType !== 3 && hOff != null && width != null) && wrapType !== 0) {
300
+ const m = f.margins;
301
+ if (m) {
302
+ if (m.top) styles.push(`margin-top:${(m.top / EMU_PER_PT).toFixed(1)}pt`);
303
+ if (m.bottom) styles.push(`margin-bottom:${(m.bottom / EMU_PER_PT).toFixed(1)}pt`);
304
+ if (m.left) styles.push(`margin-left:${(m.left / EMU_PER_PT).toFixed(1)}pt`);
305
+ if (m.right) styles.push(`margin-right:${(m.right / EMU_PER_PT).toFixed(1)}pt`);
306
+ }
307
+ }
308
+ return styles;
309
+ }
310
+ /**
311
+ * Where a wrapNone floating drawing's absolute top/left should resolve from.
312
+ *
313
+ * verticalPosition.relative (OOXML vRelativeFrom) defaults to "paragraph"
314
+ * (also "line"); "page"/"margin"/"column" scope to the page box. The drawing is
315
+ * position:absolute, so its offsetParent decides the origin: a
316
+ * paragraph-anchored drawing must sit inside a position:relative <p> (the editor
317
+ * CSS adds that via p:has([data-float-anchor])), otherwise top/left measure
318
+ * from the page top and the drawing floats over the heading/body instead of
319
+ * its own blank paragraph (verified on sample anchored drawing groups). Only
320
+ * relevant for wrapNone (type 0); the float-based wraps stay inline.
321
+ */
322
+ function floatAnchorScope(floating) {
323
+ const vRel = floating?.verticalPosition?.relative;
324
+ if (vRel === "page" || vRel === "margin" || vRel === "column") return "page";
325
+ return "paragraph";
326
+ }
327
+ /**
328
+ * Compute all paragraph-level CSS styles from nested attrs.
329
+ * Shared by Paragraph and Heading extensions for node-level renderHTML.
330
+ * Attrs store office-open native values; mappers here convert to CSS.
331
+ */
332
+ function renderParagraphStyles(attrs, opts) {
333
+ const a = attrs;
334
+ const styles = [];
335
+ const align = alignmentToCss(a.alignment);
336
+ if (align) styles.push(`text-align:${align}`);
337
+ if (a.indent) {
338
+ const left = twipToCss(a.indent.left);
339
+ if (left) styles.push(`margin-left:${left}`);
340
+ const right = twipToCss(a.indent.right);
341
+ if (right) styles.push(`margin-right:${right}`);
342
+ if (a.indent.firstLine != null) {
343
+ const fl = twipToCss(a.indent.firstLine);
344
+ if (fl) styles.push(`text-indent:${fl}`);
345
+ } else if (a.indent.hanging != null) {
346
+ const h = twipToCss(a.indent.hanging);
347
+ if (h) styles.push(`text-indent:-${h}`);
348
+ } else if (a.indent.firstLineChars != null) styles.push(`text-indent:${a.indent.firstLineChars / 100}em`);
349
+ }
350
+ const markLineHeight = a.run?.size != null ? sizeToCss(a.run.size) : null;
351
+ if (markLineHeight) styles.push(`line-height:${markLineHeight}`);
352
+ if (a.spacing) {
353
+ const before = twipToCss(a.spacing.before);
354
+ if (before) styles.push(`margin-top:${before}`);
355
+ const after = twipToCss(a.spacing.after);
356
+ if (after) styles.push(`margin-bottom:${after}`);
357
+ const lh = lineSpacingToCss(a.spacing, opts?.empty ? false : a.snapToGrid);
358
+ if (lh) styles.push(`line-height:${lh}`);
359
+ }
360
+ const bg = shadingToCss(a.shading);
361
+ if (bg) styles.push(`background-color:${bg}`, `color:contrast-color(${bg})`);
362
+ if (a.border) {
363
+ const sides = [
364
+ ["top", a.border.top],
365
+ ["bottom", a.border.bottom],
366
+ ["left", a.border.left],
367
+ ["right", a.border.right]
368
+ ];
369
+ for (const [side, b] of sides) {
370
+ const css = b ? renderBorderCSS(b) : null;
371
+ if (css) styles.push(`border-${side}:${css}`);
372
+ }
373
+ }
374
+ return styles;
375
+ }
376
+ /**
377
+ * Compute run-level CSS (font/size/color/weight/…) from office-open run attrs.
378
+ * Shared by text-style marks and the styles→CSS generator (stylesToCss).
379
+ */
380
+ function renderRunStyles(attrs) {
381
+ const a = attrs;
382
+ const styles = [];
383
+ if (a.bold) styles.push("font-weight:bold");
384
+ if (a.italic) styles.push("font-style:italic");
385
+ if (a.smallCaps) styles.push("font-variant:small-caps");
386
+ if (a.allCaps) styles.push("text-transform:uppercase");
387
+ const deco = [];
388
+ if (a.underline) deco.push("underline");
389
+ if (a.strike || a.doubleStrike) deco.push("line-through");
390
+ if (deco.length) styles.push(`text-decoration:${deco.join(" ")}`);
391
+ const font = resolveFontFamilyCss(a.font);
392
+ if (font) styles.push(`font-family:${font}`);
393
+ const size = sizeToCss(a.size);
394
+ if (size) styles.push(`font-size:${size}`);
395
+ const color = a.color === "auto" ? void 0 : normalizeColorToHex(a.color);
396
+ if (color) styles.push(`color:${color}`);
397
+ const spacing = characterSpacingToCss(a.characterSpacing);
398
+ if (spacing) styles.push(`letter-spacing:${spacing}`);
399
+ if (a.highlight) {
400
+ const hl = normalizeColorToHex(typeof a.highlight === "string" ? a.highlight : null);
401
+ if (hl) styles.push(`background-color:${hl}`, `color:contrast-color(${hl})`);
402
+ }
403
+ return styles;
404
+ }
405
+ /**
406
+ * Compute table cell CSS styles from nested attrs.
407
+ * Shared by TableCell and TableHeader extensions.
408
+ */
409
+ function renderTableCellStyles(attrs) {
410
+ const a = attrs;
411
+ const styles = [];
412
+ if (a.noWrap) styles.push("white-space:nowrap");
413
+ const bg = shadingToCss(a.shading);
414
+ if (bg) styles.push(`background-color:${bg}`, `color:contrast-color(${bg})`);
415
+ if (a.verticalAlign) styles.push(`vertical-align:${a.verticalAlign}`);
416
+ if (a.margins) {
417
+ const m = a.margins;
418
+ const side = (s) => s && typeof s.size === "number" ? twipToCss(s.size) ?? "0pt" : "0pt";
419
+ styles.push(`padding:${side(m.top)} ${side(m.right)} ${side(m.bottom)} ${side(m.left)}`);
420
+ }
421
+ if (a.borders) for (const side of [
422
+ "top",
423
+ "right",
424
+ "bottom",
425
+ "left"
426
+ ]) {
427
+ const b = a.borders[side];
428
+ if (!b) continue;
429
+ styles.push(`border-${side}:${renderBorderCSS(b) ?? "none"}`);
430
+ }
431
+ return styles;
432
+ }
433
+ /** Parse text-align → OOXML alignment. */
434
+ function alignmentFromElement(el) {
435
+ return alignmentFromCss(el.style.textAlign || null);
436
+ }
437
+ /** Parse margin-left/right + text-indent → OOXML indent (twips). */
438
+ function indentFromElement(el) {
439
+ const indent = {};
440
+ const left = cssToTwip(el.style.marginLeft);
441
+ if (left) indent.left = left;
442
+ const right = cssToTwip(el.style.marginRight);
443
+ if (right) indent.right = right;
444
+ const ti = el.style.textIndent;
445
+ if (ti) if (ti.startsWith("-")) {
446
+ const h = cssToTwip(ti.slice(1));
447
+ if (h) indent.hanging = h;
448
+ } else {
449
+ const f = cssToTwip(ti);
450
+ if (f) indent.firstLine = f;
451
+ }
452
+ return Object.keys(indent).length > 0 ? indent : null;
453
+ }
454
+ /** Parse margin-top/bottom + line-height → OOXML spacing (twips). */
455
+ function spacingFromElement(el) {
456
+ const spacing = {};
457
+ const before = cssToTwip(el.style.marginTop);
458
+ if (before) spacing.before = before;
459
+ const after = cssToTwip(el.style.marginBottom);
460
+ if (after) spacing.after = after;
461
+ const lh = el.style.lineHeight;
462
+ if (lh) {
463
+ const m = lh.match(/^([\d.]+)(pt|px)?$/);
464
+ if (m) {
465
+ const num = parseFloat(m[1]);
466
+ if (m[2]) {
467
+ spacing.line = Math.round(num * (m[2] === "px" ? 15 : 20));
468
+ spacing.lineRule = "exact";
469
+ } else {
470
+ spacing.line = Math.round(num * 240);
471
+ spacing.lineRule = "auto";
472
+ }
473
+ }
474
+ }
475
+ return Object.keys(spacing).length > 0 ? spacing : null;
476
+ }
477
+ /** Parse border-* → OOXML BordersOptions. */
478
+ function bordersFromElement(el) {
479
+ const borders = {};
480
+ const sides = [
481
+ ["top", el.style.borderTop],
482
+ ["bottom", el.style.borderBottom],
483
+ ["left", el.style.borderLeft],
484
+ ["right", el.style.borderRight]
485
+ ];
486
+ for (const [side, css] of sides) {
487
+ if (!css || css === "initial" || css === "none") continue;
488
+ const m = css.match(/^(none|solid|dashed|dotted|double)\s+([\d.]+pt)\s+(.+)$/);
489
+ if (!m) continue;
490
+ borders[side] = {
491
+ style: {
492
+ solid: "single",
493
+ dashed: "dashed",
494
+ dotted: "dotted",
495
+ double: "double"
496
+ }[m[1]] ?? "single",
497
+ size: Math.round(parseFloat(m[2]) * 8),
498
+ color: m[3]
499
+ };
500
+ }
501
+ return Object.keys(borders).length > 0 ? borders : null;
502
+ }
503
+ /** Parse background-color → OOXML shading. */
504
+ function shadingFromElement(el) {
505
+ return shadingFromCss(el.style.backgroundColor || null);
506
+ }
507
+ //#endregion
508
+ export { alignmentFromCss, alignmentFromElement, alignmentToCss, attrNative, bordersFromElement, characterSpacingFromCss, characterSpacingToCss, cssToTwip, floatAnchorScope, floatingToStyles, indentFromElement, lineSpacingToCss, normalizeColorToHex, renderBorderCSS, renderParagraphStyles, renderRunStyles, renderTableCellStyles, resolveFontFamilyCss, resolveFontName, resolvePageSize, sectionLinePitchCss, sectionMarginCss, shadingFromCss, shadingFromElement, shadingToCss, sizeFromCss, sizeToCss, spacingFromElement, twipToCss, twipsToMm };
@@ -0,0 +1,2 @@
1
+ import { b as renderWpsInterior, v as WpgGroup, x as renderWpsText, y as WpsData } from "../core-DRaLI8nd.mjs";
2
+ export { WpgGroup, WpsData, renderWpsInterior, renderWpsText };
@@ -0,0 +1,2 @@
1
+ import { d as WpgGroup, f as renderWpsInterior, p as renderWpsText } from "../core-B8ba_FNi.mjs";
2
+ export { WpgGroup, renderWpsInterior, renderWpsText };
@@ -0,0 +1,2 @@
1
+ import { _ as WpsShape } from "../core-DRaLI8nd.mjs";
2
+ export { WpsShape };
@@ -0,0 +1,2 @@
1
+ import { u as WpsShape } from "../core-B8ba_FNi.mjs";
2
+ export { WpsShape };