@usejunior/odf-core 0.10.0 → 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.
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/convert/docx_to_odt.d.ts +20 -0
- package/dist/convert/docx_to_odt.d.ts.map +1 -0
- package/dist/convert/docx_to_odt.js +199 -0
- package/dist/convert/docx_to_odt.js.map +1 -0
- package/dist/convert/index.d.ts +3 -0
- package/dist/convert/index.d.ts.map +1 -0
- package/dist/convert/index.js +2 -0
- package/dist/convert/index.js.map +1 -0
- package/dist/convert/inline.d.ts +55 -0
- package/dist/convert/inline.d.ts.map +1 -0
- package/dist/convert/inline.js +274 -0
- package/dist/convert/inline.js.map +1 -0
- package/dist/convert/lists.d.ts +41 -0
- package/dist/convert/lists.d.ts.map +1 -0
- package/dist/convert/lists.js +137 -0
- package/dist/convert/lists.js.map +1 -0
- package/dist/convert/package.d.ts +54 -0
- package/dist/convert/package.d.ts.map +1 -0
- package/dist/convert/package.js +232 -0
- package/dist/convert/package.js.map +1 -0
- package/dist/convert/paragraph_styles.d.ts +25 -0
- package/dist/convert/paragraph_styles.d.ts.map +1 -0
- package/dist/convert/paragraph_styles.js +63 -0
- package/dist/convert/paragraph_styles.js.map +1 -0
- package/dist/convert/tables.d.ts +44 -0
- package/dist/convert/tables.d.ts.map +1 -0
- package/dist/convert/tables.js +226 -0
- package/dist/convert/tables.js.map +1 -0
- package/dist/convert/types.d.ts +27 -0
- package/dist/convert/types.d.ts.map +1 -0
- package/dist/convert/types.js +25 -0
- package/dist/convert/types.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/shared/odf/OdfArchive.d.ts +16 -0
- package/dist/shared/odf/OdfArchive.d.ts.map +1 -1
- package/dist/shared/odf/OdfArchive.js +48 -1
- package/dist/shared/odf/OdfArchive.js.map +1 -1
- package/dist/shared/odf/namespaces.d.ts +4 -0
- package/dist/shared/odf/namespaces.d.ts.map +1 -1
- package/dist/shared/odf/namespaces.js +8 -0
- package/dist/shared/odf/namespaces.js.map +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native DOCX → ODT conversion (issues #331, #406).
|
|
3
|
+
*
|
|
4
|
+
* Traverses docx-core's structured document view — the same intentionally-lossy semantic
|
|
5
|
+
* model the markdown/HTML serializers consume — and emits a fresh ODT package. No external
|
|
6
|
+
* binary is involved at runtime; LibreOffice exists only as a differential test oracle.
|
|
7
|
+
*
|
|
8
|
+
* `formattingMode: 'full'` is required (not the `'compact'` default): compact mode encodes
|
|
9
|
+
* the document's dominant formatting as a modal baseline rather than per-run tags, which
|
|
10
|
+
* would silently drop bold/italic/underline on mostly-bold documents.
|
|
11
|
+
*
|
|
12
|
+
* Two narrow raw-DOM supplements cover what the view cannot carry: table borders/grid widths
|
|
13
|
+
* (read from the source `w:tbl` by the view's `table_index`) and text-empty body paragraphs
|
|
14
|
+
* (vertical spacing the view never surfaces, preserved as empty `text:p` by bookmark
|
|
15
|
+
* correlation).
|
|
16
|
+
*/
|
|
17
|
+
import { DocxDocument, getParagraphBookmarkId, getParagraphText, serializeXml, W_NS, } from '@usejunior/docx-core';
|
|
18
|
+
import { OdfArchive } from '../shared/odf/OdfArchive.js';
|
|
19
|
+
import { ODF_NS } from '../shared/odf/namespaces.js';
|
|
20
|
+
import { appendInlineContent, FontFaceRegistry, TextStyleRegistry } from './inline.js';
|
|
21
|
+
import { ListDomBuilder, ListStyleRegistry } from './lists.js';
|
|
22
|
+
import { appendTable, TableStyleRegistry } from './tables.js';
|
|
23
|
+
import { ParagraphStyleRegistry } from './paragraph_styles.js';
|
|
24
|
+
import { appendTextWithWhitespace, buildMetaXml, buildStylesXml, createContentScaffold, deriveSourceNamedStyles, } from './package.js';
|
|
25
|
+
import { LossinessCollector } from './types.js';
|
|
26
|
+
/** A heading is structural only when Word's style said so — heuristic headings stay paragraphs. */
|
|
27
|
+
function isStructuralHeading(node) {
|
|
28
|
+
return node.heading?.source === 'word_style' && typeof node.heading.level === 'number';
|
|
29
|
+
}
|
|
30
|
+
/** True when the paragraph element sits inside a table cell (any depth up to the body). */
|
|
31
|
+
function isInsideTableCell(p) {
|
|
32
|
+
let current = p.parentNode;
|
|
33
|
+
while (current && current.nodeType === 1) {
|
|
34
|
+
const el = current;
|
|
35
|
+
if (el.namespaceURI === W_NS && el.localName === 'body')
|
|
36
|
+
return false;
|
|
37
|
+
if (el.namespaceURI === W_NS && el.localName === 'tc')
|
|
38
|
+
return true;
|
|
39
|
+
current = el.parentNode;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Text-empty body-level paragraphs are never surfaced by the document view (it only yields
|
|
45
|
+
* bookmarked paragraphs with content) but they are vertical spacing in the source. Correlate
|
|
46
|
+
* `getParagraphs()` order with surfaced node ids via the bookmarks `normalize()` installed:
|
|
47
|
+
* each unsurfaced empty body paragraph is preserved before its nearest following surfaced
|
|
48
|
+
* node (`emptyBefore`) or at the end (`trailingEmpty`); in-table ones stay reported.
|
|
49
|
+
*/
|
|
50
|
+
function planUnsurfacedParagraphs(source, nodes, lossiness) {
|
|
51
|
+
const surfacedIds = new Set(nodes.map((n) => n.id));
|
|
52
|
+
const emptyBefore = new Map();
|
|
53
|
+
let pending = 0;
|
|
54
|
+
for (const p of source.getParagraphs()) {
|
|
55
|
+
const id = getParagraphBookmarkId(p);
|
|
56
|
+
if (id && surfacedIds.has(id)) {
|
|
57
|
+
if (pending > 0)
|
|
58
|
+
emptyBefore.set(id, pending);
|
|
59
|
+
pending = 0;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (getParagraphText(p).trim() !== '') {
|
|
63
|
+
// Unsurfaced despite content (e.g. exotic containers) — same drop as before, reported.
|
|
64
|
+
lossiness.add('unsurfaced-paragraphs-dropped', 'non-empty paragraph not surfaced by the document view');
|
|
65
|
+
}
|
|
66
|
+
else if (isInsideTableCell(p)) {
|
|
67
|
+
// Cell-internal spacing needs cell-level positioning the grid emitter does not model.
|
|
68
|
+
lossiness.add('unsurfaced-table-paragraphs-dropped', 'text-empty paragraph inside a table cell');
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
pending += 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { emptyBefore, trailingEmpty: pending };
|
|
75
|
+
}
|
|
76
|
+
/** The direct `w:tbl` children of `w:body`, in order — the view's `table_index` space. */
|
|
77
|
+
function bodyLevelTables(source) {
|
|
78
|
+
const docXml = source.getDocumentXmlClone();
|
|
79
|
+
const body = docXml.getElementsByTagNameNS(W_NS, 'body').item(0);
|
|
80
|
+
if (!body)
|
|
81
|
+
return [];
|
|
82
|
+
const tables = [];
|
|
83
|
+
for (let i = 0; i < body.childNodes.length; i++) {
|
|
84
|
+
const child = body.childNodes[i];
|
|
85
|
+
if (child.nodeType === 1 && child.localName === 'tbl' && child.namespaceURI === W_NS) {
|
|
86
|
+
tables.push(child);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return tables;
|
|
90
|
+
}
|
|
91
|
+
/** Convert a `.docx` buffer to a fresh `.odt` package plus a lossiness report. */
|
|
92
|
+
export async function convertDocxToOdt(docx, options) {
|
|
93
|
+
const source = await DocxDocument.load(docx);
|
|
94
|
+
// The document view only yields nodes for `_bk_`-bookmarked paragraphs, and bookmarks are
|
|
95
|
+
// normally injected per MCP session — a raw `.docx` has none. Prime the loaded copy the
|
|
96
|
+
// same way the session manager does (the input buffer is never written back).
|
|
97
|
+
source.normalize();
|
|
98
|
+
source.insertParagraphBookmarks('_convert');
|
|
99
|
+
const { nodes } = source.buildDocumentView({ showFormatting: true, formattingMode: 'full' });
|
|
100
|
+
const numbering = source.getNumberingModel();
|
|
101
|
+
const lossiness = new LossinessCollector();
|
|
102
|
+
const { emptyBefore, trailingEmpty } = planUnsurfacedParagraphs(source, nodes, lossiness);
|
|
103
|
+
const sourceTables = bodyLevelTables(source);
|
|
104
|
+
const { doc, fontFaceDecls, automaticStyles, body } = createContentScaffold();
|
|
105
|
+
const fontFaces = new FontFaceRegistry(doc, fontFaceDecls);
|
|
106
|
+
const textStyles = new TextStyleRegistry(doc, automaticStyles, fontFaces);
|
|
107
|
+
const paragraphStyles = new ParagraphStyleRegistry(doc, automaticStyles);
|
|
108
|
+
const listStyles = new ListStyleRegistry(doc, automaticStyles, numbering);
|
|
109
|
+
const tableStyles = new TableStyleRegistry(doc, automaticStyles);
|
|
110
|
+
const fillParagraph = (p, node) => {
|
|
111
|
+
appendInlineContent(doc, p, node.tagged_text, textStyles, lossiness);
|
|
112
|
+
};
|
|
113
|
+
const newParagraph = (styleName) => {
|
|
114
|
+
const p = doc.createElementNS(ODF_NS.TEXT, 'text:p');
|
|
115
|
+
p.setAttributeNS(ODF_NS.TEXT, 'text:style-name', styleName);
|
|
116
|
+
body.appendChild(p);
|
|
117
|
+
return p;
|
|
118
|
+
};
|
|
119
|
+
const appendEmptyParagraphs = (count) => {
|
|
120
|
+
for (let n = 0; n < count; n++)
|
|
121
|
+
newParagraph('Standard');
|
|
122
|
+
};
|
|
123
|
+
let listBuilder = null;
|
|
124
|
+
let tableCount = 0;
|
|
125
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
126
|
+
const node = nodes[i];
|
|
127
|
+
// ── Preserved vertical spacing: unsurfaced empty body paragraphs before this node ──
|
|
128
|
+
const emptyCount = emptyBefore.get(node.id) ?? 0;
|
|
129
|
+
if (emptyCount > 0) {
|
|
130
|
+
listBuilder = null;
|
|
131
|
+
appendEmptyParagraphs(emptyCount);
|
|
132
|
+
}
|
|
133
|
+
// ── Tables: consume the whole run of same-table_id nodes at once ──
|
|
134
|
+
if (node.table_context) {
|
|
135
|
+
listBuilder = null;
|
|
136
|
+
const tableId = node.table_context.table_id;
|
|
137
|
+
const sourceTbl = sourceTables[node.table_context.table_index] ?? null;
|
|
138
|
+
const group = [];
|
|
139
|
+
while (i < nodes.length && nodes[i].table_context?.table_id === tableId) {
|
|
140
|
+
group.push(nodes[i]);
|
|
141
|
+
i++;
|
|
142
|
+
}
|
|
143
|
+
i--; // for-loop re-increments
|
|
144
|
+
tableCount += 1;
|
|
145
|
+
appendTable(doc, body, group, tableCount, sourceTbl, tableStyles, fillParagraph, (n) => paragraphStyles.styleFor('Standard', n), lossiness);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
// ── Word-styled headings ──
|
|
149
|
+
if (isStructuralHeading(node)) {
|
|
150
|
+
listBuilder = null;
|
|
151
|
+
const level = Math.min(6, Math.max(1, node.heading.level));
|
|
152
|
+
const h = doc.createElementNS(ODF_NS.TEXT, 'text:h');
|
|
153
|
+
h.setAttributeNS(ODF_NS.TEXT, 'text:outline-level', String(level));
|
|
154
|
+
h.setAttributeNS(ODF_NS.TEXT, 'text:style-name', paragraphStyles.styleFor(`Heading_20_${level}`, node));
|
|
155
|
+
body.appendChild(h);
|
|
156
|
+
fillParagraph(h, node);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (node.list_metadata.list_level >= 0) {
|
|
160
|
+
// ── Auto-numbered / bullet items (any numPr): nested text:list ──
|
|
161
|
+
if (node.list_metadata.is_auto_numbered) {
|
|
162
|
+
const numId = node.numbering.num_id;
|
|
163
|
+
// Bullet vs number comes from the numbering model's numFmt — `LabelType` has no
|
|
164
|
+
// bullet member, so the label classification can't signal it.
|
|
165
|
+
const bulletHint = listStyles.isBulletLevel(numId, node.numbering.ilvl);
|
|
166
|
+
const styleName = listStyles.styleFor(numId, bulletHint);
|
|
167
|
+
if (!listBuilder)
|
|
168
|
+
listBuilder = new ListDomBuilder(doc, body);
|
|
169
|
+
// Alignment only: the nested text:list supplies indentation, and re-applying the
|
|
170
|
+
// source margins would double-indent the item.
|
|
171
|
+
const p = listBuilder.item(node.list_metadata.list_level, styleName, paragraphStyles.styleFor('Standard', node, { indents: false }));
|
|
172
|
+
fillParagraph(p, node);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
// ── Manual/legal labels (`Section 2.1`, `(a)`): literal paragraph text, NO text:list.
|
|
176
|
+
// Deliberate divergence from the HTML serializer's <ul><li> wrapping — an ODF list
|
|
177
|
+
// renderer would print its own number next to the legal label. ──
|
|
178
|
+
listBuilder = null;
|
|
179
|
+
const p = newParagraph(paragraphStyles.styleFor('Standard', node));
|
|
180
|
+
const label = node.list_metadata.label_string.trim();
|
|
181
|
+
if (label)
|
|
182
|
+
appendTextWithWhitespace(doc, p, `${label} `);
|
|
183
|
+
fillParagraph(p, node);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
// ── Normal paragraphs (heuristic headings land here; empty paragraphs are kept) ──
|
|
187
|
+
listBuilder = null;
|
|
188
|
+
fillParagraph(newParagraph(paragraphStyles.styleFor('Standard', node)), node);
|
|
189
|
+
}
|
|
190
|
+
appendEmptyParagraphs(trailingEmpty);
|
|
191
|
+
const archive = OdfArchive.create({
|
|
192
|
+
contentXml: serializeXml(doc),
|
|
193
|
+
stylesXml: buildStylesXml(deriveSourceNamedStyles(source.getStylesModel())),
|
|
194
|
+
metaXml: buildMetaXml(options?.metadata),
|
|
195
|
+
});
|
|
196
|
+
const odt = await archive.save();
|
|
197
|
+
return { odt, lossiness: lossiness.toArray() };
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=docx_to_odt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docx_to_odt.js","sourceRoot":"","sources":["../../src/convert/docx_to_odt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/convert/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/convert/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
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 type { LossinessCollector } from './types.js';
|
|
13
|
+
interface InlineFormats {
|
|
14
|
+
bold: boolean;
|
|
15
|
+
italic: boolean;
|
|
16
|
+
underline: boolean;
|
|
17
|
+
/** ODF `fo:background-color` hex (e.g. `#00ff00`), or null when unhighlighted. */
|
|
18
|
+
highlight: string | null;
|
|
19
|
+
/** ODF `fo:color` hex (e.g. `#ff0000`), or null for the default color. */
|
|
20
|
+
fontColor: string | null;
|
|
21
|
+
/** Font size in points as carried by the TOON tag (may be fractional), or null. */
|
|
22
|
+
fontSizePt: number | null;
|
|
23
|
+
/** Font face name, or null for the default face. */
|
|
24
|
+
fontFace: string | null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Deduped `office:font-face-decls` registry: one `style:font-face` per distinct face name
|
|
28
|
+
* used anywhere in the document (ODF resolves `style:font-name` against declared faces).
|
|
29
|
+
*/
|
|
30
|
+
export declare class FontFaceRegistry {
|
|
31
|
+
private readonly doc;
|
|
32
|
+
private readonly container;
|
|
33
|
+
private declared;
|
|
34
|
+
constructor(doc: Document, container: Element);
|
|
35
|
+
ensure(face: string): void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Deduped `office:automatic-styles` registry for run formatting: one `T<n>` text style per
|
|
39
|
+
* distinct format combination, shared across the whole document.
|
|
40
|
+
*/
|
|
41
|
+
export declare class TextStyleRegistry {
|
|
42
|
+
private readonly doc;
|
|
43
|
+
private readonly container;
|
|
44
|
+
private readonly fontFaces;
|
|
45
|
+
private byKey;
|
|
46
|
+
constructor(doc: Document, container: Element, fontFaces: FontFaceRegistry);
|
|
47
|
+
styleFor(formats: InlineFormats): string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Append one `tagged_text` string's content to `parent` (a `text:p`/`text:h` or table-cell
|
|
51
|
+
* paragraph), wrapping formatted runs in `text:span` and hyperlinks in `text:a`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function appendInlineContent(doc: Document, parent: Element, taggedText: string, styles: TextStyleRegistry, lossiness: LossinessCollector): void;
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=inline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../../src/convert/inline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA4BrD,UAAU,aAAa;IACrB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,kFAAkF;IAClF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mFAAmF;IACnF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oDAAoD;IACpD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAIzB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAJ5B,OAAO,CAAC,QAAQ,CAAqB;gBAGlB,GAAG,EAAE,QAAQ,EACb,SAAS,EAAE,OAAO;IAGrC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAQ3B;AAED;;;GAGG;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,gBAAgB;IAG9C,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM;CAqCzC;AA2BD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,OAAO,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,kBAAkB,GAC5B,IAAI,CAwGN"}
|
|
@@ -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` (`&` last so it can't re-expand). */
|
|
119
|
+
function unescapeAttributeValue(value) {
|
|
120
|
+
return value
|
|
121
|
+
.replaceAll('<', '<')
|
|
122
|
+
.replaceAll('>', '>')
|
|
123
|
+
.replaceAll('"', '"')
|
|
124
|
+
.replaceAll('&', '&');
|
|
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 `&` 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"}
|