@usejunior/odf-core 0.9.1 → 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 (56) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/compare/emit.d.ts +8 -3
  5. package/dist/compare/emit.d.ts.map +1 -1
  6. package/dist/compare/emit.js +39 -7
  7. package/dist/compare/emit.js.map +1 -1
  8. package/dist/compare/inline_diff.d.ts +5 -0
  9. package/dist/compare/inline_diff.d.ts.map +1 -1
  10. package/dist/compare/inline_diff.js +71 -1
  11. package/dist/compare/inline_diff.js.map +1 -1
  12. package/dist/convert/docx_to_odt.d.ts +20 -0
  13. package/dist/convert/docx_to_odt.d.ts.map +1 -0
  14. package/dist/convert/docx_to_odt.js +199 -0
  15. package/dist/convert/docx_to_odt.js.map +1 -0
  16. package/dist/convert/index.d.ts +3 -0
  17. package/dist/convert/index.d.ts.map +1 -0
  18. package/dist/convert/index.js +2 -0
  19. package/dist/convert/index.js.map +1 -0
  20. package/dist/convert/inline.d.ts +55 -0
  21. package/dist/convert/inline.d.ts.map +1 -0
  22. package/dist/convert/inline.js +274 -0
  23. package/dist/convert/inline.js.map +1 -0
  24. package/dist/convert/lists.d.ts +41 -0
  25. package/dist/convert/lists.d.ts.map +1 -0
  26. package/dist/convert/lists.js +137 -0
  27. package/dist/convert/lists.js.map +1 -0
  28. package/dist/convert/package.d.ts +54 -0
  29. package/dist/convert/package.d.ts.map +1 -0
  30. package/dist/convert/package.js +232 -0
  31. package/dist/convert/package.js.map +1 -0
  32. package/dist/convert/paragraph_styles.d.ts +25 -0
  33. package/dist/convert/paragraph_styles.d.ts.map +1 -0
  34. package/dist/convert/paragraph_styles.js +63 -0
  35. package/dist/convert/paragraph_styles.js.map +1 -0
  36. package/dist/convert/tables.d.ts +44 -0
  37. package/dist/convert/tables.d.ts.map +1 -0
  38. package/dist/convert/tables.js +226 -0
  39. package/dist/convert/tables.js.map +1 -0
  40. package/dist/convert/types.d.ts +27 -0
  41. package/dist/convert/types.d.ts.map +1 -0
  42. package/dist/convert/types.js +25 -0
  43. package/dist/convert/types.js.map +1 -0
  44. package/dist/index.d.ts +1 -0
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +1 -0
  47. package/dist/index.js.map +1 -1
  48. package/dist/shared/odf/OdfArchive.d.ts +16 -0
  49. package/dist/shared/odf/OdfArchive.d.ts.map +1 -1
  50. package/dist/shared/odf/OdfArchive.js +48 -1
  51. package/dist/shared/odf/OdfArchive.js.map +1 -1
  52. package/dist/shared/odf/namespaces.d.ts +4 -0
  53. package/dist/shared/odf/namespaces.d.ts.map +1 -1
  54. package/dist/shared/odf/namespaces.js +8 -0
  55. package/dist/shared/odf/namespaces.js.map +1 -1
  56. package/package.json +5 -4
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Inline content emission for the DOCX → ODT converter: `tagged_text` TOON tokens →
3
+ * `text:span` / `text:a` DOM, backed by a deduped automatic text-style registry.
4
+ *
5
+ * The token grammar is owned by docx-core (`tokenizeToonInline`, the same primitive the
6
+ * markdown/HTML serializers consume) so this module never re-derives it. Supported wraps are
7
+ * bold / italic / underline / highlight (with its full-mode source color) and the full-mode
8
+ * `<font color size face>` tag, mapped to `fo:color` / `fo:font-size` / `style:font-name`
9
+ * automatic styles (#406 phase 3). Unsafe hyperlink schemes degrade to plain text via the
10
+ * shared `isSafeHref`.
11
+ */
12
+ import { isSafeHref, tokenizeToonInline } from '@usejunior/docx-core';
13
+ import { ODF_NS } from '../shared/odf/namespaces.js';
14
+ import { appendTextWithWhitespace, quoteFontFamily } from './package.js';
15
+ /**
16
+ * OOXML `ST_HighlightColor` enum values (`w:highlight`) → the hex Word renders them as.
17
+ * The TOON highlight tag carries the source enum value in full mode; anything unknown
18
+ * degrades to yellow with a lossiness entry rather than dropping the highlight.
19
+ */
20
+ const HIGHLIGHT_COLOR_HEX = {
21
+ yellow: '#ffff00',
22
+ green: '#00ff00',
23
+ cyan: '#00ffff',
24
+ magenta: '#ff00ff',
25
+ blue: '#0000ff',
26
+ red: '#ff0000',
27
+ darkBlue: '#00008b',
28
+ darkCyan: '#008b8b',
29
+ darkGreen: '#006400',
30
+ darkMagenta: '#8b008b',
31
+ darkRed: '#8b0000',
32
+ darkYellow: '#808000',
33
+ darkGray: '#a9a9a9',
34
+ lightGray: '#d3d3d3',
35
+ black: '#000000',
36
+ white: '#ffffff',
37
+ };
38
+ const DEFAULT_HIGHLIGHT_HEX = HIGHLIGHT_COLOR_HEX['yellow'];
39
+ /**
40
+ * Deduped `office:font-face-decls` registry: one `style:font-face` per distinct face name
41
+ * used anywhere in the document (ODF resolves `style:font-name` against declared faces).
42
+ */
43
+ export class FontFaceRegistry {
44
+ doc;
45
+ container;
46
+ declared = new Set();
47
+ constructor(doc, container) {
48
+ this.doc = doc;
49
+ this.container = container;
50
+ }
51
+ ensure(face) {
52
+ if (this.declared.has(face))
53
+ return;
54
+ this.declared.add(face);
55
+ const decl = this.doc.createElementNS(ODF_NS.STYLE, 'style:font-face');
56
+ decl.setAttributeNS(ODF_NS.STYLE, 'style:name', face);
57
+ decl.setAttributeNS(ODF_NS.SVG, 'svg:font-family', quoteFontFamily(face));
58
+ this.container.appendChild(decl);
59
+ }
60
+ }
61
+ /**
62
+ * Deduped `office:automatic-styles` registry for run formatting: one `T<n>` text style per
63
+ * distinct format combination, shared across the whole document.
64
+ */
65
+ export class TextStyleRegistry {
66
+ doc;
67
+ container;
68
+ fontFaces;
69
+ byKey = new Map();
70
+ constructor(doc, container, fontFaces) {
71
+ this.doc = doc;
72
+ this.container = container;
73
+ this.fontFaces = fontFaces;
74
+ }
75
+ styleFor(formats) {
76
+ const key = [
77
+ formats.bold ? 'b' : '',
78
+ formats.italic ? 'i' : '',
79
+ formats.underline ? 'u' : '',
80
+ formats.highlight ?? '',
81
+ formats.fontColor ?? '',
82
+ formats.fontSizePt ?? '',
83
+ formats.fontFace ?? '',
84
+ ].join('|');
85
+ const existing = this.byKey.get(key);
86
+ if (existing)
87
+ return existing;
88
+ const name = `T${this.byKey.size + 1}`;
89
+ const style = this.doc.createElementNS(ODF_NS.STYLE, 'style:style');
90
+ style.setAttributeNS(ODF_NS.STYLE, 'style:name', name);
91
+ style.setAttributeNS(ODF_NS.STYLE, 'style:family', 'text');
92
+ const props = this.doc.createElementNS(ODF_NS.STYLE, 'style:text-properties');
93
+ if (formats.bold)
94
+ props.setAttributeNS(ODF_NS.FO, 'fo:font-weight', 'bold');
95
+ if (formats.italic)
96
+ props.setAttributeNS(ODF_NS.FO, 'fo:font-style', 'italic');
97
+ if (formats.underline) {
98
+ props.setAttributeNS(ODF_NS.STYLE, 'style:text-underline-style', 'solid');
99
+ props.setAttributeNS(ODF_NS.STYLE, 'style:text-underline-width', 'auto');
100
+ props.setAttributeNS(ODF_NS.STYLE, 'style:text-underline-color', 'font-color');
101
+ }
102
+ if (formats.highlight)
103
+ props.setAttributeNS(ODF_NS.FO, 'fo:background-color', formats.highlight);
104
+ if (formats.fontColor)
105
+ props.setAttributeNS(ODF_NS.FO, 'fo:color', formats.fontColor);
106
+ if (formats.fontSizePt !== null)
107
+ props.setAttributeNS(ODF_NS.FO, 'fo:font-size', `${formats.fontSizePt}pt`);
108
+ if (formats.fontFace) {
109
+ this.fontFaces.ensure(formats.fontFace);
110
+ props.setAttributeNS(ODF_NS.STYLE, 'style:font-name', formats.fontFace);
111
+ }
112
+ style.appendChild(props);
113
+ this.container.appendChild(style);
114
+ this.byKey.set(key, name);
115
+ return name;
116
+ }
117
+ }
118
+ /** Reverse of `formatting_tags.ts`'s `escapeHtmlAttribute` (`&amp;` last so it can't re-expand). */
119
+ function unescapeAttributeValue(value) {
120
+ return value
121
+ .replaceAll('&lt;', '<')
122
+ .replaceAll('&gt;', '>')
123
+ .replaceAll('&quot;', '"')
124
+ .replaceAll('&amp;', '&');
125
+ }
126
+ function attributeValue(tag, name) {
127
+ const match = new RegExp(`\\b${name}="([^"]*)"`).exec(tag);
128
+ return match ? unescapeAttributeValue(match[1]) : null;
129
+ }
130
+ /** Map a TOON highlight open tag to its ODF background hex. */
131
+ function highlightHexFor(tag, lossiness) {
132
+ const val = attributeValue(tag, 'color');
133
+ // Compact mode emits the value-less form; the historical normalization to yellow applies.
134
+ if (val === null)
135
+ return DEFAULT_HIGHLIGHT_HEX;
136
+ const hex = HIGHLIGHT_COLOR_HEX[val];
137
+ if (hex)
138
+ return hex;
139
+ lossiness.add('unknown-highlight-color', val);
140
+ return DEFAULT_HIGHLIGHT_HEX;
141
+ }
142
+ /**
143
+ * Append one `tagged_text` string's content to `parent` (a `text:p`/`text:h` or table-cell
144
+ * paragraph), wrapping formatted runs in `text:span` and hyperlinks in `text:a`.
145
+ */
146
+ export function appendInlineContent(doc, parent, taggedText, styles, lossiness) {
147
+ const formats = {
148
+ bold: false,
149
+ italic: false,
150
+ underline: false,
151
+ highlight: null,
152
+ fontColor: null,
153
+ fontSizePt: null,
154
+ fontFace: null,
155
+ };
156
+ let openAnchor = null;
157
+ // The currently open span and the format key it was created for: consecutive text tokens with
158
+ // an unchanged format set share one span instead of fragmenting into adjacent twins.
159
+ let openSpan = null;
160
+ const formatKey = () => [
161
+ formats.bold ? 'b' : '',
162
+ formats.italic ? 'i' : '',
163
+ formats.underline ? 'u' : '',
164
+ formats.highlight ?? '',
165
+ formats.fontColor ?? '',
166
+ formats.fontSizePt ?? '',
167
+ formats.fontFace ?? '',
168
+ ].join('|');
169
+ const EMPTY_KEY = '||||||';
170
+ for (const token of tokenizeToonInline(taggedText)) {
171
+ if (token.kind === 'text') {
172
+ const container = openAnchor ?? parent;
173
+ const key = formatKey();
174
+ if (key === EMPTY_KEY) {
175
+ openSpan = null;
176
+ appendTextWithWhitespace(doc, container, token.value);
177
+ continue;
178
+ }
179
+ if (!openSpan || openSpan.key !== key || openSpan.el.parentNode !== container) {
180
+ const span = doc.createElementNS(ODF_NS.TEXT, 'text:span');
181
+ span.setAttributeNS(ODF_NS.TEXT, 'text:style-name', styles.styleFor(formats));
182
+ container.appendChild(span);
183
+ openSpan = { el: span, key };
184
+ }
185
+ appendTextWithWhitespace(doc, openSpan.el, token.value);
186
+ continue;
187
+ }
188
+ const tag = token.value;
189
+ if (tag === '<b>') {
190
+ formats.bold = true;
191
+ openSpan = null;
192
+ }
193
+ else if (tag === '</b>') {
194
+ formats.bold = false;
195
+ openSpan = null;
196
+ }
197
+ else if (tag === '<i>') {
198
+ formats.italic = true;
199
+ openSpan = null;
200
+ }
201
+ else if (tag === '</i>') {
202
+ formats.italic = false;
203
+ openSpan = null;
204
+ }
205
+ else if (tag === '<u>') {
206
+ formats.underline = true;
207
+ openSpan = null;
208
+ }
209
+ else if (tag === '</u>') {
210
+ formats.underline = false;
211
+ openSpan = null;
212
+ }
213
+ else if (tag === '<highlight>' || tag.startsWith('<highlight ')) {
214
+ formats.highlight = highlightHexFor(tag, lossiness);
215
+ openSpan = null;
216
+ }
217
+ else if (tag === '</highlight>') {
218
+ formats.highlight = null;
219
+ openSpan = null;
220
+ }
221
+ else if (tag.startsWith('<a ')) {
222
+ const escapedHref = /href="([^"]*)"/.exec(tag)?.[1] ?? '';
223
+ // The TOON attribute value is escaped by emitFormattingTags; setAttributeNS escapes
224
+ // again on serialize, so assign the DECODED value or `&amp;` doubles.
225
+ const href = unescapeAttributeValue(escapedHref);
226
+ if (isSafeHref(href)) {
227
+ const anchor = doc.createElementNS(ODF_NS.TEXT, 'text:a');
228
+ anchor.setAttributeNS(ODF_NS.XLINK, 'xlink:type', 'simple');
229
+ anchor.setAttributeNS(ODF_NS.XLINK, 'xlink:href', href);
230
+ parent.appendChild(anchor);
231
+ openAnchor = anchor;
232
+ }
233
+ else {
234
+ lossiness.add('unsafe-hyperlink-href', href);
235
+ openAnchor = null;
236
+ }
237
+ openSpan = null;
238
+ }
239
+ else if (tag === '</a>') {
240
+ openAnchor = null;
241
+ openSpan = null;
242
+ }
243
+ else if (tag.startsWith('<font ')) {
244
+ // Full-mode font runs: color is the raw w:color hex (no '#'), size is points, face is
245
+ // the font name. A malformed color degrades to the default color, reported.
246
+ const color = attributeValue(tag, 'color');
247
+ if (color !== null) {
248
+ if (/^[0-9A-Fa-f]{6}$/.test(color)) {
249
+ formats.fontColor = `#${color.toLowerCase()}`;
250
+ }
251
+ else {
252
+ lossiness.add('unmappable-font-color', color);
253
+ formats.fontColor = null;
254
+ }
255
+ }
256
+ else {
257
+ formats.fontColor = null;
258
+ }
259
+ const size = attributeValue(tag, 'size');
260
+ const sizePt = size !== null ? Number(size) : NaN;
261
+ formats.fontSizePt = Number.isFinite(sizePt) && sizePt > 0 ? sizePt : null;
262
+ const face = attributeValue(tag, 'face');
263
+ formats.fontFace = face !== null && face.trim() !== '' ? face : null;
264
+ openSpan = null;
265
+ }
266
+ else if (tag === '</font>') {
267
+ formats.fontColor = null;
268
+ formats.fontSizePt = null;
269
+ formats.fontFace = null;
270
+ openSpan = null;
271
+ }
272
+ }
273
+ }
274
+ //# sourceMappingURL=inline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline.js","sourceRoot":"","sources":["../../src/convert/inline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
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 type { NumberingModel } from '@usejunior/docx-core';
11
+ /**
12
+ * Deduped `office:automatic-styles` registry for lists: one `L<n>` list style per source
13
+ * `num_id` (or per fallback kind when the numbering model has no entry), with one
14
+ * `text:list-level-style-*` per level sourced from the OOXML numbering definition.
15
+ */
16
+ export declare class ListStyleRegistry {
17
+ private readonly doc;
18
+ private readonly container;
19
+ private readonly numbering;
20
+ private byKey;
21
+ constructor(doc: Document, container: Element, numbering: NumberingModel | null);
22
+ /** Style name for a list opened by a node with the given `num_id` (bullet hint for model-less docs). */
23
+ styleFor(numId: string | null, bulletHint: boolean): string;
24
+ /** True when the level's source `numFmt` is `bullet` (used to pick the fallback hint). */
25
+ isBulletLevel(numId: string | null, ilvl: number | null): boolean;
26
+ private abstractFor;
27
+ private bulletLevel;
28
+ private numberLevel;
29
+ }
30
+ /**
31
+ * Builds one contiguous run of list items as nested `text:list` DOM under `parent`.
32
+ * `item()` returns the `text:p` to fill with the node's inline content.
33
+ */
34
+ export declare class ListDomBuilder {
35
+ private readonly doc;
36
+ private readonly parent;
37
+ private stack;
38
+ constructor(doc: Document, parent: Element);
39
+ item(level: number, styleName: string, paragraphStyleName?: string): Element;
40
+ }
41
+ //# sourceMappingURL=lists.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lists.d.ts","sourceRoot":"","sources":["../../src/convert/lists.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAgB3D;;;;GAIG;AACH,qBAAa,iBAAiB;IAI1B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAL5B,OAAO,CAAC,KAAK,CAA6B;gBAGvB,GAAG,EAAE,QAAQ,EACb,SAAS,EAAE,OAAO,EAClB,SAAS,EAAE,cAAc,GAAG,IAAI;IAGnD,wGAAwG;IACxG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM;IAsB3D,0FAA0F;IAC1F,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO;IAKjE,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,WAAW;CAUpB;AAED;;;GAGG;AACH,qBAAa,cAAc;IAIvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,OAAO,CAAC,KAAK,CAA+C;gBAGzC,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,OAAO;IAGlC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,kBAAkB,SAAa,GAAG,OAAO;CAgCjF"}
@@ -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"}