@usejunior/odf-core 0.10.0 → 0.11.1

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 (48) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/convert/docx_to_odt.d.ts +20 -0
  5. package/dist/convert/docx_to_odt.d.ts.map +1 -0
  6. package/dist/convert/docx_to_odt.js +199 -0
  7. package/dist/convert/docx_to_odt.js.map +1 -0
  8. package/dist/convert/index.d.ts +3 -0
  9. package/dist/convert/index.d.ts.map +1 -0
  10. package/dist/convert/index.js +2 -0
  11. package/dist/convert/index.js.map +1 -0
  12. package/dist/convert/inline.d.ts +55 -0
  13. package/dist/convert/inline.d.ts.map +1 -0
  14. package/dist/convert/inline.js +274 -0
  15. package/dist/convert/inline.js.map +1 -0
  16. package/dist/convert/lists.d.ts +41 -0
  17. package/dist/convert/lists.d.ts.map +1 -0
  18. package/dist/convert/lists.js +137 -0
  19. package/dist/convert/lists.js.map +1 -0
  20. package/dist/convert/package.d.ts +54 -0
  21. package/dist/convert/package.d.ts.map +1 -0
  22. package/dist/convert/package.js +232 -0
  23. package/dist/convert/package.js.map +1 -0
  24. package/dist/convert/paragraph_styles.d.ts +25 -0
  25. package/dist/convert/paragraph_styles.d.ts.map +1 -0
  26. package/dist/convert/paragraph_styles.js +63 -0
  27. package/dist/convert/paragraph_styles.js.map +1 -0
  28. package/dist/convert/tables.d.ts +44 -0
  29. package/dist/convert/tables.d.ts.map +1 -0
  30. package/dist/convert/tables.js +226 -0
  31. package/dist/convert/tables.js.map +1 -0
  32. package/dist/convert/types.d.ts +27 -0
  33. package/dist/convert/types.d.ts.map +1 -0
  34. package/dist/convert/types.js +25 -0
  35. package/dist/convert/types.js.map +1 -0
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +1 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/shared/odf/OdfArchive.d.ts +16 -0
  41. package/dist/shared/odf/OdfArchive.d.ts.map +1 -1
  42. package/dist/shared/odf/OdfArchive.js +48 -1
  43. package/dist/shared/odf/OdfArchive.js.map +1 -1
  44. package/dist/shared/odf/namespaces.d.ts +4 -0
  45. package/dist/shared/odf/namespaces.d.ts.map +1 -1
  46. package/dist/shared/odf/namespaces.js +8 -0
  47. package/dist/shared/odf/namespaces.js.map +1 -1
  48. package/package.json +5 -4
@@ -0,0 +1,137 @@
1
+ /**
2
+ * List emission for the DOCX → ODT converter: flat auto-numbered/bullet view nodes →
3
+ * nested `text:list` DOM plus synthesized `text:list-style`s.
4
+ *
5
+ * The nesting logic is a DOM port of the HTML serializer's `ListBuilder`: OOXML `ilvl`
6
+ * carries no monotonicity guarantee, so each open list records the item LEVEL that opened
7
+ * it (not just depth) — that keeps consecutive same-level items siblings even after a
8
+ * level jump that skipped intermediate depths.
9
+ */
10
+ import { ODF_NS } from '../shared/odf/namespaces.js';
11
+ /** OOXML `numFmt` → ODF `style:num-format`. Anything unmapped falls back to decimal. */
12
+ const NUM_FORMAT_MAP = {
13
+ decimal: '1',
14
+ lowerLetter: 'a',
15
+ upperLetter: 'A',
16
+ lowerRoman: 'i',
17
+ upperRoman: 'I',
18
+ };
19
+ const MAX_LIST_LEVELS = 10;
20
+ const DEFAULT_BULLET_CHAR = '•';
21
+ /**
22
+ * Deduped `office:automatic-styles` registry for lists: one `L<n>` list style per source
23
+ * `num_id` (or per fallback kind when the numbering model has no entry), with one
24
+ * `text:list-level-style-*` per level sourced from the OOXML numbering definition.
25
+ */
26
+ export class ListStyleRegistry {
27
+ doc;
28
+ container;
29
+ numbering;
30
+ byKey = new Map();
31
+ constructor(doc, container, numbering) {
32
+ this.doc = doc;
33
+ this.container = container;
34
+ this.numbering = numbering;
35
+ }
36
+ /** Style name for a list opened by a node with the given `num_id` (bullet hint for model-less docs). */
37
+ styleFor(numId, bulletHint) {
38
+ const abstract = this.abstractFor(numId);
39
+ const key = abstract ? `num:${abstract.abstractNumId}` : bulletHint ? 'fallback:bullet' : 'fallback:number';
40
+ const existing = this.byKey.get(key);
41
+ if (existing)
42
+ return existing;
43
+ const name = `L${this.byKey.size + 1}`;
44
+ const style = this.doc.createElementNS(ODF_NS.TEXT, 'text:list-style');
45
+ style.setAttributeNS(ODF_NS.STYLE, 'style:name', name);
46
+ for (let level = 0; level < MAX_LIST_LEVELS; level++) {
47
+ const def = abstract?.levels.get(level);
48
+ if (def ? def.numFmt === 'bullet' : bulletHint) {
49
+ style.appendChild(this.bulletLevel(level));
50
+ }
51
+ else {
52
+ style.appendChild(this.numberLevel(level, def ?? null));
53
+ }
54
+ }
55
+ this.container.appendChild(style);
56
+ this.byKey.set(key, name);
57
+ return name;
58
+ }
59
+ /** True when the level's source `numFmt` is `bullet` (used to pick the fallback hint). */
60
+ isBulletLevel(numId, ilvl) {
61
+ const def = this.abstractFor(numId)?.levels.get(ilvl ?? 0);
62
+ return def?.numFmt === 'bullet';
63
+ }
64
+ abstractFor(numId) {
65
+ if (!numId || !this.numbering)
66
+ return null;
67
+ const instance = this.numbering.nums.get(numId);
68
+ if (!instance)
69
+ return null;
70
+ return this.numbering.abstractNums.get(instance.abstractNumId) ?? null;
71
+ }
72
+ bulletLevel(level) {
73
+ const el = this.doc.createElementNS(ODF_NS.TEXT, 'text:list-level-style-bullet');
74
+ el.setAttributeNS(ODF_NS.TEXT, 'text:level', String(level + 1));
75
+ el.setAttributeNS(ODF_NS.TEXT, 'text:bullet-char', DEFAULT_BULLET_CHAR);
76
+ return el;
77
+ }
78
+ numberLevel(level, def) {
79
+ const el = this.doc.createElementNS(ODF_NS.TEXT, 'text:list-level-style-number');
80
+ el.setAttributeNS(ODF_NS.TEXT, 'text:level', String(level + 1));
81
+ el.setAttributeNS(ODF_NS.STYLE, 'style:num-format', NUM_FORMAT_MAP[def?.numFmt ?? 'decimal'] ?? '1');
82
+ // The literal after the last `%N` placeholder (e.g. `%1.` → `.`, `%1)` → `)`).
83
+ const suffix = def ? /%\d+([^%]*)$/.exec(def.lvlText)?.[1] ?? '' : '.';
84
+ if (suffix)
85
+ el.setAttributeNS(ODF_NS.STYLE, 'style:num-suffix', suffix);
86
+ if (def && def.start !== 1)
87
+ el.setAttributeNS(ODF_NS.TEXT, 'text:start-value', String(def.start));
88
+ return el;
89
+ }
90
+ }
91
+ /**
92
+ * Builds one contiguous run of list items as nested `text:list` DOM under `parent`.
93
+ * `item()` returns the `text:p` to fill with the node's inline content.
94
+ */
95
+ export class ListDomBuilder {
96
+ doc;
97
+ parent;
98
+ stack = [];
99
+ constructor(doc, parent) {
100
+ this.doc = doc;
101
+ this.parent = parent;
102
+ }
103
+ item(level, styleName, paragraphStyleName = 'Standard') {
104
+ const lvl = Math.max(0, level);
105
+ while (this.stack.length > 0 && this.stack[this.stack.length - 1].level > lvl) {
106
+ this.stack.pop();
107
+ }
108
+ let top = this.stack[this.stack.length - 1];
109
+ if (!top || top.level !== lvl) {
110
+ // Deeper than the open top (or the first item): open ONE nested list recording the
111
+ // item's actual level, so a >1 jump still nests a single step.
112
+ const list = this.doc.createElementNS(ODF_NS.TEXT, 'text:list');
113
+ list.setAttributeNS(ODF_NS.TEXT, 'text:style-name', styleName);
114
+ if (!top) {
115
+ this.parent.appendChild(list);
116
+ }
117
+ else {
118
+ // Nested lists live inside a list-item; reuse the last one or create a holder.
119
+ let host = top.list.lastChild;
120
+ if (!host || host.localName !== 'list-item') {
121
+ host = this.doc.createElementNS(ODF_NS.TEXT, 'text:list-item');
122
+ top.list.appendChild(host);
123
+ }
124
+ host.appendChild(list);
125
+ }
126
+ this.stack.push({ list, level: lvl });
127
+ top = this.stack[this.stack.length - 1];
128
+ }
129
+ const item = this.doc.createElementNS(ODF_NS.TEXT, 'text:list-item');
130
+ top.list.appendChild(item);
131
+ const p = this.doc.createElementNS(ODF_NS.TEXT, 'text:p');
132
+ p.setAttributeNS(ODF_NS.TEXT, 'text:style-name', paragraphStyleName);
133
+ item.appendChild(p);
134
+ return p;
135
+ }
136
+ }
137
+ //# sourceMappingURL=lists.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lists.js","sourceRoot":"","sources":["../../src/convert/lists.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Fresh-package scaffolding for the DOCX → ODT converter: XML part templates and the
3
+ * whitespace writer that is the emit-side mirror of `shared/odf/text_segments.ts`.
4
+ *
5
+ * The `office:version="1.3"` attribute on every document root is required — LibreOffice
6
+ * tolerates its absence, but strict ODF validators reject the package without it.
7
+ */
8
+ import { type StyleRunFormatting, type StylesModel } from '@usejunior/docx-core';
9
+ /** Quote a font family for `svg:font-family` when it contains non-name characters (spaces). */
10
+ export declare function quoteFontFamily(face: string): string;
11
+ /** The skeleton `content.xml` DOM plus the insertion points the converter fills. */
12
+ export interface ContentScaffold {
13
+ doc: Document;
14
+ fontFaceDecls: Element;
15
+ automaticStyles: Element;
16
+ body: Element;
17
+ }
18
+ /** Parse the empty content.xml skeleton and hand back its insertion points. */
19
+ export declare function createContentScaffold(): ContentScaffold;
20
+ /** Source-derived named-style formatting feeding {@link buildStylesXml}. */
21
+ export interface SourceNamedStyles {
22
+ /** Resolved run formatting per heading level 1..6 (absent levels keep template defaults). */
23
+ headings: Map<number, StyleRunFormatting>;
24
+ /** Resolved run formatting of the source `Normal` style, or null when undefined. */
25
+ normal: StyleRunFormatting | null;
26
+ }
27
+ /**
28
+ * Resolve the source styles the converted `styles.xml` is seeded from: `Heading1..6` (matched
29
+ * by styleId or by the canonical `heading N` style name) and `Normal`. Properties a chain
30
+ * never specifies stay `null` so the template defaults survive.
31
+ */
32
+ export declare function deriveSourceNamedStyles(styles: StylesModel): SourceNamedStyles;
33
+ /**
34
+ * Seed `styles.xml`: `Standard`, a bold `Heading` base, `Heading_20_1..6` (descending sizes,
35
+ * `style:default-outline-level`), and `Text_20_body`. When `source` carries the document's
36
+ * resolved style-chain formatting, heading sizes/weights/colors/fonts and the `Standard` body
37
+ * font come from the source instead of the fixed template (#406 phase 3); properties the
38
+ * source never specifies keep the template defaults.
39
+ */
40
+ export declare function buildStylesXml(source?: SourceNamedStyles): string;
41
+ /** Build `meta.xml` carrying the generator string and optional title. */
42
+ export declare function buildMetaXml(metadata?: {
43
+ title?: string;
44
+ generator?: string;
45
+ }): string;
46
+ /**
47
+ * Append visible text to `parent`, encoding whitespace the way ODF readers decode it
48
+ * (the writer mirror of `buildSegments`): a run of N≥2 spaces becomes one literal space +
49
+ * `<text:s text:c="N-1"/>`, tabs become `text:tab`, newlines become `text:line-break`.
50
+ * A leading space is encoded as `text:s` outright — ODF processors collapse literal
51
+ * leading whitespace.
52
+ */
53
+ export declare function appendTextWithWhitespace(doc: Document, parent: Element, text: string): void;
54
+ //# sourceMappingURL=package.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.d.ts","sourceRoot":"","sources":["../../src/convert/package.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAuC,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAItH,+FAA+F;AAC/F,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAsBD,oFAAoF;AACpF,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,QAAQ,CAAC;IACd,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,IAAI,eAAe,CAMvD;AAED,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAChC,6FAA6F;IAC7F,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC1C,oFAAoF;IACpF,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACnC;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,WAAW,GAAG,iBAAiB,CAe9E;AA+BD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,MAAM,CA2DjE;AAMD,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,QAAQ,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAiBtF;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAwC3F"}
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Fresh-package scaffolding for the DOCX → ODT converter: XML part templates and the
3
+ * whitespace writer that is the emit-side mirror of `shared/odf/text_segments.ts`.
4
+ *
5
+ * The `office:version="1.3"` attribute on every document root is required — LibreOffice
6
+ * tolerates its absence, but strict ODF validators reject the package without it.
7
+ */
8
+ import { parseXml, extractStyleRunFormatting } from '@usejunior/docx-core';
9
+ import { ODF_NS } from '../shared/odf/namespaces.js';
10
+ /** Quote a font family for `svg:font-family` when it contains non-name characters (spaces). */
11
+ export function quoteFontFamily(face) {
12
+ return /^[A-Za-z0-9-]+$/.test(face) ? face : `'${face.replaceAll("'", '')}'`;
13
+ }
14
+ /** Template heading font sizes (pt) for `Heading_20_1` … `Heading_20_6` (source may override). */
15
+ const HEADING_SIZES_PT = [18, 16, 14, 13, 12, 11];
16
+ const CONTENT_SKELETON = [
17
+ '<?xml version="1.0" encoding="UTF-8"?>',
18
+ '<office:document-content',
19
+ ` xmlns:office="${ODF_NS.OFFICE}"`,
20
+ ` xmlns:text="${ODF_NS.TEXT}"`,
21
+ ` xmlns:table="${ODF_NS.TABLE}"`,
22
+ ` xmlns:style="${ODF_NS.STYLE}"`,
23
+ ` xmlns:fo="${ODF_NS.FO}"`,
24
+ ` xmlns:xlink="${ODF_NS.XLINK}"`,
25
+ ` xmlns:svg="${ODF_NS.SVG}"`,
26
+ ' office:version="1.3">',
27
+ ' <office:font-face-decls/>',
28
+ ' <office:automatic-styles/>',
29
+ ' <office:body><office:text/></office:body>',
30
+ '</office:document-content>',
31
+ ].join('\n');
32
+ /** Parse the empty content.xml skeleton and hand back its insertion points. */
33
+ export function createContentScaffold() {
34
+ const doc = parseXml(CONTENT_SKELETON);
35
+ const fontFaceDecls = doc.getElementsByTagNameNS(ODF_NS.OFFICE, 'font-face-decls')[0];
36
+ const automaticStyles = doc.getElementsByTagNameNS(ODF_NS.OFFICE, 'automatic-styles')[0];
37
+ const body = doc.getElementsByTagNameNS(ODF_NS.OFFICE, 'text')[0];
38
+ return { doc, fontFaceDecls, automaticStyles, body };
39
+ }
40
+ /**
41
+ * Resolve the source styles the converted `styles.xml` is seeded from: `Heading1..6` (matched
42
+ * by styleId or by the canonical `heading N` style name) and `Normal`. Properties a chain
43
+ * never specifies stay `null` so the template defaults survive.
44
+ */
45
+ export function deriveSourceNamedStyles(styles) {
46
+ const headingIdByLevel = new Map();
47
+ let normalId = null;
48
+ for (const [id, def] of styles.byId) {
49
+ const byId = /^Heading([1-6])$/.exec(id);
50
+ const byName = /^heading ([1-6])$/i.exec(def.name);
51
+ const level = byId ? Number(byId[1]) : byName ? Number(byName[1]) : null;
52
+ if (level !== null && !headingIdByLevel.has(level))
53
+ headingIdByLevel.set(level, id);
54
+ if (normalId === null && (id === 'Normal' || /^normal$/i.test(def.name)))
55
+ normalId = id;
56
+ }
57
+ const headings = new Map();
58
+ for (const [level, id] of headingIdByLevel) {
59
+ headings.set(level, extractStyleRunFormatting(styles, id));
60
+ }
61
+ return { headings, normal: normalId ? extractStyleRunFormatting(styles, normalId) : null };
62
+ }
63
+ function escapeXmlAttr(value) {
64
+ return value
65
+ .replace(/&/g, '&amp;')
66
+ .replace(/</g, '&lt;')
67
+ .replace(/>/g, '&gt;')
68
+ .replace(/"/g, '&quot;');
69
+ }
70
+ /**
71
+ * `style:text-properties` attributes for a source-derived named style. Only properties the
72
+ * source chain specifies are emitted; `null` lets the template's own defaults (or the parent
73
+ * style) win. `collectFace` records faces needing a `style:font-face` declaration.
74
+ */
75
+ function textPropertiesAttrs(fmt, collectFace) {
76
+ if (!fmt)
77
+ return [];
78
+ const attrs = [];
79
+ if (fmt.fontSizePt !== null && fmt.fontSizePt > 0)
80
+ attrs.push(`fo:font-size="${fmt.fontSizePt}pt"`);
81
+ if (fmt.bold !== null)
82
+ attrs.push(`fo:font-weight="${fmt.bold ? 'bold' : 'normal'}"`);
83
+ if (fmt.italic !== null)
84
+ attrs.push(`fo:font-style="${fmt.italic ? 'italic' : 'normal'}"`);
85
+ if (fmt.colorHex !== null && /^[0-9A-Fa-f]{6}$/.test(fmt.colorHex)) {
86
+ attrs.push(`fo:color="#${fmt.colorHex.toLowerCase()}"`);
87
+ }
88
+ if (fmt.fontName !== null && fmt.fontName.trim() !== '') {
89
+ collectFace(fmt.fontName);
90
+ attrs.push(`style:font-name="${escapeXmlAttr(fmt.fontName)}"`);
91
+ }
92
+ return attrs;
93
+ }
94
+ /**
95
+ * Seed `styles.xml`: `Standard`, a bold `Heading` base, `Heading_20_1..6` (descending sizes,
96
+ * `style:default-outline-level`), and `Text_20_body`. When `source` carries the document's
97
+ * resolved style-chain formatting, heading sizes/weights/colors/fonts and the `Standard` body
98
+ * font come from the source instead of the fixed template (#406 phase 3); properties the
99
+ * source never specifies keep the template defaults.
100
+ */
101
+ export function buildStylesXml(source) {
102
+ const faces = new Set();
103
+ const collectFace = (face) => { faces.add(face); };
104
+ const headingStyles = HEADING_SIZES_PT.map((templateSize, i) => {
105
+ const level = i + 1;
106
+ const sourceAttrs = textPropertiesAttrs(source?.headings.get(level) ?? null, collectFace);
107
+ const attrs = sourceAttrs.some((a) => a.startsWith('fo:font-size='))
108
+ ? sourceAttrs
109
+ : [`fo:font-size="${templateSize}pt"`, ...sourceAttrs];
110
+ return [
111
+ ` <style:style style:name="Heading_20_${level}" style:display-name="Heading ${level}"`,
112
+ ` style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body"`,
113
+ ` style:default-outline-level="${level}" style:class="text">`,
114
+ ` <style:text-properties ${attrs.join(' ')}/>`,
115
+ ' </style:style>',
116
+ ].join('\n');
117
+ });
118
+ const standardAttrs = textPropertiesAttrs(source?.normal ?? null, collectFace);
119
+ const standardStyle = standardAttrs.length > 0
120
+ ? [
121
+ ' <style:style style:name="Standard" style:family="paragraph" style:class="text">',
122
+ ` <style:text-properties ${standardAttrs.join(' ')}/>`,
123
+ ' </style:style>',
124
+ ].join('\n')
125
+ : ' <style:style style:name="Standard" style:family="paragraph" style:class="text"/>';
126
+ const fontFaceDecls = faces.size > 0
127
+ ? [
128
+ ' <office:font-face-decls>',
129
+ ...Array.from(faces, (face) => ` <style:font-face style:name="${escapeXmlAttr(face)}" svg:font-family="${escapeXmlAttr(quoteFontFamily(face))}"/>`),
130
+ ' </office:font-face-decls>',
131
+ ]
132
+ : [];
133
+ return [
134
+ '<?xml version="1.0" encoding="UTF-8"?>',
135
+ '<office:document-styles',
136
+ ` xmlns:office="${ODF_NS.OFFICE}"`,
137
+ ` xmlns:style="${ODF_NS.STYLE}"`,
138
+ ` xmlns:fo="${ODF_NS.FO}"`,
139
+ ` xmlns:svg="${ODF_NS.SVG}"`,
140
+ ' office:version="1.3">',
141
+ ...fontFaceDecls,
142
+ ' <office:styles>',
143
+ standardStyle,
144
+ ' <style:style style:name="Text_20_body" style:display-name="Text body"',
145
+ ' style:family="paragraph" style:parent-style-name="Standard" style:class="text"/>',
146
+ ' <style:style style:name="Heading" style:family="paragraph"',
147
+ ' style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">',
148
+ ' <style:text-properties fo:font-weight="bold"/>',
149
+ ' </style:style>',
150
+ ...headingStyles,
151
+ ' </office:styles>',
152
+ '</office:document-styles>',
153
+ '',
154
+ ].join('\n');
155
+ }
156
+ function escapeXmlText(value) {
157
+ return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
158
+ }
159
+ /** Build `meta.xml` carrying the generator string and optional title. */
160
+ export function buildMetaXml(metadata) {
161
+ const generator = metadata?.generator ?? '@usejunior/odf-core convertDocxToOdt';
162
+ const title = metadata?.title;
163
+ return [
164
+ '<?xml version="1.0" encoding="UTF-8"?>',
165
+ '<office:document-meta',
166
+ ` xmlns:office="${ODF_NS.OFFICE}"`,
167
+ ` xmlns:meta="${ODF_NS.META}"`,
168
+ ` xmlns:dc="${ODF_NS.DC}"`,
169
+ ' office:version="1.3">',
170
+ ' <office:meta>',
171
+ ` <meta:generator>${escapeXmlText(generator)}</meta:generator>`,
172
+ ...(title ? [` <dc:title>${escapeXmlText(title)}</dc:title>`] : []),
173
+ ' </office:meta>',
174
+ '</office:document-meta>',
175
+ '',
176
+ ].join('\n');
177
+ }
178
+ /**
179
+ * Append visible text to `parent`, encoding whitespace the way ODF readers decode it
180
+ * (the writer mirror of `buildSegments`): a run of N≥2 spaces becomes one literal space +
181
+ * `<text:s text:c="N-1"/>`, tabs become `text:tab`, newlines become `text:line-break`.
182
+ * A leading space is encoded as `text:s` outright — ODF processors collapse literal
183
+ * leading whitespace.
184
+ */
185
+ export function appendTextWithWhitespace(doc, parent, text) {
186
+ if (text.length === 0)
187
+ return;
188
+ const isAtBlockStart = parent.firstChild === null;
189
+ let i = 0;
190
+ while (i < text.length) {
191
+ const ch = text[i];
192
+ if (ch === '\t') {
193
+ parent.appendChild(doc.createElementNS(ODF_NS.TEXT, 'text:tab'));
194
+ i += 1;
195
+ continue;
196
+ }
197
+ if (ch === '\n') {
198
+ parent.appendChild(doc.createElementNS(ODF_NS.TEXT, 'text:line-break'));
199
+ i += 1;
200
+ continue;
201
+ }
202
+ if (ch === ' ') {
203
+ let n = 1;
204
+ while (text[i + n] === ' ')
205
+ n += 1;
206
+ const leading = isAtBlockStart && i === 0 && parent.firstChild === null;
207
+ if (leading) {
208
+ const s = doc.createElementNS(ODF_NS.TEXT, 'text:s');
209
+ if (n > 1)
210
+ s.setAttributeNS(ODF_NS.TEXT, 'text:c', String(n));
211
+ parent.appendChild(s);
212
+ }
213
+ else {
214
+ parent.appendChild(doc.createTextNode(' '));
215
+ if (n > 1) {
216
+ const s = doc.createElementNS(ODF_NS.TEXT, 'text:s');
217
+ if (n > 2)
218
+ s.setAttributeNS(ODF_NS.TEXT, 'text:c', String(n - 1));
219
+ parent.appendChild(s);
220
+ }
221
+ }
222
+ i += n;
223
+ continue;
224
+ }
225
+ let end = i;
226
+ while (end < text.length && text[end] !== ' ' && text[end] !== '\t' && text[end] !== '\n')
227
+ end += 1;
228
+ parent.appendChild(doc.createTextNode(text.slice(i, end)));
229
+ i = end;
230
+ }
231
+ }
232
+ //# sourceMappingURL=package.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.js","sourceRoot":"","sources":["../../src/convert/package.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Paragraph-level automatic styles for the DOCX → ODT converter: the view's
3
+ * `paragraph_alignment` + `paragraph_indents_pt` → deduped `P<n>` styles carrying
4
+ * `fo:text-align` / `fo:margin-left` / `fo:text-indent` (#406 phase 3).
5
+ *
6
+ * Styles are only created when something deviates from the named parent's defaults
7
+ * (non-LEFT alignment or a non-zero indent); plain paragraphs keep the parent name directly.
8
+ * List items request alignment only — `text:list` nesting already supplies indentation, and
9
+ * re-applying `fo:margin-left` would double-indent.
10
+ */
11
+ import type { DocumentViewNode } from '@usejunior/docx-core';
12
+ export declare class ParagraphStyleRegistry {
13
+ private readonly doc;
14
+ private readonly container;
15
+ private byKey;
16
+ constructor(doc: Document, container: Element);
17
+ /**
18
+ * Style name for a paragraph with `parentStyle` and the node's alignment/indents.
19
+ * Returns `parentStyle` itself when nothing deviates.
20
+ */
21
+ styleFor(parentStyle: string, node: DocumentViewNode, opts?: {
22
+ indents?: boolean;
23
+ }): string;
24
+ }
25
+ //# sourceMappingURL=paragraph_styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paragraph_styles.d.ts","sourceRoot":"","sources":["../../src/convert/paragraph_styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAgB7D,qBAAa,sBAAsB;IAI/B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAJ5B,OAAO,CAAC,KAAK,CAA6B;gBAGvB,GAAG,EAAE,QAAQ,EACb,SAAS,EAAE,OAAO;IAGrC;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM;CAyB5F"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Paragraph-level automatic styles for the DOCX → ODT converter: the view's
3
+ * `paragraph_alignment` + `paragraph_indents_pt` → deduped `P<n>` styles carrying
4
+ * `fo:text-align` / `fo:margin-left` / `fo:text-indent` (#406 phase 3).
5
+ *
6
+ * Styles are only created when something deviates from the named parent's defaults
7
+ * (non-LEFT alignment or a non-zero indent); plain paragraphs keep the parent name directly.
8
+ * List items request alignment only — `text:list` nesting already supplies indentation, and
9
+ * re-applying `fo:margin-left` would double-indent.
10
+ */
11
+ import { ODF_NS } from '../shared/odf/namespaces.js';
12
+ /** OOXML `ParagraphAlignment` → ODF `fo:text-align`. LEFT is the default and emits nothing. */
13
+ const TEXT_ALIGN_MAP = {
14
+ CENTER: 'center',
15
+ RIGHT: 'end',
16
+ JUSTIFY: 'justify',
17
+ };
18
+ /** Format points for style attributes: round to 2 decimals, trim trailing zeros. */
19
+ function fmtPt(v) {
20
+ return `${Number(v.toFixed(2))}pt`;
21
+ }
22
+ export class ParagraphStyleRegistry {
23
+ doc;
24
+ container;
25
+ byKey = new Map();
26
+ constructor(doc, container) {
27
+ this.doc = doc;
28
+ this.container = container;
29
+ }
30
+ /**
31
+ * Style name for a paragraph with `parentStyle` and the node's alignment/indents.
32
+ * Returns `parentStyle` itself when nothing deviates.
33
+ */
34
+ styleFor(parentStyle, node, opts) {
35
+ const includeIndents = opts?.indents ?? true;
36
+ const align = TEXT_ALIGN_MAP[node.paragraph_alignment] ?? null;
37
+ const left = includeIndents ? node.paragraph_indents_pt.left : 0;
38
+ const firstLine = includeIndents ? node.paragraph_indents_pt.first_line : 0;
39
+ if (align === null && left === 0 && firstLine === 0)
40
+ return parentStyle;
41
+ const key = `${parentStyle}|${align ?? ''}|${left}|${firstLine}`;
42
+ const existing = this.byKey.get(key);
43
+ if (existing)
44
+ return existing;
45
+ const name = `P${this.byKey.size + 1}`;
46
+ const style = this.doc.createElementNS(ODF_NS.STYLE, 'style:style');
47
+ style.setAttributeNS(ODF_NS.STYLE, 'style:name', name);
48
+ style.setAttributeNS(ODF_NS.STYLE, 'style:family', 'paragraph');
49
+ style.setAttributeNS(ODF_NS.STYLE, 'style:parent-style-name', parentStyle);
50
+ const props = this.doc.createElementNS(ODF_NS.STYLE, 'style:paragraph-properties');
51
+ if (align !== null)
52
+ props.setAttributeNS(ODF_NS.FO, 'fo:text-align', align);
53
+ if (left !== 0)
54
+ props.setAttributeNS(ODF_NS.FO, 'fo:margin-left', fmtPt(left));
55
+ if (firstLine !== 0)
56
+ props.setAttributeNS(ODF_NS.FO, 'fo:text-indent', fmtPt(firstLine));
57
+ style.appendChild(props);
58
+ this.container.appendChild(style);
59
+ this.byKey.set(key, name);
60
+ return name;
61
+ }
62
+ }
63
+ //# sourceMappingURL=paragraph_styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paragraph_styles.js","sourceRoot":"","sources":["../../src/convert/paragraph_styles.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Table emission for the DOCX → ODT converter: a contiguous run of view nodes sharing one
3
+ * `table_context.table_id` → a complete rectangular `table:table` grid.
4
+ *
5
+ * Lossy by design, mirroring the HTML serializer's `renderTable`: the view model discards
6
+ * `gridSpan`/`vMerge`, so merged cells are indistinguishable from genuinely empty grid
7
+ * positions — gaps are filled with empty cells (recorded as `table-grid-gaps-filled`).
8
+ * Borders and column widths come from the raw source `w:tbl` (#406 phase 3): an explicit
9
+ * `w:tblBorders` is honored uniformly per table (including explicitly borderless tables) and
10
+ * `w:tblGrid` widths become `table:table-column` styles. Tables without explicit borders keep
11
+ * the 0.5pt default (most Word tables are bordered via their table style, whose `w:tblPr`
12
+ * the styles model does not carry). Per-cell `w:tcBorders` overrides are out of scope.
13
+ */
14
+ import { type DocumentViewNode } from '@usejunior/docx-core';
15
+ import type { LossinessCollector } from './types.js';
16
+ /**
17
+ * Resolve the uniform `fo:border` for a table from its explicit `w:tblBorders`, preferring
18
+ * inside edges (what most cells show). Explicitly border-free tables return `'none'`; tables
19
+ * without a `w:tblBorders` at all return the bordered default.
20
+ */
21
+ export declare function resolveTableBorderSpec(tbl: Element | null): string;
22
+ /** Column widths in points from `w:tblGrid/w:gridCol` (`w:w` is twips); empty when absent. */
23
+ export declare function readGridColWidthsPt(tbl: Element | null): number[];
24
+ /**
25
+ * Deduped automatic styles for table emission: one `table-cell` style per distinct border
26
+ * spec and one `table-column` style per distinct width, shared across tables.
27
+ */
28
+ export declare class TableStyleRegistry {
29
+ private readonly doc;
30
+ private readonly container;
31
+ private cellByBorder;
32
+ private columnByWidth;
33
+ constructor(doc: Document, container: Element);
34
+ cellStyleFor(borderSpec: string): string;
35
+ columnStyleFor(widthPt: number): string;
36
+ }
37
+ /**
38
+ * Append the table built from `group` to `body`. Cell paragraph content is delegated to
39
+ * `fillParagraph` (the orchestrator's inline emitter) and cell paragraph styles to
40
+ * `paragraphStyleFor` so this module stays cycle-free. `sourceTbl` is the raw `w:tbl` this
41
+ * group was derived from (null when unavailable — defaults apply).
42
+ */
43
+ export declare function appendTable(doc: Document, body: Element, group: DocumentViewNode[], tableNumber: number, sourceTbl: Element | null, tableStyles: TableStyleRegistry, fillParagraph: (p: Element, node: DocumentViewNode) => void, paragraphStyleFor: (node: DocumentViewNode) => string, lossiness: LossinessCollector): void;
44
+ //# sourceMappingURL=tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../src/convert/tables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAQ,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAGnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA4BrD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,GAAG,MAAM,CAqBlE;AAED,8FAA8F;AAC9F,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,GAAG,MAAM,EAAE,CAYjE;AAED;;;GAGG;AACH,qBAAa,kBAAkB;IAK3B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAL5B,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,aAAa,CAA6B;gBAG/B,GAAG,EAAE,QAAQ,EACb,SAAS,EAAE,OAAO;IAGrC,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAgBxC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;CAexC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,gBAAgB,EAAE,EACzB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,OAAO,GAAG,IAAI,EACzB,WAAW,EAAE,kBAAkB,EAC/B,aAAa,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,KAAK,IAAI,EAC3D,iBAAiB,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,MAAM,EACrD,SAAS,EAAE,kBAAkB,GAC5B,IAAI,CAoFN"}