@unisim/doc 0.1.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/README.md +91 -0
- package/dist/convert.d.ts +54 -0
- package/dist/convert.d.ts.map +1 -0
- package/dist/convert.js +216 -0
- package/dist/convert.js.map +1 -0
- package/dist/image.d.ts +17 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +66 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/merge.d.ts +14 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +51 -0
- package/dist/merge.js.map +1 -0
- package/dist/metrics.d.ts +19 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +151 -0
- package/dist/metrics.js.map +1 -0
- package/dist/model.d.ts +114 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +121 -0
- package/dist/model.js.map +1 -0
- package/dist/pdfcore.d.ts +118 -0
- package/dist/pdfcore.d.ts.map +1 -0
- package/dist/pdfcore.js +398 -0
- package/dist/pdfcore.js.map +1 -0
- package/dist/read/csv.d.ts +17 -0
- package/dist/read/csv.d.ts.map +1 -0
- package/dist/read/csv.js +167 -0
- package/dist/read/csv.js.map +1 -0
- package/dist/read/doc.d.ts +3 -0
- package/dist/read/doc.d.ts.map +1 -0
- package/dist/read/doc.js +360 -0
- package/dist/read/doc.js.map +1 -0
- package/dist/read/docx.d.ts +3 -0
- package/dist/read/docx.d.ts.map +1 -0
- package/dist/read/docx.js +455 -0
- package/dist/read/docx.js.map +1 -0
- package/dist/read/html.d.ts +4 -0
- package/dist/read/html.d.ts.map +1 -0
- package/dist/read/html.js +236 -0
- package/dist/read/html.js.map +1 -0
- package/dist/read/json.d.ts +9 -0
- package/dist/read/json.d.ts.map +1 -0
- package/dist/read/json.js +105 -0
- package/dist/read/json.js.map +1 -0
- package/dist/read/markdown.d.ts +4 -0
- package/dist/read/markdown.d.ts.map +1 -0
- package/dist/read/markdown.js +247 -0
- package/dist/read/markdown.js.map +1 -0
- package/dist/read/odt.d.ts +3 -0
- package/dist/read/odt.d.ts.map +1 -0
- package/dist/read/odt.js +359 -0
- package/dist/read/odt.js.map +1 -0
- package/dist/read/rtf.d.ts +3 -0
- package/dist/read/rtf.d.ts.map +1 -0
- package/dist/read/rtf.js +341 -0
- package/dist/read/rtf.js.map +1 -0
- package/dist/read/text.d.ts +4 -0
- package/dist/read/text.d.ts.map +1 -0
- package/dist/read/text.js +62 -0
- package/dist/read/text.js.map +1 -0
- package/dist/unzip.d.ts +23 -0
- package/dist/unzip.d.ts.map +1 -0
- package/dist/unzip.js +152 -0
- package/dist/unzip.js.map +1 -0
- package/dist/write/data.d.ts +16 -0
- package/dist/write/data.d.ts.map +1 -0
- package/dist/write/data.js +89 -0
- package/dist/write/data.js.map +1 -0
- package/dist/write/pdf.d.ts +10 -0
- package/dist/write/pdf.d.ts.map +1 -0
- package/dist/write/pdf.js +696 -0
- package/dist/write/pdf.js.map +1 -0
- package/dist/write/pdfSettings.d.ts +13 -0
- package/dist/write/pdfSettings.d.ts.map +1 -0
- package/dist/write/pdfSettings.js +26 -0
- package/dist/write/pdfSettings.js.map +1 -0
- package/dist/write/text.d.ts +5 -0
- package/dist/write/text.d.ts.map +1 -0
- package/dist/write/text.js +341 -0
- package/dist/write/text.js.map +1 -0
- package/dist/xml.d.ts +22 -0
- package/dist/xml.d.ts.map +1 -0
- package/dist/xml.js +83 -0
- package/dist/xml.js.map +1 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# @unisim/doc
|
|
2
|
+
|
|
3
|
+
The suite's document stack. Reads Word, OpenDocument, RTF, HTML, Markdown, CSV,
|
|
4
|
+
JSON and plain text into one model, and writes that model back out as PDF, text,
|
|
5
|
+
HTML, Markdown, CSV or JSON.
|
|
6
|
+
|
|
7
|
+
**No runtime dependencies.** That is not incidental — one of its two consumers
|
|
8
|
+
(Universal Converter) is sold on being offline from first visit with nothing
|
|
9
|
+
fetched, so a dependency here would cost that product its pitch.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { readToDoc, convertDocument, DEFAULT_DOC_SETTINGS } from '@unisim/doc'
|
|
13
|
+
|
|
14
|
+
// The model, for a caller that wants to inspect or combine documents
|
|
15
|
+
const doc = await readToDoc(file)
|
|
16
|
+
|
|
17
|
+
// Or straight to an output
|
|
18
|
+
const { blob, ext, notices, pages } = await convertDocument(file, {
|
|
19
|
+
...DEFAULT_DOC_SETTINGS,
|
|
20
|
+
format: 'pdf',
|
|
21
|
+
})
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`convertDocument` hands back the target **extension**, not a filename. Naming
|
|
25
|
+
the output file is the caller's job on purpose: the app knows what the source
|
|
26
|
+
was called and which of its own naming rules apply, and a library offering a
|
|
27
|
+
second opinion is how you end up chasing a `.png.png`.
|
|
28
|
+
|
|
29
|
+
## Why it exists
|
|
30
|
+
|
|
31
|
+
It existed twice. On 2026-08-13 two sessions that could not see each other
|
|
32
|
+
shipped the same 1,200–1,500 lines on the same afternoon — a ZIP reader, a
|
|
33
|
+
namespace-agnostic XML layer, an OOXML walker, an ODF walker, and a
|
|
34
|
+
tokenise → wrap → paginate layout engine — into Universal PDF and Universal
|
|
35
|
+
Converter. Both files ended up carrying a header saying *"a fix to one is a fix
|
|
36
|
+
to neither."*
|
|
37
|
+
|
|
38
|
+
This is Converter's version, because it was the better one: a model designed as
|
|
39
|
+
a hub (nine readers × six writers) rather than grown out of a Markdown renderer,
|
|
40
|
+
with heading level 4, real image blocks, `underline`/`strike` on a run, and
|
|
41
|
+
`DocNotice` — a channel for reporting what a conversion **lost**, which the
|
|
42
|
+
other had no equivalent of. Universal PDF's model had exactly one thing this
|
|
43
|
+
lacked, `pagesetup`, which was merged in.
|
|
44
|
+
|
|
45
|
+
## What it does not do
|
|
46
|
+
|
|
47
|
+
It re-typesets a document; it does not reproduce the page. Fonts, columns,
|
|
48
|
+
headers and footers, text boxes and floating shapes do not survive. Anything a
|
|
49
|
+
reader has to drop is reported through `RichDoc.notices` rather than discovered
|
|
50
|
+
later — the failure worth designing against is someone assuming the output is a
|
|
51
|
+
copy.
|
|
52
|
+
|
|
53
|
+
## Two writers, on purpose
|
|
54
|
+
|
|
55
|
+
The PDF writer here goes through `pdfcore` — base-14 fonts, its own AFM tables,
|
|
56
|
+
no dependency. A consumer that already carries pdf-lib for other reasons
|
|
57
|
+
(Universal PDF does, for signing, redaction and forms) gains nothing by dropping
|
|
58
|
+
it, and can take the readers and the model from here while keeping its own
|
|
59
|
+
writer. The seam is that `write/pdf.ts` depends only on `pdfcore`'s small
|
|
60
|
+
surface — `addPage` / `drawText` / `drawRect` / `drawImage` / `widthOfText`.
|
|
61
|
+
|
|
62
|
+
## Notes for anyone editing it
|
|
63
|
+
|
|
64
|
+
- **Imports carry a `.js` extension.** Not `.ts` (which needs
|
|
65
|
+
`allowImportingTsExtensions`, only legal with `noEmit`) and not extensionless
|
|
66
|
+
(emitted verbatim, so `dist/index.js` would import `./convert` and Node would
|
|
67
|
+
refuse it). The first build of this package made exactly that mistake.
|
|
68
|
+
- **The model is the intersection of what these formats can express, not the
|
|
69
|
+
union.** A DOCX can hold a text box rotated 3° over a chart; Markdown cannot.
|
|
70
|
+
Anything outside the vocabulary is flattened on the way *in*, by the reader
|
|
71
|
+
that knows what it meant.
|
|
72
|
+
- **`pagesetup` is an instruction, not a property**, because that is how the
|
|
73
|
+
source formats model it — orientation belongs to a section, and a section runs
|
|
74
|
+
until the next break. ⚠️ The two office formats express it in **opposite
|
|
75
|
+
directions**: OOXML's `w:sectPr` sits on the **last** paragraph of the section
|
|
76
|
+
it describes, ODF's `style:master-page-name` on the **first** paragraph of the
|
|
77
|
+
new one. Reading them the same way puts every change one page out.
|
|
78
|
+
|
|
79
|
+
## Tests
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
npm test
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Covers what is DOM-free: the hand-written PDF byte layer (including that the
|
|
86
|
+
xref offsets actually point at their objects — a reader that repairs a damaged
|
|
87
|
+
table will open a broken file, so "it opened in Chrome" proves nothing), pages
|
|
88
|
+
of differing sizes in one document, and the `setSize` guard rail. The rest is
|
|
89
|
+
DOM-bound and is covered by the consuming apps' browser suites — in particular
|
|
90
|
+
Universal PDF's `e2e/office-import.e2e.mjs`, which drives real `.docx`/`.odt`
|
|
91
|
+
fixtures and asserts on the page sizes pdf.js reads back out.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { DocNotice, RichDoc } from './model.js';
|
|
2
|
+
import { type PdfSettings } from './write/pdfSettings.js';
|
|
3
|
+
export type { PdfSettings, PaperSize, PageMargin, FontChoice } from './write/pdfSettings.js';
|
|
4
|
+
export { DEFAULT_PDF_SETTINGS } from './write/pdfSettings.js';
|
|
5
|
+
export type { DocNotice, RichDoc } from './model.js';
|
|
6
|
+
export type DocFormat = 'pdf' | 'txt' | 'html' | 'md' | 'csv' | 'json';
|
|
7
|
+
export interface DocSettings {
|
|
8
|
+
format: DocFormat;
|
|
9
|
+
pdf: PdfSettings;
|
|
10
|
+
/** CSV → JSON: read "42" as a number rather than a string. */
|
|
11
|
+
inferTypes: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare const DEFAULT_DOC_SETTINGS: DocSettings;
|
|
14
|
+
/** What came out, plus anything the person should be told about it. */
|
|
15
|
+
/**
|
|
16
|
+
* What a conversion produced.
|
|
17
|
+
*
|
|
18
|
+
* ⚠️ Carries the target EXTENSION, not a filename. Naming the output file is
|
|
19
|
+
* the caller's job and deliberately not this package's: the app knows what the
|
|
20
|
+
* source was called and which of its own naming rules apply, and a library that
|
|
21
|
+
* guessed would be a second opinion on a question that has already caused a
|
|
22
|
+
* `.png.png` hunt once. Callers pass this to their own `withExtension`.
|
|
23
|
+
*/
|
|
24
|
+
export interface DocResult {
|
|
25
|
+
blob: Blob;
|
|
26
|
+
/** Target extension without the dot — 'pdf', 'txt', 'html', 'md', 'csv', 'json'. */
|
|
27
|
+
ext: DocFormat;
|
|
28
|
+
notices: DocNotice[];
|
|
29
|
+
/** Page count, for the formats that have pages. */
|
|
30
|
+
pages?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Input extensions, grouped by the shape of what they produce.
|
|
34
|
+
*
|
|
35
|
+
* `tabular` sources also yield a `Grid`, which is the only thing that can
|
|
36
|
+
* become CSV or JSON. Everything else is a document, and asking for CSV from a
|
|
37
|
+
* Word file is refused by the UI rather than answered with a one-column sheet
|
|
38
|
+
* of its paragraphs.
|
|
39
|
+
*/
|
|
40
|
+
export declare const DOC_INPUT_EXTS: readonly ["docx", "doc", "odt", "rtf", "txt", "text", "log", "md", "markdown", "html", "htm", "csv", "tsv", "json"];
|
|
41
|
+
export declare function isTabular(ext: string): boolean;
|
|
42
|
+
/** The targets a given input can actually reach. */
|
|
43
|
+
export declare function targetsFor(ext: string): DocFormat[];
|
|
44
|
+
/** Targets reachable by every file in a mixed queue. */
|
|
45
|
+
export declare function commonTargets(exts: readonly string[]): DocFormat[];
|
|
46
|
+
/**
|
|
47
|
+
* Read a file to a RichDoc, whichever kind it is.
|
|
48
|
+
*
|
|
49
|
+
* The one entry point the "join into one PDF" export needs — it wants the model
|
|
50
|
+
* and not a converted file, so that it can concatenate before writing once.
|
|
51
|
+
*/
|
|
52
|
+
export declare function readToDoc(file: File): Promise<RichDoc>;
|
|
53
|
+
export declare function convertDocument(file: File, settings: DocSettings, onProgress?: (fraction: number) => void): Promise<DocResult>;
|
|
54
|
+
//# sourceMappingURL=convert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,SAAS,EAAQ,OAAO,EAAE,MAAM,YAAY,CAAA;AAI1D,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAE/E,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAC5F,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEpD,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,GAAG,MAAM,CAAA;AAEtE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,SAAS,CAAA;IACjB,GAAG,EAAE,WAAW,CAAA;IAChB,8DAA8D;IAC9D,UAAU,EAAE,OAAO,CAAA;CACpB;AAED,eAAO,MAAM,oBAAoB,EAAE,WAIlC,CAAA;AAED,uEAAuE;AACvE;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,IAAI,CAAA;IACV,oFAAoF;IACpF,GAAG,EAAE,SAAS,CAAA;IACd,OAAO,EAAE,SAAS,EAAE,CAAA;IACpB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAID;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,qHAGjB,CAAA;AAIV,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,oDAAoD;AACpD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,CAKnD;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EAAE,CAKlE;AAOD;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAE5D;AAsDD,wBAAsB,eAAe,CACnC,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,WAAW,EACrB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GACtC,OAAO,CAAC,SAAS,CAAC,CAqEpB"}
|
package/dist/convert.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// The Files pipeline: read anything → RichDoc (or a Grid) → write anything.
|
|
3
|
+
//
|
|
4
|
+
// This file is the only place that knows which inputs pair with which outputs.
|
|
5
|
+
// Everything either side of it is ignorant of the other, which is what keeps a
|
|
6
|
+
// tenth input format to one new file.
|
|
7
|
+
//
|
|
8
|
+
// EVERY READER IS LAZILY IMPORTED. Together they are a fair amount of code —
|
|
9
|
+
// a CFB reader, an RTF tokeniser, two XML dialects — and somebody who opened
|
|
10
|
+
// the app to convert a JPEG should not download any of it. Dynamic `import()`
|
|
11
|
+
// puts each in its own chunk, fetched the first time a file of that type is
|
|
12
|
+
// converted, and never on the audio, images or video tabs. That is the same
|
|
13
|
+
// bargain LAME strikes, except these are already in the bundle rather than
|
|
14
|
+
// coming from npm, so it works offline: the service worker precaches them.
|
|
15
|
+
//
|
|
16
|
+
// ⚠️ THIS STACK EXISTS TWICE IN THE SUITE. Universal PDF shipped its own office
|
|
17
|
+
// import — `unzip.ts`, `officeXml.ts`, `docxToBlocks.ts`, `odtToBlocks.ts`,
|
|
18
|
+
// `blockPdf.ts` — at 16:24 on 2026-08-13 (commit c1f3faa), the same afternoon
|
|
19
|
+
// this landed, from a parallel session neither could see. The ZIP reader, the
|
|
20
|
+
// XML helpers, the OOXML/ODF walkers and the layout engine all overlap.
|
|
21
|
+
//
|
|
22
|
+
// They are not identical: this side reads .doc, .rtf, .csv, .json, .txt, .md and
|
|
23
|
+
// .html, embeds images, and writes with NO dependency; that side refuses .doc and
|
|
24
|
+
// .rtf and renders through pdf-lib. Consolidating them into @unisim/sdk is an
|
|
25
|
+
// open backlog item ("Universal Converter" in backlog-unisim.md). Until it is
|
|
26
|
+
// done, A FIX HERE IS A FIX TO NEITHER — check the twin.
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// ⚠️ From `pdfSettings`, NOT from `write/pdf`. A static import of the renderer
|
|
29
|
+
// here would cancel the dynamic one below and pull the whole layout engine and
|
|
30
|
+
// its font tables into the main bundle — see that file's header.
|
|
31
|
+
import { DEFAULT_PDF_SETTINGS } from './write/pdfSettings.js';
|
|
32
|
+
export { DEFAULT_PDF_SETTINGS } from './write/pdfSettings.js';
|
|
33
|
+
export const DEFAULT_DOC_SETTINGS = {
|
|
34
|
+
format: 'pdf',
|
|
35
|
+
pdf: DEFAULT_PDF_SETTINGS,
|
|
36
|
+
inferTypes: true,
|
|
37
|
+
};
|
|
38
|
+
// ── What reads what ──────────────────────────────────────────────────────────
|
|
39
|
+
/**
|
|
40
|
+
* Input extensions, grouped by the shape of what they produce.
|
|
41
|
+
*
|
|
42
|
+
* `tabular` sources also yield a `Grid`, which is the only thing that can
|
|
43
|
+
* become CSV or JSON. Everything else is a document, and asking for CSV from a
|
|
44
|
+
* Word file is refused by the UI rather than answered with a one-column sheet
|
|
45
|
+
* of its paragraphs.
|
|
46
|
+
*/
|
|
47
|
+
export const DOC_INPUT_EXTS = [
|
|
48
|
+
'docx', 'doc', 'odt', 'rtf', 'txt', 'text', 'log', 'md', 'markdown',
|
|
49
|
+
'html', 'htm', 'csv', 'tsv', 'json',
|
|
50
|
+
];
|
|
51
|
+
const TABULAR_EXTS = new Set(['csv', 'tsv', 'json']);
|
|
52
|
+
export function isTabular(ext) {
|
|
53
|
+
return TABULAR_EXTS.has(ext);
|
|
54
|
+
}
|
|
55
|
+
/** The targets a given input can actually reach. */
|
|
56
|
+
export function targetsFor(ext) {
|
|
57
|
+
if (ext === 'csv' || ext === 'tsv')
|
|
58
|
+
return ['pdf', 'json', 'html', 'md', 'txt'];
|
|
59
|
+
if (ext === 'json')
|
|
60
|
+
return ['pdf', 'csv', 'html', 'md', 'txt'];
|
|
61
|
+
// A document has no grid behind it, so CSV and JSON are not on offer.
|
|
62
|
+
return ['pdf', 'txt', 'html', 'md'];
|
|
63
|
+
}
|
|
64
|
+
/** Targets reachable by every file in a mixed queue. */
|
|
65
|
+
export function commonTargets(exts) {
|
|
66
|
+
if (!exts.length)
|
|
67
|
+
return ['pdf', 'txt', 'html', 'md'];
|
|
68
|
+
return exts
|
|
69
|
+
.map(targetsFor)
|
|
70
|
+
.reduce((shared, next) => shared.filter((f) => next.includes(f)));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Read a file to a RichDoc, whichever kind it is.
|
|
74
|
+
*
|
|
75
|
+
* The one entry point the "join into one PDF" export needs — it wants the model
|
|
76
|
+
* and not a converted file, so that it can concatenate before writing once.
|
|
77
|
+
*/
|
|
78
|
+
export async function readToDoc(file) {
|
|
79
|
+
return (await read(file, extensionOf(file.name))).doc;
|
|
80
|
+
}
|
|
81
|
+
async function read(file, ext) {
|
|
82
|
+
switch (ext) {
|
|
83
|
+
case 'docx': {
|
|
84
|
+
const { readDocx } = await import('./read/docx.js');
|
|
85
|
+
return { doc: await readDocx(file), grid: null };
|
|
86
|
+
}
|
|
87
|
+
case 'doc': {
|
|
88
|
+
const { readDoc } = await import('./read/doc.js');
|
|
89
|
+
return { doc: await readDoc(file), grid: null };
|
|
90
|
+
}
|
|
91
|
+
case 'odt': {
|
|
92
|
+
const { readOdt } = await import('./read/odt.js');
|
|
93
|
+
return { doc: await readOdt(file), grid: null };
|
|
94
|
+
}
|
|
95
|
+
case 'rtf': {
|
|
96
|
+
const { readRtf } = await import('./read/rtf.js');
|
|
97
|
+
return { doc: await readRtf(file), grid: null };
|
|
98
|
+
}
|
|
99
|
+
case 'md':
|
|
100
|
+
case 'markdown': {
|
|
101
|
+
const { readMarkdown } = await import('./read/markdown.js');
|
|
102
|
+
return { doc: await readMarkdown(file), grid: null };
|
|
103
|
+
}
|
|
104
|
+
case 'html':
|
|
105
|
+
case 'htm': {
|
|
106
|
+
const { readHtml } = await import('./read/html.js');
|
|
107
|
+
return { doc: await readHtml(file), grid: null };
|
|
108
|
+
}
|
|
109
|
+
case 'csv':
|
|
110
|
+
case 'tsv': {
|
|
111
|
+
const { readCsv } = await import('./read/csv.js');
|
|
112
|
+
const { doc, grid } = await readCsv(file);
|
|
113
|
+
return { doc, grid };
|
|
114
|
+
}
|
|
115
|
+
case 'json': {
|
|
116
|
+
const { readJson } = await import('./read/json.js');
|
|
117
|
+
const { doc, grid } = await readJson(file);
|
|
118
|
+
return { doc, grid };
|
|
119
|
+
}
|
|
120
|
+
case 'txt':
|
|
121
|
+
case 'text':
|
|
122
|
+
case 'log': {
|
|
123
|
+
const { readText } = await import('./read/text.js');
|
|
124
|
+
return { doc: await readText(file), grid: null };
|
|
125
|
+
}
|
|
126
|
+
default:
|
|
127
|
+
throw new Error(`${ext ? ext.toUpperCase() : 'That file type'} isn’t a document this can read.`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// ── The conversion ───────────────────────────────────────────────────────────
|
|
131
|
+
export async function convertDocument(file, settings, onProgress) {
|
|
132
|
+
const ext = extensionOf(file.name);
|
|
133
|
+
onProgress?.(0.05);
|
|
134
|
+
const { doc, grid } = await read(file, ext);
|
|
135
|
+
// Reading is most of the work for the formats that need unzipping and
|
|
136
|
+
// decoding; writing is fast. The split reflects that rather than pretending
|
|
137
|
+
// to know a percentage it cannot measure.
|
|
138
|
+
onProgress?.(0.65);
|
|
139
|
+
const notices = [...doc.notices];
|
|
140
|
+
switch (settings.format) {
|
|
141
|
+
case 'pdf': {
|
|
142
|
+
const { docToPdf } = await import('./write/pdf.js');
|
|
143
|
+
const result = await docToPdf(doc, settings.pdf);
|
|
144
|
+
if (result.dropped.length) {
|
|
145
|
+
// Named, not counted. "12 characters could not be written" sends
|
|
146
|
+
// somebody hunting; showing them the actual glyphs makes it obvious
|
|
147
|
+
// at a glance that it was the Greek in one footnote.
|
|
148
|
+
const sample = result.dropped.slice(0, 12).join(' ');
|
|
149
|
+
notices.push({
|
|
150
|
+
severity: 'loss',
|
|
151
|
+
message: `Some characters aren’t in the PDF’s built-in fonts and came out as “?”: ${sample}` +
|
|
152
|
+
(result.dropped.length > 12 ? ` and ${result.dropped.length - 12} more.` : '.') +
|
|
153
|
+
' Those alphabets need a font embedded in the file, which this converter doesn’t do.',
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
onProgress?.(1);
|
|
157
|
+
return { blob: result.blob, ext: 'pdf', notices, pages: result.pages };
|
|
158
|
+
}
|
|
159
|
+
case 'txt': {
|
|
160
|
+
const { docToText } = await import('./write/text.js');
|
|
161
|
+
onProgress?.(1);
|
|
162
|
+
return { blob: textBlob(docToText(doc), 'text/plain'), ext: 'txt', notices };
|
|
163
|
+
}
|
|
164
|
+
case 'html': {
|
|
165
|
+
const { docToHtml } = await import('./write/text.js');
|
|
166
|
+
onProgress?.(1);
|
|
167
|
+
return { blob: textBlob(docToHtml(doc), 'text/html'), ext: 'html', notices };
|
|
168
|
+
}
|
|
169
|
+
case 'md': {
|
|
170
|
+
const { docToMarkdown } = await import('./write/text.js');
|
|
171
|
+
onProgress?.(1);
|
|
172
|
+
return { blob: textBlob(docToMarkdown(doc), 'text/markdown'), ext: 'md', notices };
|
|
173
|
+
}
|
|
174
|
+
case 'csv': {
|
|
175
|
+
if (!grid)
|
|
176
|
+
throw new Error('Only a spreadsheet-shaped file can become a CSV.');
|
|
177
|
+
const { gridToCsv } = await import('./write/data.js');
|
|
178
|
+
onProgress?.(1);
|
|
179
|
+
return { blob: csvBlob(gridToCsv(grid)), ext: 'csv', notices };
|
|
180
|
+
}
|
|
181
|
+
case 'json': {
|
|
182
|
+
if (!grid)
|
|
183
|
+
throw new Error('Only a spreadsheet-shaped file can become JSON.');
|
|
184
|
+
const { gridToJson } = await import('./write/data.js');
|
|
185
|
+
onProgress?.(1);
|
|
186
|
+
return {
|
|
187
|
+
blob: textBlob(gridToJson(grid, { inferTypes: settings.inferTypes }), 'application/json'),
|
|
188
|
+
ext: 'json',
|
|
189
|
+
notices,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function textBlob(text, type) {
|
|
195
|
+
return new Blob([text], { type: `${type};charset=utf-8` });
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* A CSV, and the ONE place a byte-order mark is written.
|
|
199
|
+
*
|
|
200
|
+
* ⚠️ Excel opens a UTF-8 CSV as Windows-1252 unless the file starts with a BOM,
|
|
201
|
+
* so "Ré" arrives as "Ré" for anybody who double-clicks the result — and
|
|
202
|
+
* double-clicking it is what a CSV is for. The BOM is NOT put on the other text
|
|
203
|
+
* targets, and specifically not on JSON: RFC 8259 forbids one, and `JSON.parse`
|
|
204
|
+
* throws on a string that has it, so the tidy-looking "put it on everything"
|
|
205
|
+
* rule would hand back JSON that will not load.
|
|
206
|
+
*/
|
|
207
|
+
function csvBlob(text) {
|
|
208
|
+
return new Blob(['', text], { type: 'text/csv;charset=utf-8' });
|
|
209
|
+
}
|
|
210
|
+
function extensionOf(filename) {
|
|
211
|
+
const dot = filename.lastIndexOf('.');
|
|
212
|
+
if (dot < 1 || dot === filename.length - 1)
|
|
213
|
+
return '';
|
|
214
|
+
return filename.slice(dot + 1).toLowerCase();
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=convert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert.js","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,4EAA4E;AAC5E,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAC/E,sCAAsC;AACtC,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,8EAA8E;AAC9E,4EAA4E;AAC5E,4EAA4E;AAC5E,2EAA2E;AAC3E,2EAA2E;AAC3E,EAAE;AACF,gFAAgF;AAChF,4EAA4E;AAC5E,8EAA8E;AAC9E,8EAA8E;AAC9E,wEAAwE;AACxE,EAAE;AACF,iFAAiF;AACjF,kFAAkF;AAClF,8EAA8E;AAC9E,8EAA8E;AAC9E,yDAAyD;AACzD,8EAA8E;AAG9E,+EAA+E;AAC/E,+EAA+E;AAC/E,iEAAiE;AACjE,OAAO,EAAE,oBAAoB,EAAoB,MAAM,wBAAwB,CAAA;AAG/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAY7D,MAAM,CAAC,MAAM,oBAAoB,GAAgB;IAC/C,MAAM,EAAE,KAAK;IACb,GAAG,EAAE,oBAAoB;IACzB,UAAU,EAAE,IAAI;CACjB,CAAA;AAqBD,gFAAgF;AAEhF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU;IACnE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM;CAC3B,CAAA;AAEV,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;AAEpD,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,OAAO,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;AAC9B,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,KAAK;QAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;IAC/E,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;IAC9D,sEAAsE;IACtE,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;AACrC,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,aAAa,CAAC,IAAuB;IACnD,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;IACrD,OAAO,IAAI;SACR,GAAG,CAAC,UAAU,CAAC;SACf,MAAM,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACrE,CAAC;AAOD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAU;IACxC,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;AACvD,CAAC;AAED,KAAK,UAAU,IAAI,CAAC,IAAU,EAAE,GAAW;IACzC,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAA;YACnD,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;QAClD,CAAC;QACD,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAA;YACjD,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;QACjD,CAAC;QACD,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAA;YACjD,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;QACjD,CAAC;QACD,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAA;YACjD,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;QACjD,CAAC;QACD,KAAK,IAAI,CAAC;QACV,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAA;YAC3D,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;QACtD,CAAC;QACD,KAAK,MAAM,CAAC;QACZ,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAA;YACnD,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;QAClD,CAAC;QACD,KAAK,KAAK,CAAC;QACX,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAA;YACjD,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;YACzC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;QACtB,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAA;YACnD,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAA;YAC1C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;QACtB,CAAC;QACD,KAAK,KAAK,CAAC;QACX,KAAK,MAAM,CAAC;QACZ,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAA;YACnD,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;QAClD,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,gBAAgB,kCAAkC,CAAC,CAAA;IACpG,CAAC;AACH,CAAC;AAED,gFAAgF;AAEhF,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAU,EACV,QAAqB,EACrB,UAAuC;IAEvC,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAClC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAA;IAElB,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAC3C,sEAAsE;IACtE,4EAA4E;IAC5E,0CAA0C;IAC1C,UAAU,EAAE,CAAC,IAAI,CAAC,CAAA;IAElB,MAAM,OAAO,GAAG,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,CAAA;IAEhC,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC;QACxB,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAA;YACnD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAA;YAChD,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBAC1B,iEAAiE;gBACjE,oEAAoE;gBACpE,qDAAqD;gBACrD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;gBACpD,OAAO,CAAC,IAAI,CAAC;oBACX,QAAQ,EAAE,MAAM;oBAChB,OAAO,EACL,2EAA2E,MAAM,EAAE;wBACnF,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;wBAC/E,qFAAqF;iBACxF,CAAC,CAAA;YACJ,CAAC;YACD,UAAU,EAAE,CAAC,CAAC,CAAC,CAAA;YACf,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAA;QACxE,CAAC;QAED,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAA;YACrD,UAAU,EAAE,CAAC,CAAC,CAAC,CAAA;YACf,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,YAAY,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;QAC9E,CAAC;QAED,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAA;YACrD,UAAU,EAAE,CAAC,CAAC,CAAC,CAAA;YACf,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,WAAW,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;QAC9E,CAAC;QAED,KAAK,IAAI,CAAC,CAAC,CAAC;YACV,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAA;YACzD,UAAU,EAAE,CAAC,CAAC,CAAC,CAAA;YACf,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,eAAe,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;QACpF,CAAC;QAED,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAA;YAC9E,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAA;YACrD,UAAU,EAAE,CAAC,CAAC,CAAC,CAAA;YACf,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;QAChE,CAAC;QAED,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAA;YAC7E,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAA;YACtD,UAAU,EAAE,CAAC,CAAC,CAAC,CAAA;YACf,OAAO;gBACL,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,EAAE,kBAAkB,CAAC;gBACzF,GAAG,EAAE,MAAM;gBACX,OAAO;aACR,CAAA;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,IAAY;IAC1C,OAAO,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,gBAAgB,EAAE,CAAC,CAAA;AAC5D,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,IAAI,IAAI,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE,CAAC,CAAA;AAClE,CAAC;AAED,SAAS,WAAW,CAAC,QAAgB;IACnC,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;IACrC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,CAAA;IACrD,OAAO,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAA;AAC9C,CAAC"}
|
package/dist/image.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type PdfImage } from './pdfcore.js';
|
|
2
|
+
export type PdfPageSource = PdfImage;
|
|
3
|
+
/**
|
|
4
|
+
* Draw a file to a canvas and read it back as JPEG.
|
|
5
|
+
*
|
|
6
|
+
* The white fill is not cosmetic: a canvas starts transparent, and drawing a
|
|
7
|
+
* PNG with alpha onto it and asking for JPEG gives BLACK where the transparency
|
|
8
|
+
* was on some browsers and white on others. Filling first makes it white
|
|
9
|
+
* everywhere, which is what somebody printing a page expects.
|
|
10
|
+
*/
|
|
11
|
+
export declare function imageToJpeg(file: Blob, quality: number, maxEdge: number | null): Promise<PdfPageSource>;
|
|
12
|
+
/**
|
|
13
|
+
* Assemble pages into a PDF — one image per page, laid out at 72 dpi so a pixel
|
|
14
|
+
* is a point and the page is exactly the size of the picture on it.
|
|
15
|
+
*/
|
|
16
|
+
export declare function imagesToPdf(pages: readonly PdfPageSource[], title: string): Blob;
|
|
17
|
+
//# sourceMappingURL=image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEzD,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAA;AAEpC;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,CAuB7G;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAQhF"}
|
package/dist/image.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Re-encoding a picture for embedding in a PDF.
|
|
2
|
+
//
|
|
3
|
+
// Lifted out of Universal Converter's `pdf.ts` when the document stack moved
|
|
4
|
+
// here, because the document WRITER needs it (a `{ kind: 'image' }` block) and
|
|
5
|
+
// the rest of that file — one-full-bleed-page-per-image — is a different
|
|
6
|
+
// product feature that stayed in the app.
|
|
7
|
+
//
|
|
8
|
+
// EVERY EMBEDDED IMAGE IS A JPEG, AND THAT IS A REAL TRADE. `/DCTDecode` is the
|
|
9
|
+
// one filter every reader supports and the only one that lets the compressed
|
|
10
|
+
// bytes go in untouched. The cost is that it is LOSSY (a PNG screenshot of text
|
|
11
|
+
// softens slightly) and that TRANSPARENCY IS FLATTENED onto white, because JPEG
|
|
12
|
+
// has no alpha. The alternative, `/FlateDecode` with raw RGB, needs a deflate
|
|
13
|
+
// encoder and would turn a 2 MP photo into a 20 MB page.
|
|
14
|
+
// ⚠️ `.js`, not `.ts` and not extensionless — the TypeScript-ESM convention
|
|
15
|
+
// that @unisim/media already follows, and it is load-bearing twice over. This
|
|
16
|
+
// package EMITS, so `allowImportingTsExtensions` (which needs noEmit) is out;
|
|
17
|
+
// and extensionless specifiers are emitted VERBATIM, so `dist/index.js` would
|
|
18
|
+
// import './convert' and Node would refuse it with ERR_MODULE_NOT_FOUND. That
|
|
19
|
+
// is not hypothetical: it is what the first build of this package did.
|
|
20
|
+
import { PdfDocument } from './pdfcore.js';
|
|
21
|
+
/**
|
|
22
|
+
* Draw a file to a canvas and read it back as JPEG.
|
|
23
|
+
*
|
|
24
|
+
* The white fill is not cosmetic: a canvas starts transparent, and drawing a
|
|
25
|
+
* PNG with alpha onto it and asking for JPEG gives BLACK where the transparency
|
|
26
|
+
* was on some browsers and white on others. Filling first makes it white
|
|
27
|
+
* everywhere, which is what somebody printing a page expects.
|
|
28
|
+
*/
|
|
29
|
+
export async function imageToJpeg(file, quality, maxEdge) {
|
|
30
|
+
const bitmap = await createImageBitmap(file);
|
|
31
|
+
let { width, height } = bitmap;
|
|
32
|
+
if (maxEdge && Math.max(width, height) > maxEdge) {
|
|
33
|
+
const scale = maxEdge / Math.max(width, height);
|
|
34
|
+
width = Math.max(1, Math.round(width * scale));
|
|
35
|
+
height = Math.max(1, Math.round(height * scale));
|
|
36
|
+
}
|
|
37
|
+
const canvas = document.createElement('canvas');
|
|
38
|
+
canvas.width = width;
|
|
39
|
+
canvas.height = height;
|
|
40
|
+
const ctx = canvas.getContext('2d');
|
|
41
|
+
if (!ctx)
|
|
42
|
+
throw new Error('This browser would not give us a canvas to draw on.');
|
|
43
|
+
ctx.fillStyle = '#ffffff';
|
|
44
|
+
ctx.fillRect(0, 0, width, height);
|
|
45
|
+
ctx.drawImage(bitmap, 0, 0, width, height);
|
|
46
|
+
bitmap.close?.();
|
|
47
|
+
const blob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/jpeg', quality));
|
|
48
|
+
if (!blob)
|
|
49
|
+
throw new Error('This browser could not encode a JPEG.');
|
|
50
|
+
return { jpeg: new Uint8Array(await blob.arrayBuffer()), width, height };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Assemble pages into a PDF — one image per page, laid out at 72 dpi so a pixel
|
|
54
|
+
* is a point and the page is exactly the size of the picture on it.
|
|
55
|
+
*/
|
|
56
|
+
export function imagesToPdf(pages, title) {
|
|
57
|
+
if (!pages.length)
|
|
58
|
+
throw new Error('A PDF needs at least one page.');
|
|
59
|
+
const pdf = new PdfDocument();
|
|
60
|
+
for (const image of pages) {
|
|
61
|
+
const page = pdf.addPage(image.width, image.height);
|
|
62
|
+
page.drawImage(image, { x: 0, y: 0, width: image.width, height: image.height });
|
|
63
|
+
}
|
|
64
|
+
return pdf.save({ title });
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.js","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type { Block, DocNotice, Grid, ListItem, RichDoc, Run, } from './model.js';
|
|
2
|
+
export { DEFAULT_DOC_SETTINGS, DEFAULT_PDF_SETTINGS, DOC_INPUT_EXTS, commonTargets, convertDocument, isTabular, readToDoc, targetsFor, } from './convert.js';
|
|
3
|
+
export type { DocFormat, DocResult, DocSettings, FontChoice, PageMargin, PaperSize, PdfSettings, } from './convert.js';
|
|
4
|
+
export { mergeToPdf, readForMerge } from './merge.js';
|
|
5
|
+
export type { MergePart } from './merge.js';
|
|
6
|
+
export { PdfDocument, PdfPage, rgb, widthOfText } from './pdfcore.js';
|
|
7
|
+
export type { FontId, PdfImage, Rgb } from './pdfcore.js';
|
|
8
|
+
export { imageToJpeg, imagesToPdf } from './image.js';
|
|
9
|
+
export type { PdfPageSource } from './image.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2CA,YAAY,EACV,KAAK,EACL,SAAS,EACT,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,GAAG,GACJ,MAAM,YAAY,CAAA;AAGnB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACT,SAAS,EACT,UAAU,GACX,MAAM,cAAc,CAAA;AACrB,YAAY,EACV,SAAS,EACT,SAAS,EACT,WAAW,EACX,UAAU,EACV,UAAU,EACV,SAAS,EACT,WAAW,GACZ,MAAM,cAAc,CAAA;AAGrB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACrD,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAK3C,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AACrE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACrD,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @unisim/doc — the suite's document stack.
|
|
3
|
+
//
|
|
4
|
+
// WHY THIS PACKAGE EXISTS
|
|
5
|
+
// -----------------------
|
|
6
|
+
// It existed twice. On 2026-08-13, two sessions that could not see each other
|
|
7
|
+
// shipped the same 1,200–1,500 lines on the same afternoon: Universal PDF's
|
|
8
|
+
// `unzip.ts` / `officeXml.ts` / `docxToBlocks.ts` / `odtToBlocks.ts` /
|
|
9
|
+
// `blockPdf.ts`, and Universal Converter's `doc/` tree. A ZIP reader, a
|
|
10
|
+
// namespace-agnostic XML layer, an OOXML walker, an ODF walker, and a
|
|
11
|
+
// tokenise → wrap → paginate layout engine — twice, and diverging from the day
|
|
12
|
+
// they landed. Both files carried a header saying "a fix to one is a fix to
|
|
13
|
+
// neither".
|
|
14
|
+
//
|
|
15
|
+
// This is Converter's version, because Converter's was ahead on substance: a
|
|
16
|
+
// model designed as a hub (nine readers × six writers) rather than grown out of
|
|
17
|
+
// a Markdown renderer, with heading level 4, real image blocks, `underline` and
|
|
18
|
+
// `strike` on a run, and `DocNotice` — a channel for reporting what a
|
|
19
|
+
// conversion LOST, which the other had no equivalent of. It also fixed defects
|
|
20
|
+
// the original still had: smart quotes and dashes survive (Universal PDF's
|
|
21
|
+
// `sanitize()` flattened '’' to "'" and '—' to '-', though both are WinAnsi and
|
|
22
|
+
// encode fine), and a hard line break inside a paragraph is honoured.
|
|
23
|
+
//
|
|
24
|
+
// Universal PDF's model had exactly one thing this lacked — `pagesetup`, added
|
|
25
|
+
// 2026-08-20 for per-page orientation on import — so the merge is a superset.
|
|
26
|
+
//
|
|
27
|
+
// ONE MODEL AND ONE ENGINE, BUT TWO WRITERS ON PURPOSE
|
|
28
|
+
// ----------------------------------------------------
|
|
29
|
+
// The obvious next step, unifying the PDF writer too, is the wrong one. This
|
|
30
|
+
// package writes PDFs through `pdfcore` — its own base-14 fonts and AFM tables,
|
|
31
|
+
// no dependency — because Universal Converter's whole pitch is
|
|
32
|
+
// offline-from-first-visit with nothing fetched. Universal PDF carries pdf-lib
|
|
33
|
+
// anyway for signing, redaction and forms, so it gains nothing by dropping it.
|
|
34
|
+
// A consumer that would rather render through pdf-lib can take the readers and
|
|
35
|
+
// the model from here and keep its own writer; the seam is that `write/pdf.ts`
|
|
36
|
+
// depends only on `pdfcore`'s small surface (addPage / drawText / drawRect /
|
|
37
|
+
// drawImage / widthOfText), not on the rest of the package.
|
|
38
|
+
//
|
|
39
|
+
// NO RUNTIME DEPENDENCIES, and it should stay that way — that constraint is the
|
|
40
|
+
// product for one of its two consumers.
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// ---- Reading, writing, and the orchestration over both ---------------------
|
|
43
|
+
export { DEFAULT_DOC_SETTINGS, DEFAULT_PDF_SETTINGS, DOC_INPUT_EXTS, commonTargets, convertDocument, isTabular, readToDoc, targetsFor, } from './convert.js';
|
|
44
|
+
// ---- Joining several documents into one -----------------------------------
|
|
45
|
+
export { mergeToPdf, readForMerge } from './merge.js';
|
|
46
|
+
// ---- The PDF primitives, for a consumer that wants them directly ----------
|
|
47
|
+
// Universal Converter's "images → one PDF" feature builds pages itself rather
|
|
48
|
+
// than going through the document model, so the writer surface is public.
|
|
49
|
+
export { PdfDocument, PdfPage, rgb, widthOfText } from './pdfcore.js';
|
|
50
|
+
export { imageToJpeg, imagesToPdf } from './image.js';
|
|
51
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,4CAA4C;AAC5C,EAAE;AACF,0BAA0B;AAC1B,0BAA0B;AAC1B,8EAA8E;AAC9E,4EAA4E;AAC5E,uEAAuE;AACvE,wEAAwE;AACxE,sEAAsE;AACtE,+EAA+E;AAC/E,4EAA4E;AAC5E,YAAY;AACZ,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,gFAAgF;AAChF,sEAAsE;AACtE,+EAA+E;AAC/E,2EAA2E;AAC3E,gFAAgF;AAChF,sEAAsE;AACtE,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,EAAE;AACF,uDAAuD;AACvD,uDAAuD;AACvD,6EAA6E;AAC7E,gFAAgF;AAChF,+DAA+D;AAC/D,+EAA+E;AAC/E,+EAA+E;AAC/E,+EAA+E;AAC/E,+EAA+E;AAC/E,6EAA6E;AAC7E,4DAA4D;AAC5D,EAAE;AACF,gFAAgF;AAChF,wCAAwC;AACxC,8EAA8E;AAY9E,+EAA+E;AAC/E,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACT,SAAS,EACT,UAAU,GACX,MAAM,cAAc,CAAA;AAWrB,8EAA8E;AAC9E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAGrD,8EAA8E;AAC9E,8EAA8E;AAC9E,0EAA0E;AAC1E,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAErE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/merge.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type RichDoc } from './model.js';
|
|
2
|
+
import type { PdfSettings } from './write/pdfSettings.js';
|
|
3
|
+
export interface MergePart {
|
|
4
|
+
name: string;
|
|
5
|
+
doc: RichDoc;
|
|
6
|
+
}
|
|
7
|
+
/** Read one file to a RichDoc, whichever kind it is. */
|
|
8
|
+
export declare function readForMerge(file: File): Promise<MergePart>;
|
|
9
|
+
export declare function mergeToPdf(parts: readonly MergePart[], settings: PdfSettings): Promise<{
|
|
10
|
+
blob: Blob;
|
|
11
|
+
pages: number;
|
|
12
|
+
notices: RichDoc['notices'];
|
|
13
|
+
}>;
|
|
14
|
+
//# sourceMappingURL=merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAeA,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,YAAY,CAAA;AAEpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAEzD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,OAAO,CAAA;CACb;AAED,wDAAwD;AACxD,wBAAsB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC,CAGjE;AAED,wBAAsB,UAAU,CAC9B,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,QAAQ,EAAE,WAAW,GACpB,OAAO,CAAC;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;CAAE,CAAC,CAgCrE"}
|
package/dist/merge.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Several documents → one PDF.
|
|
3
|
+
//
|
|
4
|
+
// Deliberately NOT a second pipeline. Each file goes through the same reader it
|
|
5
|
+
// would on its own, the resulting RichDocs are concatenated into one, and that
|
|
6
|
+
// goes through the same writer with the same settings. The only thing this file
|
|
7
|
+
// adds is the join: a page break between documents and a heading naming each.
|
|
8
|
+
//
|
|
9
|
+
// The alternative — writing each to its own PDF and stitching the byte streams
|
|
10
|
+
// — needs a PDF *parser* to renumber every object, which is the thing
|
|
11
|
+
// `pdfcore.ts` explicitly does not do. Joining at the model level is both less
|
|
12
|
+
// code and a better result: the page numbers run 1..n across the whole thing
|
|
13
|
+
// rather than restarting per document.
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
import { addNotice } from './model.js';
|
|
16
|
+
import { docToPdf } from './write/pdf.js';
|
|
17
|
+
/** Read one file to a RichDoc, whichever kind it is. */
|
|
18
|
+
export async function readForMerge(file) {
|
|
19
|
+
const { readToDoc } = await import('./index.js');
|
|
20
|
+
return { name: file.name, doc: await readToDoc(file) };
|
|
21
|
+
}
|
|
22
|
+
export async function mergeToPdf(parts, settings) {
|
|
23
|
+
if (!parts.length)
|
|
24
|
+
throw new Error('There are no documents to join.');
|
|
25
|
+
const merged = { blocks: [], notices: [], title: 'Documents' };
|
|
26
|
+
parts.forEach((part, index) => {
|
|
27
|
+
// A page break BEFORE each document except the first: putting one after
|
|
28
|
+
// each instead leaves a blank final page.
|
|
29
|
+
if (index > 0)
|
|
30
|
+
merged.blocks.push({ kind: 'pagebreak' });
|
|
31
|
+
// The filename as a heading. Without it a joined PDF is one undifferentiated
|
|
32
|
+
// run of text and there is no way to tell where one document ended.
|
|
33
|
+
merged.blocks.push({
|
|
34
|
+
kind: 'heading',
|
|
35
|
+
level: 1,
|
|
36
|
+
runs: [{ text: part.name.replace(/\.[^.]+$/, '') }],
|
|
37
|
+
});
|
|
38
|
+
merged.blocks.push(...part.doc.blocks);
|
|
39
|
+
for (const notice of part.doc.notices) {
|
|
40
|
+
// Prefixed with the filename: in a joined batch "only its text could be
|
|
41
|
+
// read" is useless without knowing which of the six it refers to.
|
|
42
|
+
addNotice(merged, `${part.name}: ${notice.message}`, notice.severity);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
const result = await docToPdf(merged, settings);
|
|
46
|
+
if (result.dropped.length) {
|
|
47
|
+
addNotice(merged, `Some characters aren’t in the PDF’s built-in fonts and came out as “?”: ${result.dropped.slice(0, 12).join(' ')}`);
|
|
48
|
+
}
|
|
49
|
+
return { blob: result.blob, pages: result.pages, notices: merged.notices };
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,+BAA+B;AAC/B,EAAE;AACF,gFAAgF;AAChF,+EAA+E;AAC/E,gFAAgF;AAChF,8EAA8E;AAC9E,EAAE;AACF,+EAA+E;AAC/E,sEAAsE;AACtE,+EAA+E;AAC/E,6EAA6E;AAC7E,uCAAuC;AACvC,8EAA8E;AAE9E,OAAO,EAAE,SAAS,EAAgB,MAAM,YAAY,CAAA;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAQzC,wDAAwD;AACxD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAU;IAC3C,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAA;IAChD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC,IAAI,CAAC,EAAE,CAAA;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,KAA2B,EAC3B,QAAqB;IAErB,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IAErE,MAAM,MAAM,GAAY,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAA;IAEvE,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC5B,wEAAwE;QACxE,0CAA0C;QAC1C,IAAI,KAAK,GAAG,CAAC;YAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAA;QACxD,6EAA6E;QAC7E,oEAAoE;QACpE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;YACjB,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,CAAC;YACR,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC;SACpD,CAAC,CAAA;QACF,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACtC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;YACtC,wEAAwE;YACxE,kEAAkE;YAClE,SAAS,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;QACvE,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAC/C,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QAC1B,SAAS,CACP,MAAM,EACN,2EAA2E,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CACnH,CAAA;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAA;AAC5E,CAAC"}
|