dom-docx 0.1.0

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 (132) hide show
  1. package/API.md +533 -0
  2. package/LICENSE +21 -0
  3. package/README.md +236 -0
  4. package/dist/browser.d.ts +34 -0
  5. package/dist/browser.d.ts.map +1 -0
  6. package/dist/browser.js +35 -0
  7. package/dist/browser.js.map +1 -0
  8. package/dist/cli.d.ts +3 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +118 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/converter/bordered-block.d.ts +54 -0
  13. package/dist/converter/bordered-block.d.ts.map +1 -0
  14. package/dist/converter/bordered-block.js +124 -0
  15. package/dist/converter/bordered-block.js.map +1 -0
  16. package/dist/converter/build-docx.d.ts +46 -0
  17. package/dist/converter/build-docx.d.ts.map +1 -0
  18. package/dist/converter/build-docx.js +161 -0
  19. package/dist/converter/build-docx.js.map +1 -0
  20. package/dist/converter/computed-style-snapshot.browser.js +73 -0
  21. package/dist/converter/computed-style-snapshot.d.ts +10 -0
  22. package/dist/converter/computed-style-snapshot.d.ts.map +1 -0
  23. package/dist/converter/computed-style-snapshot.js +78 -0
  24. package/dist/converter/computed-style-snapshot.js.map +1 -0
  25. package/dist/converter/constants.d.ts +51 -0
  26. package/dist/converter/constants.d.ts.map +1 -0
  27. package/dist/converter/constants.js +163 -0
  28. package/dist/converter/constants.js.map +1 -0
  29. package/dist/converter/css.d.ts +112 -0
  30. package/dist/converter/css.d.ts.map +1 -0
  31. package/dist/converter/css.js +621 -0
  32. package/dist/converter/css.js.map +1 -0
  33. package/dist/converter/flex.d.ts +59 -0
  34. package/dist/converter/flex.d.ts.map +1 -0
  35. package/dist/converter/flex.js +252 -0
  36. package/dist/converter/flex.js.map +1 -0
  37. package/dist/converter/image.d.ts +38 -0
  38. package/dist/converter/image.d.ts.map +1 -0
  39. package/dist/converter/image.js +159 -0
  40. package/dist/converter/image.js.map +1 -0
  41. package/dist/converter/inline.d.ts +18 -0
  42. package/dist/converter/inline.d.ts.map +1 -0
  43. package/dist/converter/inline.js +213 -0
  44. package/dist/converter/inline.js.map +1 -0
  45. package/dist/converter/ooxml-patch.d.ts +23 -0
  46. package/dist/converter/ooxml-patch.d.ts.map +1 -0
  47. package/dist/converter/ooxml-patch.js +54 -0
  48. package/dist/converter/ooxml-patch.js.map +1 -0
  49. package/dist/converter/style-path.d.ts +4 -0
  50. package/dist/converter/style-path.d.ts.map +1 -0
  51. package/dist/converter/style-path.js +17 -0
  52. package/dist/converter/style-path.js.map +1 -0
  53. package/dist/converter/style-resolver-node.d.ts +7 -0
  54. package/dist/converter/style-resolver-node.d.ts.map +1 -0
  55. package/dist/converter/style-resolver-node.js +26 -0
  56. package/dist/converter/style-resolver-node.js.map +1 -0
  57. package/dist/converter/style-resolver.d.ts +24 -0
  58. package/dist/converter/style-resolver.d.ts.map +1 -0
  59. package/dist/converter/style-resolver.js +122 -0
  60. package/dist/converter/style-resolver.js.map +1 -0
  61. package/dist/converter/svg.d.ts +11 -0
  62. package/dist/converter/svg.d.ts.map +1 -0
  63. package/dist/converter/svg.js +116 -0
  64. package/dist/converter/svg.js.map +1 -0
  65. package/dist/converter/table.d.ts +8 -0
  66. package/dist/converter/table.d.ts.map +1 -0
  67. package/dist/converter/table.js +745 -0
  68. package/dist/converter/table.js.map +1 -0
  69. package/dist/converter/text-metrics.d.ts +17 -0
  70. package/dist/converter/text-metrics.d.ts.map +1 -0
  71. package/dist/converter/text-metrics.js +51 -0
  72. package/dist/converter/text-metrics.js.map +1 -0
  73. package/dist/converter/types.d.ts +82 -0
  74. package/dist/converter/types.d.ts.map +1 -0
  75. package/dist/converter/types.js +9 -0
  76. package/dist/converter/types.js.map +1 -0
  77. package/dist/converter/visitor.d.ts +11 -0
  78. package/dist/converter/visitor.d.ts.map +1 -0
  79. package/dist/converter/visitor.js +910 -0
  80. package/dist/converter/visitor.js.map +1 -0
  81. package/dist/converter.d.ts +28 -0
  82. package/dist/converter.d.ts.map +1 -0
  83. package/dist/converter.js +44 -0
  84. package/dist/converter.js.map +1 -0
  85. package/dist/html-wrap.d.ts +3 -0
  86. package/dist/html-wrap.d.ts.map +1 -0
  87. package/dist/html-wrap.js +26 -0
  88. package/dist/html-wrap.js.map +1 -0
  89. package/dist/index.d.ts +17 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +16 -0
  92. package/dist/index.js.map +1 -0
  93. package/examples/README.md +39 -0
  94. package/examples/balance-sheet/compare_side_by_side.png +0 -0
  95. package/examples/balance-sheet/input.html +41 -0
  96. package/examples/balance-sheet/output.docx +0 -0
  97. package/examples/balance-sheet/preview.png +0 -0
  98. package/examples/invoice/compare_side_by_side.png +0 -0
  99. package/examples/invoice/input.html +88 -0
  100. package/examples/invoice/logo.png +0 -0
  101. package/examples/invoice/output.docx +0 -0
  102. package/examples/invoice/preview.png +0 -0
  103. package/examples/javascript-essay/compare_side_by_side.png +0 -0
  104. package/examples/javascript-essay/input.html +39 -0
  105. package/examples/javascript-essay/output.docx +0 -0
  106. package/examples/javascript-essay/preview.png +0 -0
  107. package/examples/product-launch-brief/compare_side_by_side.png +0 -0
  108. package/examples/product-launch-brief/input.html +120 -0
  109. package/examples/product-launch-brief/output.docx +0 -0
  110. package/examples/product-launch-brief/preview.png +0 -0
  111. package/examples/quarterly-financials/compare_side_by_side.png +0 -0
  112. package/examples/quarterly-financials/input.html +27 -0
  113. package/examples/quarterly-financials/output.docx +0 -0
  114. package/examples/quarterly-financials/preview.png +0 -0
  115. package/examples/react-dashboard/compare_side_by_side.png +0 -0
  116. package/examples/react-dashboard/input.html +1 -0
  117. package/examples/react-dashboard/output.docx +0 -0
  118. package/examples/react-dashboard/preview.html +107 -0
  119. package/examples/react-dashboard/preview.png +0 -0
  120. package/examples/regional-sales-dashboard/compare_side_by_side.png +0 -0
  121. package/examples/regional-sales-dashboard/input.html +129 -0
  122. package/examples/regional-sales-dashboard/output.docx +0 -0
  123. package/examples/regional-sales-dashboard/preview.png +0 -0
  124. package/examples/sales-contract/compare_side_by_side.png +0 -0
  125. package/examples/sales-contract/input.html +68 -0
  126. package/examples/sales-contract/output.docx +0 -0
  127. package/examples/sales-contract/preview.png +0 -0
  128. package/examples/sprint-retrospective/compare_side_by_side.png +0 -0
  129. package/examples/sprint-retrospective/input.html +51 -0
  130. package/examples/sprint-retrospective/output.docx +0 -0
  131. package/examples/sprint-retrospective/preview.png +0 -0
  132. package/package.json +108 -0
@@ -0,0 +1,46 @@
1
+ import { type ImageResolver } from "./image.js";
2
+ import { type StyleResolver } from "./style-resolver.js";
3
+ /** Page/font/metadata options (Tier 1 `ConvertOptions`). All lengths in inches / points. */
4
+ export interface DocumentConfig {
5
+ /** `"letter"` (default), `"a4"`, or a custom size in inches. */
6
+ pageSize?: "letter" | "a4" | {
7
+ width: number;
8
+ height: number;
9
+ };
10
+ orientation?: "portrait" | "landscape";
11
+ /** Page margins in inches (each side defaults to 1). */
12
+ margins?: {
13
+ top?: number;
14
+ right?: number;
15
+ bottom?: number;
16
+ left?: number;
17
+ };
18
+ /** Default body font family and size (points). */
19
+ defaultFont?: {
20
+ family?: string;
21
+ sizePt?: number;
22
+ };
23
+ /** Core document properties written to `docProps/core.xml`. */
24
+ metadata?: {
25
+ title?: string;
26
+ subject?: string;
27
+ creator?: string;
28
+ keywords?: string[];
29
+ description?: string;
30
+ };
31
+ /** HTML fragment rendered as the page header (its own inline-styled fragment). */
32
+ headerHtml?: string;
33
+ /** HTML fragment rendered as the page footer. */
34
+ footerHtml?: string;
35
+ /** Append a centered `Page N` field to the footer (created if `footerHtml` is absent). */
36
+ pageNumber?: boolean;
37
+ /** Document language (spell-check locale), e.g. `"en-US"`, `"ar-SA"`. */
38
+ lang?: string;
39
+ /** Text direction; `"rtl"` sets right-to-left paragraphs. */
40
+ direction?: "ltr" | "rtl";
41
+ }
42
+ /** Platform-neutral DOCX bytes from an HTML body fragment and style resolver. */
43
+ export declare function buildDocxUint8Array(html: string, styleResolver: StyleResolver, imageResolver?: ImageResolver, documentConfig?: DocumentConfig): Promise<Uint8Array>;
44
+ /** Browser entry — returns a `.docx` Blob. */
45
+ export declare function buildDocxBlob(html: string, styleResolver: StyleResolver, imageResolver?: ImageResolver, documentConfig?: DocumentConfig): Promise<Blob>;
46
+ //# sourceMappingURL=build-docx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-docx.d.ts","sourceRoot":"","sources":["../../src/converter/build-docx.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGhF,4FAA4F;AAC5F,MAAM,WAAW,cAAc;IAC7B,gEAAgE;IAChE,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/D,WAAW,CAAC,EAAE,UAAU,GAAG,WAAW,CAAC;IACvC,wDAAwD;IACxD,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,kDAAkD;IAClD,WAAW,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,+DAA+D;IAC/D,QAAQ,CAAC,EAAE;QACT,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;CAC3B;AAkKD,iFAAiF;AACjF,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,aAAa,EAC5B,aAAa,CAAC,EAAE,aAAa,EAC7B,cAAc,CAAC,EAAE,cAAc,GAC9B,OAAO,CAAC,UAAU,CAAC,CAWrB;AAED,8CAA8C;AAC9C,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,aAAa,EAC5B,aAAa,CAAC,EAAE,aAAa,EAC7B,cAAc,CAAC,EAAE,cAAc,GAC9B,OAAO,CAAC,IAAI,CAAC,CAKf"}
@@ -0,0 +1,161 @@
1
+ import { AlignmentType, Document, Footer, Header, PageNumber, Packer, PageOrientation, Paragraph, TextRun, convertInchesToTwip, } from "docx";
2
+ import * as cheerio from "cheerio";
3
+ import { unzipSync, zipSync } from "fflate";
4
+ import { BODY_FONT, BODY_FONT_HALF_POINTS, NUMBERING_CONFIG, PAGE_MARGIN_TWIPS } from "./constants.js";
5
+ import { patchDocumentXml, patchNumberingXml } from "./ooxml-patch.js";
6
+ import { applyImageResolver } from "./image.js";
7
+ import { INLINE_STYLE_RESOLVER } from "./style-resolver.js";
8
+ import { htmlToDocxBlocks } from "./visitor.js";
9
+ // Portrait dimensions in twips. Letter matches convertInchesToTwip(8.5)×(11).
10
+ const PAGE_PRESETS_TWIPS = {
11
+ letter: { width: 12240, height: 15840 },
12
+ a4: { width: 11906, height: 16838 },
13
+ };
14
+ function resolveDocumentConfig(config) {
15
+ const ps = config?.pageSize;
16
+ const base = !ps || ps === "letter"
17
+ ? PAGE_PRESETS_TWIPS.letter
18
+ : ps === "a4"
19
+ ? PAGE_PRESETS_TWIPS.a4
20
+ : { width: convertInchesToTwip(ps.width), height: convertInchesToTwip(ps.height) };
21
+ // docx swaps width/height itself for landscape, so pass portrait dims + the flag.
22
+ const size = config?.orientation === "landscape"
23
+ ? { width: base.width, height: base.height, orientation: PageOrientation.LANDSCAPE }
24
+ : { width: base.width, height: base.height };
25
+ const m = config?.margins;
26
+ const marginIn = (v) => v !== undefined ? convertInchesToTwip(v) : PAGE_MARGIN_TWIPS;
27
+ const meta = config?.metadata;
28
+ const metadata = {};
29
+ if (meta?.title)
30
+ metadata.title = meta.title;
31
+ if (meta?.subject)
32
+ metadata.subject = meta.subject;
33
+ if (meta?.creator)
34
+ metadata.creator = meta.creator;
35
+ if (meta?.keywords?.length)
36
+ metadata.keywords = meta.keywords.join(", ");
37
+ if (meta?.description)
38
+ metadata.description = meta.description;
39
+ return {
40
+ size,
41
+ margin: { top: marginIn(m?.top), right: marginIn(m?.right), bottom: marginIn(m?.bottom), left: marginIn(m?.left) },
42
+ font: config?.defaultFont?.family ?? BODY_FONT,
43
+ fontHalfPoints: config?.defaultFont?.sizePt !== undefined
44
+ ? Math.round(config.defaultFont.sizePt * 2)
45
+ : BODY_FONT_HALF_POINTS,
46
+ metadata,
47
+ lang: config?.lang,
48
+ rtl: config?.direction === "rtl",
49
+ };
50
+ }
51
+ /** Convert a standalone HTML fragment (header/footer) to DOCX blocks via the inline resolver. */
52
+ function fragmentToBlocks(html, sizeHalfPoints) {
53
+ const $ = cheerio.load(`<body>${html.trim()}</body>`, { xml: false });
54
+ return htmlToDocxBlocks($, INLINE_STYLE_RESOLVER, sizeHalfPoints);
55
+ }
56
+ function pageNumberParagraph() {
57
+ return new Paragraph({
58
+ alignment: AlignmentType.CENTER,
59
+ children: [new TextRun("Page "), new TextRun({ children: [PageNumber.CURRENT] })],
60
+ });
61
+ }
62
+ function buildFooter(config, resolved) {
63
+ const hasFooterHtml = Boolean(config?.footerHtml);
64
+ if (!hasFooterHtml && !config?.pageNumber)
65
+ return undefined;
66
+ const children = hasFooterHtml
67
+ ? fragmentToBlocks(config.footerHtml, resolved.fontHalfPoints)
68
+ : [];
69
+ if (config?.pageNumber)
70
+ children.push(pageNumberParagraph());
71
+ return new Footer({ children });
72
+ }
73
+ function buildHeader(config, resolved) {
74
+ if (!config?.headerHtml)
75
+ return undefined;
76
+ return new Header({ children: fragmentToBlocks(config.headerHtml, resolved.fontHalfPoints) });
77
+ }
78
+ async function packDocxToUint8Array(children, resolved, chrome) {
79
+ const listStyleRun = { font: resolved.font, size: resolved.fontHalfPoints };
80
+ const doc = new Document({
81
+ ...resolved.metadata,
82
+ numbering: NUMBERING_CONFIG,
83
+ styles: {
84
+ default: {
85
+ document: {
86
+ run: {
87
+ font: resolved.font,
88
+ size: resolved.fontHalfPoints,
89
+ ...(resolved.lang ? { language: { value: resolved.lang } } : {}),
90
+ ...(resolved.rtl ? { rightToLeft: true } : {}),
91
+ },
92
+ },
93
+ },
94
+ paragraphStyles: [
95
+ {
96
+ id: "ListNumber",
97
+ name: "List Number",
98
+ basedOn: "Normal",
99
+ next: "Normal",
100
+ quickFormat: true,
101
+ run: listStyleRun,
102
+ },
103
+ {
104
+ id: "ListBullet",
105
+ name: "List Bullet",
106
+ basedOn: "Normal",
107
+ next: "Normal",
108
+ quickFormat: true,
109
+ run: listStyleRun,
110
+ },
111
+ ],
112
+ },
113
+ sections: [
114
+ {
115
+ properties: {
116
+ page: {
117
+ size: resolved.size,
118
+ margin: resolved.margin,
119
+ },
120
+ },
121
+ ...(chrome.header ? { headers: { default: chrome.header } } : {}),
122
+ ...(chrome.footer ? { footers: { default: chrome.footer } } : {}),
123
+ children,
124
+ },
125
+ ],
126
+ });
127
+ const blob = await Packer.toBlob(doc);
128
+ return new Uint8Array(await blob.arrayBuffer());
129
+ }
130
+ function patchPackedDocx(packed) {
131
+ const files = unzipSync(packed);
132
+ const documentXml = new TextDecoder().decode(files["word/document.xml"]);
133
+ files["word/document.xml"] = new TextEncoder().encode(patchDocumentXml(documentXml));
134
+ if (files["word/numbering.xml"]) {
135
+ const numberingXml = new TextDecoder().decode(files["word/numbering.xml"]);
136
+ files["word/numbering.xml"] = new TextEncoder().encode(patchNumberingXml(numberingXml));
137
+ }
138
+ return zipSync(files);
139
+ }
140
+ /** Platform-neutral DOCX bytes from an HTML body fragment and style resolver. */
141
+ export async function buildDocxUint8Array(html, styleResolver, imageResolver, documentConfig) {
142
+ const resolved = resolveDocumentConfig(documentConfig);
143
+ const $ = cheerio.load(`<body>${html.trim()}</body>`, { xml: false });
144
+ if (imageResolver)
145
+ await applyImageResolver($, imageResolver);
146
+ const children = htmlToDocxBlocks($, styleResolver, resolved.fontHalfPoints);
147
+ const chrome = {
148
+ header: buildHeader(documentConfig, resolved),
149
+ footer: buildFooter(documentConfig, resolved),
150
+ };
151
+ const packed = await packDocxToUint8Array(children, resolved, chrome);
152
+ return patchPackedDocx(packed);
153
+ }
154
+ /** Browser entry — returns a `.docx` Blob. */
155
+ export async function buildDocxBlob(html, styleResolver, imageResolver, documentConfig) {
156
+ const bytes = await buildDocxUint8Array(html, styleResolver, imageResolver, documentConfig);
157
+ return new Blob([bytes.slice()], {
158
+ type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
159
+ });
160
+ }
161
+ //# sourceMappingURL=build-docx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-docx.js","sourceRoot":"","sources":["../../src/converter/build-docx.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ () => {
2
+ const elementStylePath = (el) => {
3
+ const parts = [];
4
+ let current = el;
5
+ while (current && current.tagName !== "BODY") {
6
+ const tagName = current.tagName.toLowerCase();
7
+ const parent = current.parentElement;
8
+ if (!parent) break;
9
+ const siblings = Array.from(parent.children).filter(
10
+ (c) => c.tagName.toLowerCase() === tagName,
11
+ );
12
+ const index = siblings.indexOf(current);
13
+ parts.unshift(tagName + "[" + index + "]");
14
+ current = parent;
15
+ }
16
+ return parts.join("/");
17
+ };
18
+
19
+ const props = [
20
+ "color",
21
+ "backgroundColor",
22
+ "display",
23
+ "flexDirection",
24
+ "gap",
25
+ "columnGap",
26
+ "rowGap",
27
+ "textAlign",
28
+ "fontSize",
29
+ "fontWeight",
30
+ "fontStyle",
31
+ "marginTop",
32
+ "marginRight",
33
+ "marginBottom",
34
+ "marginLeft",
35
+ "paddingTop",
36
+ "paddingRight",
37
+ "paddingBottom",
38
+ "paddingLeft",
39
+ "height",
40
+ "width",
41
+ "maxWidth",
42
+ "borderTopWidth",
43
+ "borderTopColor",
44
+ "borderRightWidth",
45
+ "borderRightColor",
46
+ "borderBottomWidth",
47
+ "borderBottomColor",
48
+ "borderLeftWidth",
49
+ "borderLeftColor",
50
+ ];
51
+
52
+ const results = [];
53
+ const walk = (el) => {
54
+ const cs = getComputedStyle(el);
55
+ const styles = {};
56
+ for (let i = 0; i < props.length; i++) {
57
+ const prop = props[i];
58
+ styles[prop] = cs[prop];
59
+ }
60
+ results.push({ path: elementStylePath(el), styles: styles });
61
+ const children = el.children;
62
+ for (let i = 0; i < children.length; i++) {
63
+ const child = children[i];
64
+ if (child instanceof Element) walk(child);
65
+ }
66
+ };
67
+ const bodyChildren = document.body.children;
68
+ for (let i = 0; i < bodyChildren.length; i++) {
69
+ const child = bodyChildren[i];
70
+ if (child instanceof Element) walk(child);
71
+ }
72
+ return results;
73
+ }
@@ -0,0 +1,10 @@
1
+ export interface ComputedStyleSnapshot {
2
+ path: string;
3
+ styles: Record<string, string>;
4
+ }
5
+ /**
6
+ * Batch-read `getComputedStyle` for every element under `document.body` children.
7
+ * Browser-native primitive — no Playwright, no server round-trip.
8
+ */
9
+ export declare function snapshotComputedStylesFromDocument(doc?: Document): ComputedStyleSnapshot[];
10
+ //# sourceMappingURL=computed-style-snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computed-style-snapshot.d.ts","sourceRoot":"","sources":["../../src/converter/computed-style-snapshot.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAQD;;;GAGG;AACH,wBAAgB,kCAAkC,CAChD,GAAG,GAAE,QAAmB,GACvB,qBAAqB,EAAE,CAsEzB"}
@@ -0,0 +1,78 @@
1
+ const ELEMENT_NODE = 1;
2
+ function isElement(node) {
3
+ return node.nodeType === ELEMENT_NODE;
4
+ }
5
+ /**
6
+ * Batch-read `getComputedStyle` for every element under `document.body` children.
7
+ * Browser-native primitive — no Playwright, no server round-trip.
8
+ */
9
+ export function snapshotComputedStylesFromDocument(doc = document) {
10
+ const elementStylePath = (el) => {
11
+ const parts = [];
12
+ let current = el;
13
+ while (current && current.tagName !== "BODY") {
14
+ const tagName = current.tagName.toLowerCase();
15
+ const parent = current.parentElement;
16
+ if (!parent)
17
+ break;
18
+ const siblings = Array.from(parent.children).filter((c) => c.tagName.toLowerCase() === tagName);
19
+ const index = siblings.indexOf(current);
20
+ parts.unshift(`${tagName}[${index}]`);
21
+ current = parent;
22
+ }
23
+ return parts.join("/");
24
+ };
25
+ const props = [
26
+ "color",
27
+ "backgroundColor",
28
+ "display",
29
+ "flexDirection",
30
+ "gap",
31
+ "columnGap",
32
+ "rowGap",
33
+ "textAlign",
34
+ "fontSize",
35
+ "fontWeight",
36
+ "fontStyle",
37
+ "marginTop",
38
+ "marginRight",
39
+ "marginBottom",
40
+ "marginLeft",
41
+ "paddingTop",
42
+ "paddingRight",
43
+ "paddingBottom",
44
+ "paddingLeft",
45
+ "height",
46
+ "width",
47
+ "maxWidth",
48
+ "borderTopWidth",
49
+ "borderTopColor",
50
+ "borderRightWidth",
51
+ "borderRightColor",
52
+ "borderBottomWidth",
53
+ "borderBottomColor",
54
+ "borderLeftWidth",
55
+ "borderLeftColor",
56
+ ];
57
+ const results = [];
58
+ const walk = (el) => {
59
+ const cs = doc.defaultView?.getComputedStyle(el);
60
+ if (!cs)
61
+ return;
62
+ const styles = {};
63
+ for (const prop of props) {
64
+ styles[prop] = cs[prop];
65
+ }
66
+ results.push({ path: elementStylePath(el), styles });
67
+ for (const child of el.children) {
68
+ if (isElement(child))
69
+ walk(child);
70
+ }
71
+ };
72
+ for (const child of doc.body.children) {
73
+ if (isElement(child))
74
+ walk(child);
75
+ }
76
+ return results;
77
+ }
78
+ //# sourceMappingURL=computed-style-snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computed-style-snapshot.js","sourceRoot":"","sources":["../../src/converter/computed-style-snapshot.ts"],"names":[],"mappings":"AAKA,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,SAAS,SAAS,CAAC,IAAU;IAC3B,OAAO,IAAI,CAAC,QAAQ,KAAK,YAAY,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kCAAkC,CAChD,MAAgB,QAAQ;IAExB,MAAM,gBAAgB,GAAG,CAAC,EAAW,EAAU,EAAE;QAC/C,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,OAAO,GAAmB,EAAE,CAAC;QACjC,OAAO,OAAO,IAAI,OAAO,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;YAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YAC9C,MAAM,MAAM,GAAmB,OAAO,CAAC,aAAa,CAAC;YACrD,IAAI,CAAC,MAAM;gBAAE,MAAM;YACnB,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CACjD,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,OAAO,CACpD,CAAC;YACF,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACxC,KAAK,CAAC,OAAO,CAAC,GAAG,OAAO,IAAI,KAAK,GAAG,CAAC,CAAC;YACtC,OAAO,GAAG,MAAM,CAAC;QACnB,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG;QACZ,OAAO;QACP,iBAAiB;QACjB,SAAS;QACT,eAAe;QACf,KAAK;QACL,WAAW;QACX,QAAQ;QACR,WAAW;QACX,UAAU;QACV,YAAY;QACZ,WAAW;QACX,WAAW;QACX,aAAa;QACb,cAAc;QACd,YAAY;QACZ,YAAY;QACZ,cAAc;QACd,eAAe;QACf,aAAa;QACb,QAAQ;QACR,OAAO;QACP,UAAU;QACV,gBAAgB;QAChB,gBAAgB;QAChB,kBAAkB;QAClB,kBAAkB;QAClB,mBAAmB;QACnB,mBAAmB;QACnB,iBAAiB;QACjB,iBAAiB;KACT,CAAC;IAEX,MAAM,OAAO,GAA4B,EAAE,CAAC;IAE5C,MAAM,IAAI,GAAG,CAAC,EAAW,EAAQ,EAAE;QACjC,MAAM,EAAE,GAAG,GAAG,CAAC,WAAW,EAAE,gBAAgB,CAAC,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,MAAM,MAAM,GAA2B,EAAE,CAAC;QAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAiC,CAAW,CAAC;QACjE,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACrD,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC;YAChC,IAAI,SAAS,CAAC,KAAK,CAAC;gBAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtC,IAAI,SAAS,CAAC,KAAK,CAAC;YAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,51 @@
1
+ import { type INumberingOptions } from "docx";
2
+ /** Match validator viewport: 1 inch page margins (1440 dxa). */
3
+ export declare const PAGE_MARGIN_TWIPS: number;
4
+ /** 1 inch at 96 DPI — pairs with PAGE_MARGIN_TWIPS in the HTML harness. */
5
+ export declare const PAGE_MARGIN_PX = 96;
6
+ /** Letter page at 96 DPI (8.5" × 11") — matches Playwright viewport and docx page size. */
7
+ export declare const VIEWPORT_WIDTH_PX = 816;
8
+ export declare const VIEWPORT_HEIGHT_PX = 1056;
9
+ /** Printable content width: 12240 page width − 2880 total margins = 9360 dxa (6.5"). */
10
+ export declare const PRINTABLE_CONTENT_WIDTH_TWIPS = 9360;
11
+ /** Validator body font is 14px → 10.5pt → 21 half-points. */
12
+ export declare const BODY_FONT_HALF_POINTS = 21;
13
+ /** Chromium default unvisited link color (`:link` in HTML harness). */
14
+ export declare const HYPERLINK_COLOR = "0000EE";
15
+ /** Matches the HTML harness body font (validator.ts wrapHtml). */
16
+ export declare const BODY_FONT = "Arial";
17
+ /** Validator line-height: 1.4 → 14px × 1.4 = 19.6px line box. */
18
+ export declare const BODY_LINE_HEIGHT = 336;
19
+ /** Browser default `<p>` margin is 1em at the harness body font size (14px). */
20
+ export declare const DEFAULT_PARAGRAPH_MARGIN_PX = 14;
21
+ /** Blockquote vertical margin (1em at 14px body font in the HTML harness). */
22
+ export declare const BLOCKQUOTE_MARGIN_PX = 14;
23
+ /** Blockquote horizontal indent per nesting level (HTML padding-left: 12px). */
24
+ export declare const BLOCKQUOTE_INDENT_PX = 12;
25
+ /** Chromium UA default blockquote side margin (margin: 1em 40px). */
26
+ export declare const BLOCKQUOTE_UA_SIDE_MARGIN_PX = 40;
27
+ /** Chromium default `<ol>`/`<ul>`: padding-left 40px, text start at 40px from list edge. */
28
+ export declare const LIST_LEVEL_LEFT_TWIPS = 600;
29
+ /** Hanging indent: marker column ~20px when text starts at 40px (14px font, decimal markers). */
30
+ export declare const LIST_HANGING_TWIPS = 300;
31
+ /** HTML harness line box height in px (matches line-height: 1.4 on 14px font). */
32
+ export declare const BODY_LINE_BOX_PX = 19.6;
33
+ /** EXACT `w:spacing/@w:line` in twips — CSS line box, not Word AUTO multiplier. */
34
+ export declare const BODY_LINE_EXACT_TWIPS: number;
35
+ export declare const HEADING_LEVELS: {
36
+ readonly h1: "Heading1";
37
+ readonly h2: "Heading2";
38
+ readonly h3: "Heading3";
39
+ readonly h4: "Heading4";
40
+ readonly h5: "Heading5";
41
+ readonly h6: "Heading6";
42
+ };
43
+ /** Font sizes in half-points — match Chromium UA defaults at 14px body (2em, 1.5em, …). */
44
+ export declare const HEADING_FONT_HALF_POINTS: Record<keyof typeof HEADING_LEVELS, number>;
45
+ /** Chromium UA heading vertical margins as a fraction of heading font size (inline path). */
46
+ export declare const HEADING_MARGIN_EM: Record<keyof typeof HEADING_LEVELS, number>;
47
+ export declare const BLOCK_TAGS: Set<string>;
48
+ /** Maps a CSS `list-style-type` to its numbering reference (see `LIST_STYLE_REFERENCES`). */
49
+ export declare const LIST_STYLE_REFERENCES: Record<string, string>;
50
+ export declare const NUMBERING_CONFIG: INumberingOptions;
51
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/converter/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,iBAAiB,EACvB,MAAM,MAAM,CAAC;AAEd,gEAAgE;AAChE,eAAO,MAAM,iBAAiB,QAAyB,CAAC;AAExD,2EAA2E;AAC3E,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,2FAA2F;AAC3F,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,wFAAwF;AACxF,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAElD,6DAA6D;AAC7D,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,uEAAuE;AACvE,eAAO,MAAM,eAAe,WAAW,CAAC;AAExC,kEAAkE;AAClE,eAAO,MAAM,SAAS,UAAU,CAAC;AAEjC,iEAAiE;AACjE,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,gFAAgF;AAChF,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,qEAAqE;AACrE,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAE/C,4FAA4F;AAC5F,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,iGAAiG;AACjG,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,mFAAmF;AACnF,eAAO,MAAM,qBAAqB,QAAoC,CAAC;AAEvE,eAAO,MAAM,cAAc;;;;;;;CAOjB,CAAC;AAEX,2FAA2F;AAC3F,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,OAAO,cAAc,EAAE,MAAM,CAOhF,CAAC;AAEF,6FAA6F;AAC7F,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,OAAO,cAAc,EAAE,MAAM,CAOzE,CAAC;AAEF,eAAO,MAAM,UAAU,aAuCrB,CAAC;AA2CH,6FAA6F;AAC7F,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAaxD,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,iBAgB9B,CAAC"}
@@ -0,0 +1,163 @@
1
+ import { AlignmentType, convertInchesToTwip, HeadingLevel, LevelFormat, LevelSuffix, } from "docx";
2
+ /** Match validator viewport: 1 inch page margins (1440 dxa). */
3
+ export const PAGE_MARGIN_TWIPS = convertInchesToTwip(1);
4
+ /** 1 inch at 96 DPI — pairs with PAGE_MARGIN_TWIPS in the HTML harness. */
5
+ export const PAGE_MARGIN_PX = 96;
6
+ /** Letter page at 96 DPI (8.5" × 11") — matches Playwright viewport and docx page size. */
7
+ export const VIEWPORT_WIDTH_PX = 816;
8
+ export const VIEWPORT_HEIGHT_PX = 1056;
9
+ /** Printable content width: 12240 page width − 2880 total margins = 9360 dxa (6.5"). */
10
+ export const PRINTABLE_CONTENT_WIDTH_TWIPS = 9360;
11
+ /** Validator body font is 14px → 10.5pt → 21 half-points. */
12
+ export const BODY_FONT_HALF_POINTS = 21;
13
+ /** Chromium default unvisited link color (`:link` in HTML harness). */
14
+ export const HYPERLINK_COLOR = "0000EE";
15
+ /** Matches the HTML harness body font (validator.ts wrapHtml). */
16
+ export const BODY_FONT = "Arial";
17
+ /** Validator line-height: 1.4 → 14px × 1.4 = 19.6px line box. */
18
+ export const BODY_LINE_HEIGHT = 336;
19
+ /** Browser default `<p>` margin is 1em at the harness body font size (14px). */
20
+ export const DEFAULT_PARAGRAPH_MARGIN_PX = 14;
21
+ /** Blockquote vertical margin (1em at 14px body font in the HTML harness). */
22
+ export const BLOCKQUOTE_MARGIN_PX = 14;
23
+ /** Blockquote horizontal indent per nesting level (HTML padding-left: 12px). */
24
+ export const BLOCKQUOTE_INDENT_PX = 12;
25
+ /** Chromium UA default blockquote side margin (margin: 1em 40px). */
26
+ export const BLOCKQUOTE_UA_SIDE_MARGIN_PX = 40;
27
+ /** Chromium default `<ol>`/`<ul>`: padding-left 40px, text start at 40px from list edge. */
28
+ export const LIST_LEVEL_LEFT_TWIPS = 600;
29
+ /** Hanging indent: marker column ~20px when text starts at 40px (14px font, decimal markers). */
30
+ export const LIST_HANGING_TWIPS = 300;
31
+ /** HTML harness line box height in px (matches line-height: 1.4 on 14px font). */
32
+ export const BODY_LINE_BOX_PX = 19.6;
33
+ /** EXACT `w:spacing/@w:line` in twips — CSS line box, not Word AUTO multiplier. */
34
+ export const BODY_LINE_EXACT_TWIPS = Math.round(BODY_LINE_BOX_PX * 15);
35
+ export const HEADING_LEVELS = {
36
+ h1: HeadingLevel.HEADING_1,
37
+ h2: HeadingLevel.HEADING_2,
38
+ h3: HeadingLevel.HEADING_3,
39
+ h4: HeadingLevel.HEADING_4,
40
+ h5: HeadingLevel.HEADING_5,
41
+ h6: HeadingLevel.HEADING_6,
42
+ };
43
+ /** Font sizes in half-points — match Chromium UA defaults at 14px body (2em, 1.5em, …). */
44
+ export const HEADING_FONT_HALF_POINTS = {
45
+ h1: 42,
46
+ h2: 32,
47
+ h3: 25,
48
+ h4: 24,
49
+ h5: 20,
50
+ h6: 18,
51
+ };
52
+ /** Chromium UA heading vertical margins as a fraction of heading font size (inline path). */
53
+ export const HEADING_MARGIN_EM = {
54
+ h1: 0.67,
55
+ h2: 0.83,
56
+ h3: 1,
57
+ h4: 1.33,
58
+ h5: 1.67,
59
+ h6: 2.33,
60
+ };
61
+ export const BLOCK_TAGS = new Set([
62
+ "address",
63
+ "article",
64
+ "aside",
65
+ "blockquote",
66
+ "center",
67
+ "div",
68
+ "dl",
69
+ "dt",
70
+ "dd",
71
+ "fieldset",
72
+ "figcaption",
73
+ "figure",
74
+ "footer",
75
+ "form",
76
+ "h1",
77
+ "h2",
78
+ "h3",
79
+ "h4",
80
+ "h5",
81
+ "h6",
82
+ "header",
83
+ "hr",
84
+ "li",
85
+ "main",
86
+ "nav",
87
+ "ol",
88
+ "p",
89
+ "pre",
90
+ "section",
91
+ "svg",
92
+ "table",
93
+ "tbody",
94
+ "thead",
95
+ "tfoot",
96
+ "tr",
97
+ "td",
98
+ "th",
99
+ "ul",
100
+ ]);
101
+ function listLevelParagraphStyle(level) {
102
+ const left = LIST_LEVEL_LEFT_TWIPS * (level + 1);
103
+ return {
104
+ indent: {
105
+ left,
106
+ hanging: LIST_HANGING_TWIPS,
107
+ },
108
+ leftTabStop: left,
109
+ };
110
+ }
111
+ const LIST_LEVELS = [0, 1, 2, 3, 4];
112
+ function listLevel(level, format, text) {
113
+ return {
114
+ level,
115
+ format,
116
+ text,
117
+ alignment: AlignmentType.LEFT,
118
+ suffix: LevelSuffix.TAB,
119
+ style: {
120
+ paragraph: listLevelParagraphStyle(level),
121
+ run: { font: BODY_FONT, size: BODY_FONT_HALF_POINTS },
122
+ },
123
+ };
124
+ }
125
+ /** Ordered list: same numeric format at every nesting level, `%n.` text. */
126
+ function numberedConfig(reference, format) {
127
+ return { reference, levels: LIST_LEVELS.map((l) => listLevel(l, format, `%${l + 1}.`)) };
128
+ }
129
+ /** Unordered list: fixed glyph at every level. */
130
+ function bulletConfig(reference, glyph) {
131
+ return { reference, levels: LIST_LEVELS.map((l) => listLevel(l, LevelFormat.BULLET, glyph)) };
132
+ }
133
+ /** Maps a CSS `list-style-type` to its numbering reference (see `LIST_STYLE_REFERENCES`). */
134
+ export const LIST_STYLE_REFERENCES = {
135
+ // ordered
136
+ decimal: "numbers",
137
+ "lower-alpha": "numbers-lower-alpha",
138
+ "lower-latin": "numbers-lower-alpha",
139
+ "upper-alpha": "numbers-upper-alpha",
140
+ "upper-latin": "numbers-upper-alpha",
141
+ "lower-roman": "numbers-lower-roman",
142
+ "upper-roman": "numbers-upper-roman",
143
+ // unordered
144
+ disc: "bullets",
145
+ circle: "bullets-circle",
146
+ square: "bullets-square",
147
+ };
148
+ export const NUMBERING_CONFIG = {
149
+ config: [
150
+ {
151
+ reference: "bullets",
152
+ levels: LIST_LEVELS.map((level) => listLevel(level, LevelFormat.BULLET, level % 2 === 0 ? "•" : "◦")),
153
+ },
154
+ numberedConfig("numbers", LevelFormat.DECIMAL),
155
+ numberedConfig("numbers-lower-alpha", LevelFormat.LOWER_LETTER),
156
+ numberedConfig("numbers-upper-alpha", LevelFormat.UPPER_LETTER),
157
+ numberedConfig("numbers-lower-roman", LevelFormat.LOWER_ROMAN),
158
+ numberedConfig("numbers-upper-roman", LevelFormat.UPPER_ROMAN),
159
+ bulletConfig("bullets-circle", "◦"),
160
+ bulletConfig("bullets-square", "▪"),
161
+ ],
162
+ };
163
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/converter/constants.ts"],"names":[],"mappings":"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"}