@doxi/docx 0.11.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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/blocks.d.ts +30 -0
  4. package/dist/blocks.d.ts.map +1 -0
  5. package/dist/blocks.js +69 -0
  6. package/dist/blocks.js.map +1 -0
  7. package/dist/export.d.ts +41 -0
  8. package/dist/export.d.ts.map +1 -0
  9. package/dist/export.js +70 -0
  10. package/dist/export.js.map +1 -0
  11. package/dist/import.d.ts +35 -0
  12. package/dist/import.d.ts.map +1 -0
  13. package/dist/import.js +156 -0
  14. package/dist/import.js.map +1 -0
  15. package/dist/index.d.ts +4 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +4 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/inline.d.ts +15 -0
  20. package/dist/inline.d.ts.map +1 -0
  21. package/dist/inline.js +48 -0
  22. package/dist/inline.js.map +1 -0
  23. package/dist/lists.d.ts +17 -0
  24. package/dist/lists.d.ts.map +1 -0
  25. package/dist/lists.js +60 -0
  26. package/dist/lists.js.map +1 -0
  27. package/dist/parse-blocks.d.ts +33 -0
  28. package/dist/parse-blocks.d.ts.map +1 -0
  29. package/dist/parse-blocks.js +145 -0
  30. package/dist/parse-blocks.js.map +1 -0
  31. package/dist/parse-numbering.d.ts +24 -0
  32. package/dist/parse-numbering.d.ts.map +1 -0
  33. package/dist/parse-numbering.js +90 -0
  34. package/dist/parse-numbering.js.map +1 -0
  35. package/dist/parse-runs.d.ts +28 -0
  36. package/dist/parse-runs.d.ts.map +1 -0
  37. package/dist/parse-runs.js +230 -0
  38. package/dist/parse-runs.js.map +1 -0
  39. package/dist/parse-tables.d.ts +15 -0
  40. package/dist/parse-tables.d.ts.map +1 -0
  41. package/dist/parse-tables.js +200 -0
  42. package/dist/parse-tables.js.map +1 -0
  43. package/dist/parse-xml.d.ts +26 -0
  44. package/dist/parse-xml.d.ts.map +1 -0
  45. package/dist/parse-xml.js +286 -0
  46. package/dist/parse-xml.js.map +1 -0
  47. package/dist/parts.d.ts +18 -0
  48. package/dist/parts.d.ts.map +1 -0
  49. package/dist/parts.js +102 -0
  50. package/dist/parts.js.map +1 -0
  51. package/dist/runs.d.ts +40 -0
  52. package/dist/runs.d.ts.map +1 -0
  53. package/dist/runs.js +72 -0
  54. package/dist/runs.js.map +1 -0
  55. package/dist/tables.d.ts +18 -0
  56. package/dist/tables.d.ts.map +1 -0
  57. package/dist/tables.js +95 -0
  58. package/dist/tables.js.map +1 -0
  59. package/dist/unzip.d.ts +16 -0
  60. package/dist/unzip.d.ts.map +1 -0
  61. package/dist/unzip.js +108 -0
  62. package/dist/unzip.js.map +1 -0
  63. package/dist/warnings.d.ts +18 -0
  64. package/dist/warnings.d.ts.map +1 -0
  65. package/dist/warnings.js +12 -0
  66. package/dist/warnings.js.map +1 -0
  67. package/dist/xml.d.ts +23 -0
  68. package/dist/xml.d.ts.map +1 -0
  69. package/dist/xml.js +58 -0
  70. package/dist/xml.js.map +1 -0
  71. package/dist/zip.d.ts +15 -0
  72. package/dist/zip.d.ts.map +1 -0
  73. package/dist/zip.js +165 -0
  74. package/dist/zip.js.map +1 -0
  75. package/package.json +42 -0
package/dist/parts.js ADDED
@@ -0,0 +1,102 @@
1
+ import { el, renderXml } from './xml.js';
2
+ export function contentTypesXml() {
3
+ const root = el('Types', { xmlns: 'http://schemas.openxmlformats.org/package/2006/content-types' }, el('Default', { Extension: 'rels', ContentType: 'application/vnd.openxmlformats-package.relationships+xml' }), el('Default', { Extension: 'xml', ContentType: 'application/xml' }), el('Override', { PartName: '/word/document.xml', ContentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml' }), el('Override', { PartName: '/word/styles.xml', ContentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml' }), el('Override', { PartName: '/word/numbering.xml', ContentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml' }));
4
+ return renderXml(root, { declaration: true });
5
+ }
6
+ export function rootRelsXml() {
7
+ const root = el('Relationships', { xmlns: 'http://schemas.openxmlformats.org/package/2006/relationships' }, el('Relationship', {
8
+ Id: 'rId1',
9
+ Type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument',
10
+ Target: 'word/document.xml',
11
+ }));
12
+ return renderXml(root, { declaration: true });
13
+ }
14
+ export function documentRelsXml(hyperlinks) {
15
+ const items = [
16
+ el('Relationship', {
17
+ Id: 'rIdStyles',
18
+ Type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles',
19
+ Target: 'styles.xml',
20
+ }),
21
+ el('Relationship', {
22
+ Id: 'rIdNumbering',
23
+ Type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering',
24
+ Target: 'numbering.xml',
25
+ }),
26
+ ];
27
+ for (const h of hyperlinks) {
28
+ items.push(el('Relationship', {
29
+ Id: h.id,
30
+ Type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',
31
+ Target: h.target,
32
+ TargetMode: 'External',
33
+ }));
34
+ }
35
+ const root = el('Relationships', { xmlns: 'http://schemas.openxmlformats.org/package/2006/relationships' }, ...items);
36
+ return renderXml(root, { declaration: true });
37
+ }
38
+ export function stylesXml() {
39
+ return STYLES_XML;
40
+ }
41
+ /**
42
+ * Emit `numbering.xml`. The two predefined abstractNums (0 = bullet, 1 =
43
+ * decimal) and their seed `<w:num>` instances (numId 1, 2) are always
44
+ * present. Additional `<w:num>` rows can be threaded in via `extra` so each
45
+ * Doxiva `list` node gets its own numbering instance (independent restarts).
46
+ */
47
+ export function numberingXml(extra = []) {
48
+ if (extra.length === 0)
49
+ return NUMBERING_XML;
50
+ const extraRows = extra
51
+ .map((e) => ` <w:num w:numId="${e.numId}"><w:abstractNumId w:val="${e.ordered ? 1 : 0}"/></w:num>`)
52
+ .join('\n');
53
+ // Inject extra <w:num> rows just before </w:numbering>.
54
+ return NUMBERING_XML.replace('</w:numbering>', `${extraRows}\n</w:numbering>`);
55
+ }
56
+ const STYLES_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
57
+ <w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
58
+ <w:style w:type="paragraph" w:styleId="Normal" w:default="1"><w:name w:val="Normal"/></w:style>
59
+ <w:style w:type="paragraph" w:styleId="Heading1"><w:name w:val="heading 1"/><w:basedOn w:val="Normal"/><w:pPr><w:outlineLvl w:val="0"/></w:pPr><w:rPr><w:b/><w:sz w:val="48"/></w:rPr></w:style>
60
+ <w:style w:type="paragraph" w:styleId="Heading2"><w:name w:val="heading 2"/><w:basedOn w:val="Normal"/><w:pPr><w:outlineLvl w:val="1"/></w:pPr><w:rPr><w:b/><w:sz w:val="36"/></w:rPr></w:style>
61
+ <w:style w:type="paragraph" w:styleId="Heading3"><w:name w:val="heading 3"/><w:basedOn w:val="Normal"/><w:pPr><w:outlineLvl w:val="2"/></w:pPr><w:rPr><w:b/><w:sz w:val="30"/></w:rPr></w:style>
62
+ <w:style w:type="paragraph" w:styleId="Heading4"><w:name w:val="heading 4"/><w:basedOn w:val="Normal"/><w:pPr><w:outlineLvl w:val="3"/></w:pPr><w:rPr><w:b/><w:sz w:val="26"/></w:rPr></w:style>
63
+ <w:style w:type="paragraph" w:styleId="Heading5"><w:name w:val="heading 5"/><w:basedOn w:val="Normal"/><w:pPr><w:outlineLvl w:val="4"/></w:pPr><w:rPr><w:b/><w:sz w:val="24"/></w:rPr></w:style>
64
+ <w:style w:type="paragraph" w:styleId="Heading6"><w:name w:val="heading 6"/><w:basedOn w:val="Normal"/><w:pPr><w:outlineLvl w:val="5"/></w:pPr><w:rPr><w:b/><w:sz w:val="22"/></w:rPr></w:style>
65
+ <w:style w:type="paragraph" w:styleId="Quote"><w:name w:val="Quote"/><w:basedOn w:val="Normal"/><w:pPr><w:ind w:left="720"/></w:pPr><w:rPr><w:i/></w:rPr></w:style>
66
+ </w:styles>`;
67
+ const NUMBERING_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
68
+ <w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
69
+ <w:abstractNum w:abstractNumId="0">
70
+ <w:lvl w:ilvl="0">
71
+ <w:numFmt w:val="bullet"/>
72
+ <w:lvlText w:val="•"/>
73
+ <w:lvlJc w:val="left"/>
74
+ <w:pPr><w:ind w:left="720" w:hanging="360"/></w:pPr>
75
+ </w:lvl>
76
+ <w:lvl w:ilvl="1">
77
+ <w:numFmt w:val="bullet"/>
78
+ <w:lvlText w:val="◦"/>
79
+ <w:lvlJc w:val="left"/>
80
+ <w:pPr><w:ind w:left="1440" w:hanging="360"/></w:pPr>
81
+ </w:lvl>
82
+ </w:abstractNum>
83
+ <w:abstractNum w:abstractNumId="1">
84
+ <w:lvl w:ilvl="0">
85
+ <w:start w:val="1"/>
86
+ <w:numFmt w:val="decimal"/>
87
+ <w:lvlText w:val="%1."/>
88
+ <w:lvlJc w:val="left"/>
89
+ <w:pPr><w:ind w:left="720" w:hanging="360"/></w:pPr>
90
+ </w:lvl>
91
+ <w:lvl w:ilvl="1">
92
+ <w:start w:val="1"/>
93
+ <w:numFmt w:val="lowerLetter"/>
94
+ <w:lvlText w:val="%2."/>
95
+ <w:lvlJc w:val="left"/>
96
+ <w:pPr><w:ind w:left="1440" w:hanging="360"/></w:pPr>
97
+ </w:lvl>
98
+ </w:abstractNum>
99
+ <w:num w:numId="1"><w:abstractNumId w:val="0"/></w:num>
100
+ <w:num w:numId="2"><w:abstractNumId w:val="1"/></w:num>
101
+ </w:numbering>`;
102
+ //# sourceMappingURL=parts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parts.js","sourceRoot":"","sources":["../src/parts.ts"],"names":[],"mappings":"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"}
package/dist/runs.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ import type { DxNode } from '@doxi/core';
2
+ import type { Mark } from '@doxi/core';
3
+ import { type XmlElement } from './xml.js';
4
+ /**
5
+ * Render context threaded through block / inline / run renderers.
6
+ *
7
+ * Collects out-of-band data the document needs at zip-build time:
8
+ * - hyperlinks → relationship entries for word/_rels/document.xml.rels
9
+ * - numbering plan → which Doxiva `list` instances need an explicit numId
10
+ *
11
+ * The counters use boxed objects so nested helpers can mutate them without
12
+ * passing them by reference everywhere.
13
+ */
14
+ export interface RenderCtx {
15
+ readonly hyperlinks: Array<{
16
+ id: string;
17
+ target: string;
18
+ }>;
19
+ readonly numIds: Map<DxNode, number>;
20
+ readonly numberingPlan: Array<{
21
+ numId: number;
22
+ ordered: boolean;
23
+ }>;
24
+ ridCounter: {
25
+ value: number;
26
+ };
27
+ numIdCounter: {
28
+ value: number;
29
+ };
30
+ }
31
+ export declare function newRenderCtx(): RenderCtx;
32
+ /** Render a text leaf as `<w:r>` with optional `<w:rPr>` from its marks. */
33
+ export declare function renderTextRun(text: DxNode): XmlElement;
34
+ /**
35
+ * Build `<w:rPr>` from a text leaf's marks. Returns null when no run-level
36
+ * properties are needed (e.g. plain text with only a `link` mark, which is
37
+ * handled at the run-wrapping level).
38
+ */
39
+ export declare function rprFromMarks(marks: ReadonlyArray<Mark>): XmlElement | null;
40
+ //# sourceMappingURL=runs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runs.d.ts","sourceRoot":"","sources":["../src/runs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAM,KAAK,UAAU,EAAE,MAAM,UAAU,CAAA;AAE9C;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC1D,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,QAAQ,CAAC,aAAa,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IAClE,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7B,YAAY,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAChC;AAED,wBAAgB,YAAY,IAAI,SAAS,CAaxC;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAOtD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,GAAG,UAAU,GAAG,IAAI,CAqC1E"}
package/dist/runs.js ADDED
@@ -0,0 +1,72 @@
1
+ import { el } from './xml.js';
2
+ export function newRenderCtx() {
3
+ return {
4
+ hyperlinks: [],
5
+ numIds: new Map(),
6
+ numberingPlan: [],
7
+ // Start at rId100 so per-document allocations can't collide with the
8
+ // fixed-string rels in documentRelsXml (rIdStyles / rIdNumbering).
9
+ ridCounter: { value: 100 },
10
+ // 1 and 2 are predefined in numbering.xml as the default bullet/numbered
11
+ // instances. We allocate fresh numIds (>=3) per Doxiva list node so each
12
+ // list restarts independently.
13
+ numIdCounter: { value: 3 },
14
+ };
15
+ }
16
+ /** Render a text leaf as `<w:r>` with optional `<w:rPr>` from its marks. */
17
+ export function renderTextRun(text) {
18
+ const rPr = rprFromMarks(text.marks);
19
+ const children = [];
20
+ if (rPr)
21
+ children.push(rPr);
22
+ // xml:space="preserve" so leading/trailing whitespace is preserved verbatim.
23
+ children.push(el('w:t', { 'xml:space': 'preserve' }, text.text ?? ''));
24
+ return el('w:r', null, ...children);
25
+ }
26
+ /**
27
+ * Build `<w:rPr>` from a text leaf's marks. Returns null when no run-level
28
+ * properties are needed (e.g. plain text with only a `link` mark, which is
29
+ * handled at the run-wrapping level).
30
+ */
31
+ export function rprFromMarks(marks) {
32
+ if (!marks.length)
33
+ return null;
34
+ const children = [];
35
+ for (const m of marks) {
36
+ switch (m.type.name) {
37
+ case 'bold':
38
+ children.push(el('w:b', null));
39
+ break;
40
+ case 'italic':
41
+ children.push(el('w:i', null));
42
+ break;
43
+ case 'underline':
44
+ children.push(el('w:u', { 'w:val': 'single' }));
45
+ break;
46
+ case 'color': {
47
+ const v = m.attrs.value ?? '';
48
+ if (v)
49
+ children.push(el('w:color', { 'w:val': v.replace(/^#/, '') }));
50
+ break;
51
+ }
52
+ case 'font_size': {
53
+ const px = m.attrs.px;
54
+ if (typeof px === 'number') {
55
+ // Word uses half-points: 1 pt = 2 half-points; we approximate
56
+ // px → half-points by px * 1.5 (matching the v0.7 plan table).
57
+ children.push(el('w:sz', { 'w:val': String(Math.round(px * 1.5)) }));
58
+ }
59
+ break;
60
+ }
61
+ case 'font_family': {
62
+ const name = m.attrs.name;
63
+ if (name)
64
+ children.push(el('w:rFonts', { 'w:ascii': name, 'w:hAnsi': name }));
65
+ break;
66
+ }
67
+ // link is handled at the run-wrapping level (inline.ts), not in rPr.
68
+ }
69
+ }
70
+ return children.length ? el('w:rPr', null, ...children) : null;
71
+ }
72
+ //# sourceMappingURL=runs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runs.js","sourceRoot":"","sources":["../src/runs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { DxNode } from '@doxi/core';
2
+ import { type XmlElement } from './xml.js';
3
+ import type { RenderCtx } from './runs.js';
4
+ /**
5
+ * Render a Doxiva `table` node as `<w:tbl>` with proper grid + spans.
6
+ *
7
+ * Layout strategy:
8
+ * - Total content width is fixed at 9360 twips (~6.5in), distributed
9
+ * evenly across columns. The Doxiva model carries `widthPx` on cells
10
+ * but we don't honor it in v0.7 — even widths simplify the
11
+ * interaction between widthPx, colspan, and the grid renderer.
12
+ * - Continuation cells from a rowspan get an empty `<w:vMerge/>` (no
13
+ * w:val attr) and an empty paragraph placeholder.
14
+ * - Header rows emit `<w:tblHeader/>` in `<w:trPr>`, which Word uses
15
+ * when repeating header rows across page breaks.
16
+ */
17
+ export declare function renderTable(table: DxNode, ctx: RenderCtx): XmlElement;
18
+ //# sourceMappingURL=tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC,OAAO,EAAM,KAAK,UAAU,EAAE,MAAM,UAAU,CAAA;AAG9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAE1C;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,UAAU,CAmFrE"}
package/dist/tables.js ADDED
@@ -0,0 +1,95 @@
1
+ import { tableGrid } from '@doxi/core';
2
+ import { el } from './xml.js';
3
+ import { renderParagraph, renderHeading, renderHr, renderPageBreak } from './blocks.js';
4
+ import { renderList } from './lists.js';
5
+ /**
6
+ * Render a Doxiva `table` node as `<w:tbl>` with proper grid + spans.
7
+ *
8
+ * Layout strategy:
9
+ * - Total content width is fixed at 9360 twips (~6.5in), distributed
10
+ * evenly across columns. The Doxiva model carries `widthPx` on cells
11
+ * but we don't honor it in v0.7 — even widths simplify the
12
+ * interaction between widthPx, colspan, and the grid renderer.
13
+ * - Continuation cells from a rowspan get an empty `<w:vMerge/>` (no
14
+ * w:val attr) and an empty paragraph placeholder.
15
+ * - Header rows emit `<w:tblHeader/>` in `<w:trPr>`, which Word uses
16
+ * when repeating header rows across page breaks.
17
+ */
18
+ export function renderTable(table, ctx) {
19
+ // tableStart=0 is fine; we never use the absolute positions emitted by
20
+ // tableGrid, only its (row, col) topology and per-cell spans.
21
+ const grid = tableGrid(table, 0);
22
+ const colCount = Math.max(1, grid.cols);
23
+ const totalTwips = 9360;
24
+ const colWidth = Math.floor(totalTwips / colCount);
25
+ const tblGrid = el('w:tblGrid', null, ...Array.from({ length: colCount }, () => el('w:gridCol', { 'w:w': String(colWidth) })));
26
+ const rows = [];
27
+ for (let rIdx = 0; rIdx < table.content.childCount; rIdx++) {
28
+ const row = table.content.child(rIdx);
29
+ const isHeader = row.attrs.header === true;
30
+ const cellEls = [];
31
+ for (let cIdx = 0; cIdx < colCount; cIdx++) {
32
+ const cell = grid.cellAt(rIdx, cIdx);
33
+ if (!cell)
34
+ continue;
35
+ // Continuation columns of a colspan: master cell already covered this column.
36
+ if (cell.col !== cIdx)
37
+ continue;
38
+ const spanW = colWidth * (cell.colspan ?? 1);
39
+ if (cell.row !== rIdx) {
40
+ // Continuation row of a rowspan — empty cell with `<w:vMerge/>`.
41
+ cellEls.push(el('w:tc', null, el('w:tcPr', null, el('w:tcW', { 'w:w': String(spanW), 'w:type': 'dxa' }), ...((cell.colspan ?? 1) > 1 ? [el('w:gridSpan', { 'w:val': String(cell.colspan) })] : []), el('w:vMerge', null)), el('w:p', null)));
42
+ continue;
43
+ }
44
+ // Master cell — emit content.
45
+ const tcPrChildren = [];
46
+ tcPrChildren.push(el('w:tcW', { 'w:w': String(spanW), 'w:type': 'dxa' }));
47
+ if ((cell.colspan ?? 1) > 1) {
48
+ tcPrChildren.push(el('w:gridSpan', { 'w:val': String(cell.colspan) }));
49
+ }
50
+ if ((cell.rowspan ?? 1) > 1) {
51
+ tcPrChildren.push(el('w:vMerge', { 'w:val': 'restart' }));
52
+ }
53
+ const bg = cell.node.attrs.background;
54
+ if (bg) {
55
+ tcPrChildren.push(el('w:shd', {
56
+ 'w:val': 'clear',
57
+ 'w:color': 'auto',
58
+ 'w:fill': bg.replace(/^#/, ''),
59
+ }));
60
+ }
61
+ const cellChildren = [el('w:tcPr', null, ...tcPrChildren)];
62
+ for (let i = 0; i < cell.node.content.childCount; i++) {
63
+ const inner = cell.node.content.child(i);
64
+ cellChildren.push(...renderCellBlock(inner, ctx));
65
+ }
66
+ // Word requires at least one paragraph inside a `<w:tc>` — guarantee that.
67
+ if (cellChildren.length === 1)
68
+ cellChildren.push(el('w:p', null));
69
+ cellEls.push(el('w:tc', null, ...cellChildren));
70
+ }
71
+ const trChildren = [];
72
+ if (isHeader) {
73
+ trChildren.push(el('w:trPr', null, el('w:tblHeader', null)));
74
+ }
75
+ rows.push(el('w:tr', null, ...trChildren, ...cellEls));
76
+ }
77
+ return el('w:tbl', null, el('w:tblPr', null, el('w:tblW', { 'w:w': String(totalTwips), 'w:type': 'dxa' }), el('w:tblBorders', null, el('w:top', { 'w:val': 'single', 'w:sz': '4', 'w:color': 'auto' }), el('w:left', { 'w:val': 'single', 'w:sz': '4', 'w:color': 'auto' }), el('w:bottom', { 'w:val': 'single', 'w:sz': '4', 'w:color': 'auto' }), el('w:right', { 'w:val': 'single', 'w:sz': '4', 'w:color': 'auto' }), el('w:insideH', { 'w:val': 'single', 'w:sz': '4', 'w:color': 'auto' }), el('w:insideV', { 'w:val': 'single', 'w:sz': '4', 'w:color': 'auto' }))), tblGrid, ...rows);
78
+ }
79
+ /**
80
+ * Block-level dispatch for content inside a table cell. Mirrors the
81
+ * top-level export dispatcher in export.ts but kept local to avoid a
82
+ * tables ↔ export.ts circular import.
83
+ */
84
+ function renderCellBlock(node, ctx) {
85
+ switch (node.type.name) {
86
+ case 'paragraph': return [renderParagraph(node, ctx)];
87
+ case 'heading': return [renderHeading(node, ctx)];
88
+ case 'hr': return [renderHr(node)];
89
+ case 'page_break': return [renderPageBreak(node)];
90
+ case 'list': return renderList(node, ctx, 0);
91
+ case 'table': return [renderTable(node, ctx)];
92
+ default: return [];
93
+ }
94
+ }
95
+ //# sourceMappingURL=tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.js","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Minimal ZIP reader for DOCX import. Supports stored (method 0) and DEFLATE
3
+ * (method 8) entries. DEFLATE is decoded via the platform's
4
+ * `DecompressionStream('deflate-raw')` — available in Node 18+ and all modern
5
+ * browsers. If the platform doesn't expose it, an entry compressed with
6
+ * DEFLATE will throw a clear error.
7
+ *
8
+ * This is the read counterpart to `zip.ts`'s `buildZip`. We avoid bringing in
9
+ * a third-party dependency (e.g. pako) by leaning on the platform.
10
+ */
11
+ export interface UnzippedEntry {
12
+ readonly name: string;
13
+ readonly data: Uint8Array;
14
+ }
15
+ export declare function unzip(bytes: Uint8Array): Promise<ReadonlyArray<UnzippedEntry>>;
16
+ //# sourceMappingURL=unzip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unzip.d.ts","sourceRoot":"","sources":["../src/unzip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAC1B;AAMD,wBAAsB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CA4DpF"}
package/dist/unzip.js ADDED
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Minimal ZIP reader for DOCX import. Supports stored (method 0) and DEFLATE
3
+ * (method 8) entries. DEFLATE is decoded via the platform's
4
+ * `DecompressionStream('deflate-raw')` — available in Node 18+ and all modern
5
+ * browsers. If the platform doesn't expose it, an entry compressed with
6
+ * DEFLATE will throw a clear error.
7
+ *
8
+ * This is the read counterpart to `zip.ts`'s `buildZip`. We avoid bringing in
9
+ * a third-party dependency (e.g. pako) by leaning on the platform.
10
+ */
11
+ const SIG_EOCD = 0x06054b50;
12
+ const SIG_CD = 0x02014b50;
13
+ const SIG_LOCAL = 0x04034b50;
14
+ export async function unzip(bytes) {
15
+ if (bytes.length < 22)
16
+ throw new Error('unzip: input too small to be a ZIP archive');
17
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
18
+ // Locate EOCD by scanning backwards. The EOCD is at least 22 bytes; the
19
+ // comment field is at most 64 KiB. Bound the scan accordingly.
20
+ const scanLimit = Math.max(0, bytes.length - 22 - 0xffff);
21
+ let eocdPos = -1;
22
+ for (let i = bytes.length - 22; i >= scanLimit; i--) {
23
+ if (view.getUint32(i, true) === SIG_EOCD) {
24
+ eocdPos = i;
25
+ break;
26
+ }
27
+ }
28
+ if (eocdPos < 0)
29
+ throw new Error('unzip: EOCD signature not found (not a ZIP archive)');
30
+ const totalEntries = view.getUint16(eocdPos + 10, true);
31
+ const cdOffset = view.getUint32(eocdPos + 16, true);
32
+ const decoder = new TextDecoder('utf-8');
33
+ const entries = [];
34
+ let pos = cdOffset;
35
+ for (let i = 0; i < totalEntries; i++) {
36
+ if (pos + 46 > bytes.length)
37
+ throw new Error(`unzip: central directory truncated at entry ${i}`);
38
+ const sig = view.getUint32(pos, true);
39
+ if (sig !== SIG_CD)
40
+ throw new Error(`unzip: bad central-dir signature 0x${sig.toString(16)} at offset ${pos}`);
41
+ const method = view.getUint16(pos + 10, true);
42
+ const compSize = view.getUint32(pos + 20, true);
43
+ const nameLen = view.getUint16(pos + 28, true);
44
+ const extraLen = view.getUint16(pos + 30, true);
45
+ const commentLen = view.getUint16(pos + 32, true);
46
+ const localOffset = view.getUint32(pos + 42, true);
47
+ const name = decoder.decode(bytes.subarray(pos + 46, pos + 46 + nameLen));
48
+ pos += 46 + nameLen + extraLen + commentLen;
49
+ if (localOffset + 30 > bytes.length) {
50
+ throw new Error(`unzip: local file header for '${name}' points past end of archive`);
51
+ }
52
+ const lSig = view.getUint32(localOffset, true);
53
+ if (lSig !== SIG_LOCAL) {
54
+ throw new Error(`unzip: bad local file header signature for '${name}' at ${localOffset}`);
55
+ }
56
+ const lNameLen = view.getUint16(localOffset + 26, true);
57
+ const lExtraLen = view.getUint16(localOffset + 28, true);
58
+ const dataStart = localOffset + 30 + lNameLen + lExtraLen;
59
+ if (dataStart + compSize > bytes.length) {
60
+ throw new Error(`unzip: payload for '${name}' extends past end of archive`);
61
+ }
62
+ const raw = bytes.subarray(dataStart, dataStart + compSize);
63
+ let data;
64
+ if (method === 0) {
65
+ // Copy so callers can hold onto the buffer independent of the input.
66
+ data = new Uint8Array(raw);
67
+ }
68
+ else if (method === 8) {
69
+ data = await inflate(raw);
70
+ }
71
+ else {
72
+ throw new Error(`unzip: unsupported compression method ${method} for '${name}' (only store=0 and deflate=8 are supported)`);
73
+ }
74
+ entries.push({ name, data });
75
+ }
76
+ return entries;
77
+ }
78
+ async function inflate(compressed) {
79
+ const DS = globalThis.DecompressionStream;
80
+ if (!DS) {
81
+ throw new Error('unzip: DecompressionStream is not available in this environment; DEFLATE-compressed ZIP entries cannot be read');
82
+ }
83
+ const stream = new ReadableStream({
84
+ start(controller) {
85
+ controller.enqueue(compressed);
86
+ controller.close();
87
+ },
88
+ }).pipeThrough(new DS('deflate-raw'));
89
+ const reader = stream.getReader();
90
+ const chunks = [];
91
+ let total = 0;
92
+ // eslint-disable-next-line no-constant-condition
93
+ while (true) {
94
+ const { value, done } = await reader.read();
95
+ if (done)
96
+ break;
97
+ chunks.push(value);
98
+ total += value.byteLength;
99
+ }
100
+ const out = new Uint8Array(total);
101
+ let off = 0;
102
+ for (const c of chunks) {
103
+ out.set(c, off);
104
+ off += c.byteLength;
105
+ }
106
+ return out;
107
+ }
108
+ //# sourceMappingURL=unzip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unzip.js","sourceRoot":"","sources":["../src/unzip.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Import warning machinery. The DOCX importer is best-effort: it skips
3
+ * things it doesn't yet understand rather than refusing the whole document.
4
+ * Each skipped construct is reported via a structured `ImportWarning` so
5
+ * callers (UI, telemetry) can surface partial-fidelity events.
6
+ */
7
+ export type ImportWarningCode = 'unknown-element' | 'unknown-style' | 'unsupported-feature' | 'malformed-xml' | 'unknown-relationship' | 'compression-unsupported' | 'unsafe-content';
8
+ export interface ImportWarning {
9
+ readonly code: ImportWarningCode;
10
+ readonly message: string;
11
+ readonly path?: string;
12
+ }
13
+ export declare class WarningCollector {
14
+ private readonly _warnings;
15
+ get warnings(): ReadonlyArray<ImportWarning>;
16
+ add(w: ImportWarning): void;
17
+ }
18
+ //# sourceMappingURL=warnings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"warnings.d.ts","sourceRoot":"","sources":["../src/warnings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,iBAAiB,GACzB,iBAAiB,GACjB,eAAe,GACf,qBAAqB,GACrB,eAAe,GACf,sBAAsB,GACtB,yBAAyB,GACzB,gBAAgB,CAAA;AAEpB,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsB;IAChD,IAAI,QAAQ,IAAI,aAAa,CAAC,aAAa,CAAC,CAA0B;IACtE,GAAG,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI;CAC5B"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Import warning machinery. The DOCX importer is best-effort: it skips
3
+ * things it doesn't yet understand rather than refusing the whole document.
4
+ * Each skipped construct is reported via a structured `ImportWarning` so
5
+ * callers (UI, telemetry) can surface partial-fidelity events.
6
+ */
7
+ export class WarningCollector {
8
+ _warnings = [];
9
+ get warnings() { return this._warnings; }
10
+ add(w) { this._warnings.push(w); }
11
+ }
12
+ //# sourceMappingURL=warnings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"warnings.js","sourceRoot":"","sources":["../src/warnings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiBH,MAAM,OAAO,gBAAgB;IACV,SAAS,GAAoB,EAAE,CAAA;IAChD,IAAI,QAAQ,KAAmC,OAAO,IAAI,CAAC,SAAS,CAAA,CAAC,CAAC;IACtE,GAAG,CAAC,CAAgB,IAAU,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA,CAAC,CAAC;CACvD"}
package/dist/xml.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Tiny XML builder with canonical pretty-print output.
3
+ *
4
+ * Output rules:
5
+ * - Empty elements collapse to <name/>.
6
+ * - Elements whose children are all text strings render inline:
7
+ * <name>text</name>
8
+ * - Elements with at least one child element render multi-line with two-space
9
+ * indentation, each child on its own line.
10
+ */
11
+ export interface XmlElement {
12
+ readonly name: string;
13
+ readonly attrs?: Readonly<Record<string, string>>;
14
+ readonly children?: ReadonlyArray<XmlNode>;
15
+ }
16
+ export type XmlNode = XmlElement | string;
17
+ export declare function el(name: string, attrs: Record<string, string> | null, ...children: XmlNode[]): XmlElement;
18
+ export declare function escapeXml(s: string): string;
19
+ export declare function escapeXmlAttr(s: string): string;
20
+ export declare function renderXml(root: XmlElement, opts?: {
21
+ declaration?: boolean;
22
+ }): string;
23
+ //# sourceMappingURL=xml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xml.d.ts","sourceRoot":"","sources":["../src/xml.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACjD,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;CAC3C;AACD,MAAM,MAAM,OAAO,GAAG,UAAU,GAAG,MAAM,CAAA;AAEzC,wBAAgB,EAAE,CAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,EACpC,GAAG,QAAQ,EAAE,OAAO,EAAE,GACrB,UAAU,CAKZ;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAKpF"}
package/dist/xml.js ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Tiny XML builder with canonical pretty-print output.
3
+ *
4
+ * Output rules:
5
+ * - Empty elements collapse to <name/>.
6
+ * - Elements whose children are all text strings render inline:
7
+ * <name>text</name>
8
+ * - Elements with at least one child element render multi-line with two-space
9
+ * indentation, each child on its own line.
10
+ */
11
+ export function el(name, attrs, ...children) {
12
+ const result = { name };
13
+ if (attrs && Object.keys(attrs).length)
14
+ result.attrs = attrs;
15
+ if (children.length)
16
+ result.children = children;
17
+ return result;
18
+ }
19
+ export function escapeXml(s) {
20
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
21
+ }
22
+ export function escapeXmlAttr(s) {
23
+ return escapeXml(s).replace(/"/g, '&quot;');
24
+ }
25
+ export function renderXml(root, opts) {
26
+ const lines = [];
27
+ if (opts?.declaration)
28
+ lines.push('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>');
29
+ renderElement(lines, root, 0);
30
+ return lines.join('\n');
31
+ }
32
+ function renderElement(lines, node, depth) {
33
+ const indent = ' '.repeat(depth);
34
+ const attrStr = node.attrs
35
+ ? ' ' + Object.entries(node.attrs).map(([k, v]) => `${k}="${escapeXmlAttr(v)}"`).join(' ')
36
+ : '';
37
+ const children = node.children ?? [];
38
+ if (children.length === 0) {
39
+ lines.push(`${indent}<${node.name}${attrStr}/>`);
40
+ return;
41
+ }
42
+ if (children.every((c) => typeof c === 'string')) {
43
+ const text = children.map(escapeXml).join('');
44
+ lines.push(`${indent}<${node.name}${attrStr}>${text}</${node.name}>`);
45
+ return;
46
+ }
47
+ lines.push(`${indent}<${node.name}${attrStr}>`);
48
+ for (const c of children) {
49
+ if (typeof c === 'string') {
50
+ lines.push(`${indent} ${escapeXml(c)}`);
51
+ }
52
+ else {
53
+ renderElement(lines, c, depth + 1);
54
+ }
55
+ }
56
+ lines.push(`${indent}</${node.name}>`);
57
+ }
58
+ //# sourceMappingURL=xml.js.map