odf-kit 0.7.0 → 0.8.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/dist/typst/emitter.d.ts +120 -0
- package/dist/typst/emitter.d.ts.map +1 -0
- package/dist/typst/emitter.js +458 -0
- package/dist/typst/emitter.js.map +1 -0
- package/dist/typst/index.d.ts +26 -0
- package/dist/typst/index.d.ts.map +1 -0
- package/dist/typst/index.js +25 -0
- package/dist/typst/index.js.map +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typst emitter for the ODT document model.
|
|
3
|
+
*
|
|
4
|
+
* Converts the structured document model produced by the ODT parser into
|
|
5
|
+
* a Typst markup string (.typ). Zero runtime dependencies — the emitter is
|
|
6
|
+
* a pure function over OdtDocumentModel and produces output suitable for
|
|
7
|
+
* any Typst 0.11+ installation:
|
|
8
|
+
*
|
|
9
|
+
* typst compile document.typ document.pdf
|
|
10
|
+
*
|
|
11
|
+
* Structural coverage:
|
|
12
|
+
* - Headings levels 1–6 (= through ======)
|
|
13
|
+
* - Paragraphs with text-align via #align()
|
|
14
|
+
* - Bold, italic, underline, strikethrough, superscript, subscript
|
|
15
|
+
* - Hyperlinks via #link()
|
|
16
|
+
* - Footnotes and endnotes via #footnote[]
|
|
17
|
+
* - Bookmarks as Typst labels (<name>) for point and start positions
|
|
18
|
+
* - Text fields: pageNumber and pageCount mapped to Typst counters;
|
|
19
|
+
* all others rendered as their stored evaluated value
|
|
20
|
+
* - Unordered and ordered lists with nested sub-lists
|
|
21
|
+
* - Tables via #table() with column widths where available
|
|
22
|
+
* - Named sections with comment headers
|
|
23
|
+
* - Tracked changes: final (default), original, and changes modes
|
|
24
|
+
*
|
|
25
|
+
* Tier 2 character style coverage (SpanStyle):
|
|
26
|
+
* - fontColor → #text(fill: rgb("..."))[]
|
|
27
|
+
* - fontSize → #text(size: Npt)[]
|
|
28
|
+
* - fontFamily → #text(font: "...")[]
|
|
29
|
+
* - highlightColor → #highlight(fill: rgb("..."))[]
|
|
30
|
+
*
|
|
31
|
+
* Page geometry (PageLayout) → #set page(width:, height:, margin: (...))
|
|
32
|
+
*
|
|
33
|
+
* Images: base64 image data cannot be embedded inline in Typst without
|
|
34
|
+
* filesystem access. Each image is emitted as a comment placeholder
|
|
35
|
+
* preserving its document position:
|
|
36
|
+
*
|
|
37
|
+
* // [image: name 17cm × 5.74cm]
|
|
38
|
+
*
|
|
39
|
+
* Consumers that need images in the PDF should extract ImageNode.data
|
|
40
|
+
* (base64) from the model, write the files alongside the .typ output,
|
|
41
|
+
* and substitute the placeholders with #image("filename") calls.
|
|
42
|
+
*
|
|
43
|
+
* Text content is escaped for Typst markup mode. The following characters
|
|
44
|
+
* are prefixed with a backslash: \ * _ # < @ ` ~ $ [ ]
|
|
45
|
+
* A leading = on any line is also escaped to prevent heading misinterpretation.
|
|
46
|
+
*/
|
|
47
|
+
import type { OdtDocumentModel } from "../reader/types.js";
|
|
48
|
+
import type { ReadOdtOptions } from "../reader/types.js";
|
|
49
|
+
/**
|
|
50
|
+
* Options for the Typst emitter.
|
|
51
|
+
*/
|
|
52
|
+
export interface TypstEmitOptions {
|
|
53
|
+
/**
|
|
54
|
+
* Controls how tracked changes are emitted. Mirrors ReadOdtOptions.trackedChanges.
|
|
55
|
+
*
|
|
56
|
+
* "final" (default): TrackedChangeNode values render as their insertion body;
|
|
57
|
+
* deletions produce no output. Matches the "final" parse mode behavior.
|
|
58
|
+
* "original": TrackedChangeNode values render as their deletion body;
|
|
59
|
+
* insertions produce no output. Matches the "original" parse mode behavior.
|
|
60
|
+
* "changes": insertions → #underline[], deletions → #strike[],
|
|
61
|
+
* format-change → body content only with no wrapper.
|
|
62
|
+
*
|
|
63
|
+
* Set readOdt and modelToTypst to the same mode for consistent results.
|
|
64
|
+
* When readOdt is called with "final" (default), no TrackedChangeNode values
|
|
65
|
+
* are emitted by the parser and this option has no effect.
|
|
66
|
+
*/
|
|
67
|
+
trackedChanges?: "final" | "original" | "changes";
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Convert an OdtDocumentModel to a Typst markup string.
|
|
71
|
+
*
|
|
72
|
+
* This is the primary emitter function. It accepts a pre-parsed document
|
|
73
|
+
* model and returns a .typ string with no side effects and no filesystem
|
|
74
|
+
* access. Use this when you already have a model from readOdt() or when
|
|
75
|
+
* you need fine-grained control over read options.
|
|
76
|
+
*
|
|
77
|
+
* @param model - The parsed ODT document model from readOdt().
|
|
78
|
+
* @param options - Typst emitter options.
|
|
79
|
+
* @returns Typst markup string (.typ).
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* import { readOdt } from "odf-kit/reader";
|
|
84
|
+
* import { modelToTypst } from "odf-kit/typst";
|
|
85
|
+
* import { readFileSync, writeFileSync } from "node:fs";
|
|
86
|
+
*
|
|
87
|
+
* const bytes = new Uint8Array(readFileSync("document.odt"));
|
|
88
|
+
* const model = readOdt(bytes);
|
|
89
|
+
* const typ = modelToTypst(model);
|
|
90
|
+
* writeFileSync("document.typ", typ);
|
|
91
|
+
* // then: typst compile document.typ document.pdf
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare function modelToTypst(model: OdtDocumentModel, options?: TypstEmitOptions): string;
|
|
95
|
+
/**
|
|
96
|
+
* Convert an .odt file directly to a Typst markup string.
|
|
97
|
+
*
|
|
98
|
+
* Convenience wrapper around readOdt() + modelToTypst(). Use modelToTypst()
|
|
99
|
+
* directly when you need access to the document model, metadata, or want to
|
|
100
|
+
* share a single readOdt() call between multiple emitters.
|
|
101
|
+
*
|
|
102
|
+
* @param bytes - The raw .odt file as a Uint8Array.
|
|
103
|
+
* @param options - Combined emitter and read options. The trackedChanges
|
|
104
|
+
* field is forwarded to both readOdt() and modelToTypst() — set it once
|
|
105
|
+
* for consistent results across both steps.
|
|
106
|
+
* @returns Typst markup string (.typ).
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* import { odtToTypst } from "odf-kit/typst";
|
|
111
|
+
* import { readFileSync, writeFileSync } from "node:fs";
|
|
112
|
+
*
|
|
113
|
+
* const bytes = new Uint8Array(readFileSync("document.odt"));
|
|
114
|
+
* const typ = odtToTypst(bytes);
|
|
115
|
+
* writeFileSync("document.typ", typ);
|
|
116
|
+
* // then: typst compile document.typ document.pdf
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export declare function odtToTypst(bytes: Uint8Array, options?: TypstEmitOptions & ReadOdtOptions): string;
|
|
120
|
+
//# sourceMappingURL=emitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/typst/emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAajB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAMzD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;CACnD;AAkXD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAUxF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,cAAc,GAAG,MAAM,CAGjG"}
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typst emitter for the ODT document model.
|
|
3
|
+
*
|
|
4
|
+
* Converts the structured document model produced by the ODT parser into
|
|
5
|
+
* a Typst markup string (.typ). Zero runtime dependencies — the emitter is
|
|
6
|
+
* a pure function over OdtDocumentModel and produces output suitable for
|
|
7
|
+
* any Typst 0.11+ installation:
|
|
8
|
+
*
|
|
9
|
+
* typst compile document.typ document.pdf
|
|
10
|
+
*
|
|
11
|
+
* Structural coverage:
|
|
12
|
+
* - Headings levels 1–6 (= through ======)
|
|
13
|
+
* - Paragraphs with text-align via #align()
|
|
14
|
+
* - Bold, italic, underline, strikethrough, superscript, subscript
|
|
15
|
+
* - Hyperlinks via #link()
|
|
16
|
+
* - Footnotes and endnotes via #footnote[]
|
|
17
|
+
* - Bookmarks as Typst labels (<name>) for point and start positions
|
|
18
|
+
* - Text fields: pageNumber and pageCount mapped to Typst counters;
|
|
19
|
+
* all others rendered as their stored evaluated value
|
|
20
|
+
* - Unordered and ordered lists with nested sub-lists
|
|
21
|
+
* - Tables via #table() with column widths where available
|
|
22
|
+
* - Named sections with comment headers
|
|
23
|
+
* - Tracked changes: final (default), original, and changes modes
|
|
24
|
+
*
|
|
25
|
+
* Tier 2 character style coverage (SpanStyle):
|
|
26
|
+
* - fontColor → #text(fill: rgb("..."))[]
|
|
27
|
+
* - fontSize → #text(size: Npt)[]
|
|
28
|
+
* - fontFamily → #text(font: "...")[]
|
|
29
|
+
* - highlightColor → #highlight(fill: rgb("..."))[]
|
|
30
|
+
*
|
|
31
|
+
* Page geometry (PageLayout) → #set page(width:, height:, margin: (...))
|
|
32
|
+
*
|
|
33
|
+
* Images: base64 image data cannot be embedded inline in Typst without
|
|
34
|
+
* filesystem access. Each image is emitted as a comment placeholder
|
|
35
|
+
* preserving its document position:
|
|
36
|
+
*
|
|
37
|
+
* // [image: name 17cm × 5.74cm]
|
|
38
|
+
*
|
|
39
|
+
* Consumers that need images in the PDF should extract ImageNode.data
|
|
40
|
+
* (base64) from the model, write the files alongside the .typ output,
|
|
41
|
+
* and substitute the placeholders with #image("filename") calls.
|
|
42
|
+
*
|
|
43
|
+
* Text content is escaped for Typst markup mode. The following characters
|
|
44
|
+
* are prefixed with a backslash: \ * _ # < @ ` ~ $ [ ]
|
|
45
|
+
* A leading = on any line is also escaped to prevent heading misinterpretation.
|
|
46
|
+
*/
|
|
47
|
+
import { readOdt } from "../reader/parser.js";
|
|
48
|
+
// ============================================================
|
|
49
|
+
// Typst escaping
|
|
50
|
+
// ============================================================
|
|
51
|
+
/** Characters with special meaning in Typst markup mode. */
|
|
52
|
+
const TYPST_ESCAPE_RE = /[\\*_#<@`~$[\]]/g;
|
|
53
|
+
/**
|
|
54
|
+
* Escape characters that carry special meaning in Typst markup mode.
|
|
55
|
+
*
|
|
56
|
+
* Each matched character is prefixed with a backslash. A leading = on any
|
|
57
|
+
* line is also escaped so text content is never misinterpreted as a heading.
|
|
58
|
+
*/
|
|
59
|
+
function escapeTypst(text) {
|
|
60
|
+
return text.replace(TYPST_ESCAPE_RE, (ch) => `\\${ch}`).replace(/^=/gm, "\\=");
|
|
61
|
+
}
|
|
62
|
+
// ============================================================
|
|
63
|
+
// Page setup
|
|
64
|
+
// ============================================================
|
|
65
|
+
/**
|
|
66
|
+
* Emit a #set page(...) directive from a PageLayout.
|
|
67
|
+
*
|
|
68
|
+
* Returns an empty string when pageLayout is absent so the caller can
|
|
69
|
+
* skip it without emitting a blank directive.
|
|
70
|
+
*/
|
|
71
|
+
function emitPageSetup(model) {
|
|
72
|
+
const layout = model.pageLayout;
|
|
73
|
+
if (layout === undefined)
|
|
74
|
+
return "";
|
|
75
|
+
const parts = [];
|
|
76
|
+
if (layout.width !== undefined)
|
|
77
|
+
parts.push(`width: ${layout.width}`);
|
|
78
|
+
if (layout.height !== undefined)
|
|
79
|
+
parts.push(`height: ${layout.height}`);
|
|
80
|
+
const marginParts = [];
|
|
81
|
+
if (layout.marginTop !== undefined)
|
|
82
|
+
marginParts.push(`top: ${layout.marginTop}`);
|
|
83
|
+
if (layout.marginBottom !== undefined)
|
|
84
|
+
marginParts.push(`bottom: ${layout.marginBottom}`);
|
|
85
|
+
if (layout.marginLeft !== undefined)
|
|
86
|
+
marginParts.push(`left: ${layout.marginLeft}`);
|
|
87
|
+
if (layout.marginRight !== undefined)
|
|
88
|
+
marginParts.push(`right: ${layout.marginRight}`);
|
|
89
|
+
if (marginParts.length > 0)
|
|
90
|
+
parts.push(`margin: (${marginParts.join(", ")})`);
|
|
91
|
+
if (parts.length === 0)
|
|
92
|
+
return "";
|
|
93
|
+
return `#set page(${parts.join(", ")})`;
|
|
94
|
+
}
|
|
95
|
+
// ============================================================
|
|
96
|
+
// Tier 2 — SpanStyle helper
|
|
97
|
+
// ============================================================
|
|
98
|
+
/**
|
|
99
|
+
* Wrap Typst content with #text() and #highlight() calls for any SpanStyle
|
|
100
|
+
* properties that have Typst equivalents.
|
|
101
|
+
*
|
|
102
|
+
* Applied after all semantic formatting (#underline, #strike, _italic_,
|
|
103
|
+
* *bold*) so SpanStyle is the outermost wrapper below the hyperlink anchor.
|
|
104
|
+
*
|
|
105
|
+
* Properties without a clean Typst mapping (textTransform, fontVariant,
|
|
106
|
+
* textShadow, letterSpacing) are intentionally omitted — they have no
|
|
107
|
+
* standard built-in Typst equivalent and faking them would produce incorrect
|
|
108
|
+
* output.
|
|
109
|
+
*/
|
|
110
|
+
function applySpanStyle(style, content) {
|
|
111
|
+
let out = content;
|
|
112
|
+
// highlight() wraps first so #text() sits outside it
|
|
113
|
+
if (style.highlightColor !== undefined) {
|
|
114
|
+
out = `#highlight(fill: rgb("${style.highlightColor}"))[${out}]`;
|
|
115
|
+
}
|
|
116
|
+
const textArgs = [];
|
|
117
|
+
if (style.fontColor !== undefined)
|
|
118
|
+
textArgs.push(`fill: rgb("${style.fontColor}")`);
|
|
119
|
+
if (style.fontSize !== undefined)
|
|
120
|
+
textArgs.push(`size: ${style.fontSize}pt`);
|
|
121
|
+
if (style.fontFamily !== undefined)
|
|
122
|
+
textArgs.push(`font: "${style.fontFamily}"`);
|
|
123
|
+
if (textArgs.length > 0) {
|
|
124
|
+
out = `#text(${textArgs.join(", ")})[${out}]`;
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
// ============================================================
|
|
129
|
+
// Inline node renderers
|
|
130
|
+
// ============================================================
|
|
131
|
+
/**
|
|
132
|
+
* Emit a TextSpan to a Typst markup string.
|
|
133
|
+
*
|
|
134
|
+
* Hidden spans (text:display="none") produce an empty string.
|
|
135
|
+
* Hard line breaks produce a Typst forced line break: \
|
|
136
|
+
*
|
|
137
|
+
* Formatting nesting order (innermost first, outermost last):
|
|
138
|
+
* superscript/subscript → strikethrough → underline → italic → bold
|
|
139
|
+
* → SpanStyle → hyperlink anchor.
|
|
140
|
+
*/
|
|
141
|
+
function emitTextSpan(span) {
|
|
142
|
+
if (span.lineBreak)
|
|
143
|
+
return "\\\n";
|
|
144
|
+
if (span.hidden)
|
|
145
|
+
return "";
|
|
146
|
+
let out = escapeTypst(span.text);
|
|
147
|
+
if (span.superscript)
|
|
148
|
+
out = `#super[${out}]`;
|
|
149
|
+
if (span.subscript)
|
|
150
|
+
out = `#sub[${out}]`;
|
|
151
|
+
if (span.strikethrough)
|
|
152
|
+
out = `#strike[${out}]`;
|
|
153
|
+
if (span.underline)
|
|
154
|
+
out = `#underline[${out}]`;
|
|
155
|
+
if (span.italic)
|
|
156
|
+
out = `_${out}_`;
|
|
157
|
+
if (span.bold)
|
|
158
|
+
out = `*${out}*`;
|
|
159
|
+
if (span.style !== undefined) {
|
|
160
|
+
out = applySpanStyle(span.style, out);
|
|
161
|
+
}
|
|
162
|
+
if (span.href !== undefined) {
|
|
163
|
+
out = `#link("${span.href}")[${out}]`;
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Emit an ImageNode as a comment placeholder.
|
|
169
|
+
*
|
|
170
|
+
* Typst does not support inline base64 image data without filesystem access.
|
|
171
|
+
* The placeholder preserves the image's document position and carries its
|
|
172
|
+
* name and dimensions so consumers can locate and substitute it.
|
|
173
|
+
*
|
|
174
|
+
* See module-level documentation for the recommended substitution workflow.
|
|
175
|
+
*/
|
|
176
|
+
function emitImage(node) {
|
|
177
|
+
const label = node.name ?? node.title ?? "image";
|
|
178
|
+
const dims = node.width !== undefined && node.height !== undefined
|
|
179
|
+
? ` ${node.width} \u00d7 ${node.height}`
|
|
180
|
+
: "";
|
|
181
|
+
return `/* [image: ${label}${dims}] */`;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Emit a NoteNode as a Typst #footnote[].
|
|
185
|
+
*
|
|
186
|
+
* Both footnote and endnote classes are emitted as #footnote[] — Typst does
|
|
187
|
+
* not have a built-in endnote construct. Consumers that need distinct endnote
|
|
188
|
+
* placement should post-process the .typ output.
|
|
189
|
+
*/
|
|
190
|
+
function emitNote(node, options) {
|
|
191
|
+
const body = emitBodyNodes(node.body, options);
|
|
192
|
+
return `#footnote[${body}]`;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Emit a BookmarkNode as a Typst label.
|
|
196
|
+
*
|
|
197
|
+
* point and start positions emit <name> — Typst's label syntax for
|
|
198
|
+
* cross-reference anchors. end positions produce no output (the span
|
|
199
|
+
* has already been closed implicitly).
|
|
200
|
+
*/
|
|
201
|
+
function emitBookmark(node) {
|
|
202
|
+
if (node.position === "end")
|
|
203
|
+
return "";
|
|
204
|
+
return `<${node.name}>`;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Emit a FieldNode to a Typst string.
|
|
208
|
+
*
|
|
209
|
+
* pageNumber and pageCount are mapped to their Typst counter equivalents.
|
|
210
|
+
* All other field types fall back to the stored evaluated value, which is
|
|
211
|
+
* always present per the ODF spec.
|
|
212
|
+
*/
|
|
213
|
+
function emitField(node) {
|
|
214
|
+
switch (node.fieldType) {
|
|
215
|
+
case "pageNumber":
|
|
216
|
+
return "#counter(page).display()";
|
|
217
|
+
case "pageCount":
|
|
218
|
+
return "#counter(page).final().first()";
|
|
219
|
+
default:
|
|
220
|
+
return escapeTypst(node.value);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Dispatch an InlineNode to the appropriate emitter.
|
|
225
|
+
*
|
|
226
|
+
* TextSpan has no `kind` property; all other InlineNode types do.
|
|
227
|
+
* This mirrors the narrowing pattern used in the HTML renderer.
|
|
228
|
+
*/
|
|
229
|
+
function emitInlineNode(node, options) {
|
|
230
|
+
if ("kind" in node) {
|
|
231
|
+
switch (node.kind) {
|
|
232
|
+
case "image":
|
|
233
|
+
return emitImage(node);
|
|
234
|
+
case "note":
|
|
235
|
+
return emitNote(node, options);
|
|
236
|
+
case "bookmark":
|
|
237
|
+
return emitBookmark(node);
|
|
238
|
+
case "field":
|
|
239
|
+
return emitField(node);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return emitTextSpan(node);
|
|
243
|
+
}
|
|
244
|
+
/** Emit an array of InlineNode objects to a concatenated Typst string. */
|
|
245
|
+
function emitSpans(spans, options) {
|
|
246
|
+
return spans.map((n) => emitInlineNode(n, options)).join("");
|
|
247
|
+
}
|
|
248
|
+
// ============================================================
|
|
249
|
+
// Block node renderers
|
|
250
|
+
// ============================================================
|
|
251
|
+
/**
|
|
252
|
+
* Map an ODF/CSS text-align value to the Typst alignment keyword.
|
|
253
|
+
*
|
|
254
|
+
* ODF spec values "start" and "end" are passed through — Typst supports them
|
|
255
|
+
* natively for bidirectional text. "justify" maps to Typst's "justify" keyword.
|
|
256
|
+
*/
|
|
257
|
+
function mapTextAlign(textAlign) {
|
|
258
|
+
switch (textAlign) {
|
|
259
|
+
case "left":
|
|
260
|
+
return "left";
|
|
261
|
+
case "right":
|
|
262
|
+
return "right";
|
|
263
|
+
case "center":
|
|
264
|
+
return "center";
|
|
265
|
+
case "justify":
|
|
266
|
+
return "justify";
|
|
267
|
+
case "start":
|
|
268
|
+
return "start";
|
|
269
|
+
case "end":
|
|
270
|
+
return "end";
|
|
271
|
+
default:
|
|
272
|
+
return "left";
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Emit a ListNode to a Typst list string.
|
|
277
|
+
*
|
|
278
|
+
* Unordered items use the - marker; ordered items use +. Nested sub-lists
|
|
279
|
+
* are indented with two spaces per level as required by the Typst parser.
|
|
280
|
+
*/
|
|
281
|
+
function emitList(list, options, depth = 0) {
|
|
282
|
+
const marker = list.ordered ? "+" : "-";
|
|
283
|
+
const indent = " ".repeat(depth);
|
|
284
|
+
return list.items
|
|
285
|
+
.map((item) => {
|
|
286
|
+
const content = emitSpans(item.spans, options);
|
|
287
|
+
const nested = item.children !== undefined ? "\n" + emitList(item.children, options, depth + 1) : "";
|
|
288
|
+
return `${indent}${marker} ${content}${nested}`;
|
|
289
|
+
})
|
|
290
|
+
.join("\n");
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Emit a TableNode as a Typst #table() call.
|
|
294
|
+
*
|
|
295
|
+
* Column widths are taken from the first row's CellStyle.columnWidth values
|
|
296
|
+
* when present, producing a columns: (Xcm, Ycm, ...) tuple. When no widths
|
|
297
|
+
* are available, columns: N (equal-width auto layout) is used instead.
|
|
298
|
+
*
|
|
299
|
+
* Cell content is emitted as Typst content blocks [...]. colspan and rowspan
|
|
300
|
+
* are not yet expressible in standard Typst table syntax and are silently
|
|
301
|
+
* ignored; the cell content is still emitted.
|
|
302
|
+
*/
|
|
303
|
+
function emitTable(table, options) {
|
|
304
|
+
if (table.rows.length === 0)
|
|
305
|
+
return "";
|
|
306
|
+
const cols = table.rows[0].cells.length;
|
|
307
|
+
const firstRow = table.rows[0];
|
|
308
|
+
const colWidths = firstRow.cells.map((cell) => cell.cellStyle?.columnWidth);
|
|
309
|
+
const hasWidths = colWidths.some((w) => w !== undefined);
|
|
310
|
+
const columnsArg = hasWidths
|
|
311
|
+
? `columns: (${colWidths.map((w) => w ?? "1fr").join(", ")})`
|
|
312
|
+
: `columns: ${cols}`;
|
|
313
|
+
const cells = table.rows
|
|
314
|
+
.flatMap((row) => row.cells.map((cell) => `[${emitSpans(cell.spans, options)}]`))
|
|
315
|
+
.join(", ");
|
|
316
|
+
return `#table(\n ${columnsArg},\n ${cells}\n)`;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Emit a SectionNode as a Typst comment header followed by its body content.
|
|
320
|
+
*
|
|
321
|
+
* Typst has no built-in section construct. The name is preserved as a comment
|
|
322
|
+
* so consumers can identify section boundaries in the .typ output.
|
|
323
|
+
*/
|
|
324
|
+
function emitSection(node, options) {
|
|
325
|
+
const header = node.name !== undefined ? `// Section: ${node.name}` : "// Section";
|
|
326
|
+
const body = emitBodyNodes(node.body, options);
|
|
327
|
+
return `${header}\n${body}`;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Emit a TrackedChangeNode.
|
|
331
|
+
*
|
|
332
|
+
* When TypstEmitOptions.trackedChanges is "changes":
|
|
333
|
+
* insertion → #underline[body]
|
|
334
|
+
* deletion → #strike[body]
|
|
335
|
+
* format-change → body content only (no formatting wrapper; style changed,
|
|
336
|
+
* not content)
|
|
337
|
+
*
|
|
338
|
+
* In other modes ("final", "original") the parser does not emit
|
|
339
|
+
* TrackedChangeNode values. If one is encountered anyway (e.g. the consumer
|
|
340
|
+
* constructed the model manually), it is rendered transparently as its body
|
|
341
|
+
* content with no annotation wrapper — matching the HTML renderer's behavior.
|
|
342
|
+
*/
|
|
343
|
+
function emitTrackedChange(node, options) {
|
|
344
|
+
const body = emitBodyNodes(node.body, options);
|
|
345
|
+
if (options?.trackedChanges !== "changes") {
|
|
346
|
+
return body;
|
|
347
|
+
}
|
|
348
|
+
switch (node.changeType) {
|
|
349
|
+
case "insertion":
|
|
350
|
+
return `#underline[${body}]`;
|
|
351
|
+
case "deletion":
|
|
352
|
+
return `#strike[${body}]`;
|
|
353
|
+
case "format-change":
|
|
354
|
+
return body;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/** Emit a single BodyNode to a Typst markup string. */
|
|
358
|
+
function emitBodyNode(node, options) {
|
|
359
|
+
switch (node.kind) {
|
|
360
|
+
case "paragraph": {
|
|
361
|
+
const content = emitSpans(node.spans, options);
|
|
362
|
+
if (node.paragraphStyle?.textAlign !== undefined) {
|
|
363
|
+
const align = mapTextAlign(node.paragraphStyle.textAlign);
|
|
364
|
+
return `#align(${align})[${content}]`;
|
|
365
|
+
}
|
|
366
|
+
return content;
|
|
367
|
+
}
|
|
368
|
+
case "heading": {
|
|
369
|
+
const prefix = "=".repeat(node.level);
|
|
370
|
+
const content = emitSpans(node.spans, options);
|
|
371
|
+
return `${prefix} ${content}`;
|
|
372
|
+
}
|
|
373
|
+
case "list":
|
|
374
|
+
return emitList(node, options);
|
|
375
|
+
case "table":
|
|
376
|
+
return emitTable(node, options);
|
|
377
|
+
case "section":
|
|
378
|
+
return emitSection(node, options);
|
|
379
|
+
case "tracked-change":
|
|
380
|
+
return emitTrackedChange(node, options);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Emit an array of BodyNode objects separated by blank lines.
|
|
385
|
+
*
|
|
386
|
+
* Blank-line separation is the standard Typst paragraph delimiter and
|
|
387
|
+
* ensures distinct blocks do not run together in the output.
|
|
388
|
+
*/
|
|
389
|
+
function emitBodyNodes(body, options) {
|
|
390
|
+
return body.map((n) => emitBodyNode(n, options)).join("\n\n");
|
|
391
|
+
}
|
|
392
|
+
// ============================================================
|
|
393
|
+
// Public API
|
|
394
|
+
// ============================================================
|
|
395
|
+
/**
|
|
396
|
+
* Convert an OdtDocumentModel to a Typst markup string.
|
|
397
|
+
*
|
|
398
|
+
* This is the primary emitter function. It accepts a pre-parsed document
|
|
399
|
+
* model and returns a .typ string with no side effects and no filesystem
|
|
400
|
+
* access. Use this when you already have a model from readOdt() or when
|
|
401
|
+
* you need fine-grained control over read options.
|
|
402
|
+
*
|
|
403
|
+
* @param model - The parsed ODT document model from readOdt().
|
|
404
|
+
* @param options - Typst emitter options.
|
|
405
|
+
* @returns Typst markup string (.typ).
|
|
406
|
+
*
|
|
407
|
+
* @example
|
|
408
|
+
* ```typescript
|
|
409
|
+
* import { readOdt } from "odf-kit/reader";
|
|
410
|
+
* import { modelToTypst } from "odf-kit/typst";
|
|
411
|
+
* import { readFileSync, writeFileSync } from "node:fs";
|
|
412
|
+
*
|
|
413
|
+
* const bytes = new Uint8Array(readFileSync("document.odt"));
|
|
414
|
+
* const model = readOdt(bytes);
|
|
415
|
+
* const typ = modelToTypst(model);
|
|
416
|
+
* writeFileSync("document.typ", typ);
|
|
417
|
+
* // then: typst compile document.typ document.pdf
|
|
418
|
+
* ```
|
|
419
|
+
*/
|
|
420
|
+
export function modelToTypst(model, options) {
|
|
421
|
+
const sections = [];
|
|
422
|
+
const pageSetup = emitPageSetup(model);
|
|
423
|
+
if (pageSetup)
|
|
424
|
+
sections.push(pageSetup);
|
|
425
|
+
const body = emitBodyNodes(model.body, options);
|
|
426
|
+
if (body)
|
|
427
|
+
sections.push(body);
|
|
428
|
+
return sections.join("\n\n");
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Convert an .odt file directly to a Typst markup string.
|
|
432
|
+
*
|
|
433
|
+
* Convenience wrapper around readOdt() + modelToTypst(). Use modelToTypst()
|
|
434
|
+
* directly when you need access to the document model, metadata, or want to
|
|
435
|
+
* share a single readOdt() call between multiple emitters.
|
|
436
|
+
*
|
|
437
|
+
* @param bytes - The raw .odt file as a Uint8Array.
|
|
438
|
+
* @param options - Combined emitter and read options. The trackedChanges
|
|
439
|
+
* field is forwarded to both readOdt() and modelToTypst() — set it once
|
|
440
|
+
* for consistent results across both steps.
|
|
441
|
+
* @returns Typst markup string (.typ).
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```typescript
|
|
445
|
+
* import { odtToTypst } from "odf-kit/typst";
|
|
446
|
+
* import { readFileSync, writeFileSync } from "node:fs";
|
|
447
|
+
*
|
|
448
|
+
* const bytes = new Uint8Array(readFileSync("document.odt"));
|
|
449
|
+
* const typ = odtToTypst(bytes);
|
|
450
|
+
* writeFileSync("document.typ", typ);
|
|
451
|
+
* // then: typst compile document.typ document.pdf
|
|
452
|
+
* ```
|
|
453
|
+
*/
|
|
454
|
+
export function odtToTypst(bytes, options) {
|
|
455
|
+
const model = readOdt(bytes, options);
|
|
456
|
+
return modelToTypst(model, options);
|
|
457
|
+
}
|
|
458
|
+
//# sourceMappingURL=emitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../src/typst/emitter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typst emitter for odf-kit.
|
|
3
|
+
*
|
|
4
|
+
* Converts ODT documents to Typst markup (.typ), which can then be compiled
|
|
5
|
+
* to PDF by any Typst 0.11+ installation:
|
|
6
|
+
*
|
|
7
|
+
* typst compile document.typ document.pdf
|
|
8
|
+
*
|
|
9
|
+
* Import from "odf-kit/typst" (separate from the main "odf-kit" import
|
|
10
|
+
* so the emitter is only bundled when explicitly needed):
|
|
11
|
+
*
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { modelToTypst, odtToTypst } from "odf-kit/typst";
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* modelToTypst() accepts a pre-parsed OdtDocumentModel and is the primary
|
|
17
|
+
* function. odtToTypst() is a convenience wrapper that calls readOdt() +
|
|
18
|
+
* modelToTypst() in a single step.
|
|
19
|
+
*
|
|
20
|
+
* Both functions are zero-dependency pure functions — no filesystem access,
|
|
21
|
+
* no child process spawning, no Typst installation required at import time.
|
|
22
|
+
* The consumer decides how to use the returned .typ string.
|
|
23
|
+
*/
|
|
24
|
+
export { modelToTypst, odtToTypst } from "./emitter.js";
|
|
25
|
+
export type { TypstEmitOptions } from "./emitter.js";
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/typst/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typst emitter for odf-kit.
|
|
3
|
+
*
|
|
4
|
+
* Converts ODT documents to Typst markup (.typ), which can then be compiled
|
|
5
|
+
* to PDF by any Typst 0.11+ installation:
|
|
6
|
+
*
|
|
7
|
+
* typst compile document.typ document.pdf
|
|
8
|
+
*
|
|
9
|
+
* Import from "odf-kit/typst" (separate from the main "odf-kit" import
|
|
10
|
+
* so the emitter is only bundled when explicitly needed):
|
|
11
|
+
*
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { modelToTypst, odtToTypst } from "odf-kit/typst";
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* modelToTypst() accepts a pre-parsed OdtDocumentModel and is the primary
|
|
17
|
+
* function. odtToTypst() is a convenience wrapper that calls readOdt() +
|
|
18
|
+
* modelToTypst() in a single step.
|
|
19
|
+
*
|
|
20
|
+
* Both functions are zero-dependency pure functions — no filesystem access,
|
|
21
|
+
* no child process spawning, no Typst installation required at import time.
|
|
22
|
+
* The consumer decides how to use the returned .typ string.
|
|
23
|
+
*/
|
|
24
|
+
export { modelToTypst, odtToTypst } from "./emitter.js";
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/typst/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "odf-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Create OpenDocument Format files (.odt, .ods, .odp, .odg) in TypeScript/JavaScript. Works in Node.js and browsers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"./reader": {
|
|
14
14
|
"types": "./dist/reader/index.d.ts",
|
|
15
15
|
"import": "./dist/reader/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./typst": {
|
|
18
|
+
"types": "./dist/typst/index.d.ts",
|
|
19
|
+
"import": "./dist/typst/index.js"
|
|
16
20
|
}
|
|
17
21
|
},
|
|
18
22
|
"files": [
|