reamkit 1.13.0 → 1.15.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 +34 -8
- package/dist/esm/core/converter/facade.d.ts +8 -1
- package/dist/esm/core/converter/facade.js +34 -8
- package/dist/esm/core/converter/ream.d.ts +1 -0
- package/dist/esm/core/converter/ream.js +13 -8
- package/dist/esm/core/document-model/types.d.ts +2 -0
- package/dist/esm/core/ir/features.d.ts +1 -0
- package/dist/esm/core/ir/features.js +1 -0
- package/dist/esm/core/ir/sheet.d.ts +42 -3
- package/dist/esm/core/ole/cfb.d.ts +19 -0
- package/dist/esm/core/ole/cfb.js +158 -0
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +81 -1
- package/dist/esm/excel/activex-parser.d.ts +9 -0
- package/dist/esm/excel/activex-parser.js +188 -0
- package/dist/esm/excel/comments-parser.d.ts +4 -0
- package/dist/esm/excel/comments-parser.js +104 -0
- package/dist/esm/excel/conditional-format.d.ts +5 -3
- package/dist/esm/excel/conditional-format.js +248 -12
- package/dist/esm/excel/form-control-parser.d.ts +6 -0
- package/dist/esm/excel/form-control-parser.js +29 -0
- package/dist/esm/excel/formula/context.d.ts +11 -0
- package/dist/esm/excel/formula/dates.d.ts +11 -0
- package/dist/esm/excel/formula/dates.js +46 -0
- package/dist/esm/excel/formula/eval.d.ts +11 -0
- package/dist/esm/excel/formula/eval.js +196 -0
- package/dist/esm/excel/formula/functions.d.ts +7 -0
- package/dist/esm/excel/formula/functions.js +1359 -0
- package/dist/esm/excel/formula/index.d.ts +14 -0
- package/dist/esm/excel/formula/index.js +17 -0
- package/dist/esm/excel/formula/lexer.d.ts +8 -0
- package/dist/esm/excel/formula/lexer.js +163 -0
- package/dist/esm/excel/formula/parser.d.ts +57 -0
- package/dist/esm/excel/formula/parser.js +274 -0
- package/dist/esm/excel/formula/value.d.ts +52 -0
- package/dist/esm/excel/formula/value.js +102 -0
- package/dist/esm/excel/header-footer.d.ts +2 -0
- package/dist/esm/excel/header-footer.js +127 -0
- package/dist/esm/excel/index.d.ts +1 -1
- package/dist/esm/excel/number-format.d.ts +2 -0
- package/dist/esm/excel/number-format.js +5 -1
- package/dist/esm/excel/print-model.d.ts +7 -2
- package/dist/esm/excel/print-model.js +123 -16
- package/dist/esm/excel/shared-strings-parser.d.ts +6 -1
- package/dist/esm/excel/shared-strings-parser.js +91 -12
- package/dist/esm/excel/sheet-drawing.d.ts +13 -1
- package/dist/esm/excel/sheet-drawing.js +47 -14
- package/dist/esm/excel/sheet-shape-parser.d.ts +4 -0
- package/dist/esm/excel/sheet-shape-parser.js +96 -0
- package/dist/esm/excel/sheet-to-flow.d.ts +4 -1
- package/dist/esm/excel/sheet-to-flow.js +170 -3
- package/dist/esm/excel/styles-parser.js +37 -0
- package/dist/esm/excel/worksheet-parser.js +191 -1
- package/dist/esm/excel/xls/biff-chart.d.ts +8 -0
- package/dist/esm/excel/xls/biff-chart.js +121 -0
- package/dist/esm/excel/xls/biff-reader.d.ts +4 -0
- package/dist/esm/excel/xls/biff-reader.js +1418 -0
- package/dist/esm/excel/xls/biff-styles.d.ts +8 -0
- package/dist/esm/excel/xls/biff-styles.js +299 -0
- package/dist/esm/excel/xls/escher.d.ts +24 -0
- package/dist/esm/excel/xls/escher.js +171 -0
- package/dist/esm/excel/xls/xls-reader.d.ts +3 -0
- package/dist/esm/excel/xls/xls-reader.js +30 -0
- package/dist/esm/excel/xlsx-reader.d.ts +2 -1
- package/dist/esm/excel/xlsx-reader.js +134 -16
- package/dist/esm/excel/xlsx-to-pdf.d.ts +1 -0
- package/dist/esm/excel/xlsx-to-pdf.js +2 -2
- package/dist/esm/excel/xlsx-writer.js +12 -2
- package/dist/esm/layout/styled-layout.js +29 -0
- package/dist/esm/pptx/ppt/ppt-reader.d.ts +4 -0
- package/dist/esm/pptx/ppt/ppt-reader.js +337 -0
- package/dist/esm/pptx/ppt/ppt-text.d.ts +69 -0
- package/dist/esm/pptx/ppt/ppt-text.js +756 -0
- package/dist/esm/pptx/slide-parser.d.ts +4 -2
- package/dist/esm/pptx/slide-parser.js +1 -1
- package/dist/esm/word/doc/doc-reader.d.ts +4 -0
- package/dist/esm/word/doc/doc-reader.js +421 -0
- package/dist/esm/word/doc/doc-text.d.ts +79 -0
- package/dist/esm/word/doc/doc-text.js +706 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
# Ream
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
> Read Word, Excel, PowerPoint and PDF — and convert any of them to PDF, SVG, HTML, DOCX or XLSX. From scratch, in the browser. No LibreOffice, no headless Office, no commercial SDK.
|
|
4
|
+
|
|
5
|
+
Ream parses **seven document formats** — the modern Office Open XML trio (`.docx`,
|
|
6
|
+
`.xlsx`, `.pptx`), `.pdf`, and the legacy binary `.doc` / `.xls` / `.ppt` — into one
|
|
7
|
+
format-neutral interlayer, then renders that to **PDF, SVG, HTML, DOCX or XLSX**.
|
|
8
|
+
It is implemented directly from the **ECMA-376** (OOXML), **ISO 32000 / 19005**
|
|
9
|
+
(PDF / PDF/A), and Microsoft's binary-format specifications — no wrapper around
|
|
10
|
+
LibreOffice, headless Office, or any commercial SDK. Pure TypeScript/JavaScript on
|
|
11
|
+
`Uint8Array` in and `Uint8Array` out, so the same code runs unchanged in the browser,
|
|
12
|
+
Node.js, serverless and edge runtimes.
|
|
13
|
+
|
|
14
|
+
| | |
|
|
15
|
+
| ---------- | ------------------------------------------------------------------------------------------- |
|
|
16
|
+
| **Reads** | `.docx` · `.xlsx` · `.pptx` · `.pdf` · legacy `.doc` · `.xls` · `.ppt` |
|
|
17
|
+
| **Writes** | `.pdf` (incl. PDF/A-1/2/3, PDF/UA-1, signed, encrypted) · `.svg` · `.html` · `.docx` · `.xlsx` |
|
|
10
18
|
|
|
11
19
|
## Install
|
|
12
20
|
|
|
@@ -168,7 +176,8 @@ fields)/footnotes and endnotes/hyperlinks and bookmarks/floating drawings/
|
|
|
168
176
|
images/tracked changes, SpreadsheetML grids,
|
|
169
177
|
number formats and the print model (gridlines, print area, fit-to-page,
|
|
170
178
|
repeated titles, page breaks), **conditional formatting** (color scales, data
|
|
171
|
-
bars, icon sets
|
|
179
|
+
bars, icon sets, and `expression` rules evaluated by a ~140-function formula
|
|
180
|
+
engine), **sparklines** and **Excel tables**, DrawingML shapes and
|
|
172
181
|
charts, OMML math, Type0+CIDFontType2 embedding with subsetting, Knuth-Plass
|
|
173
182
|
line breaking, Liang hyphenation, OpenType ligatures/kerning + Arabic cursive
|
|
174
183
|
joining, BiDi (UAX #9), hyperlinks (PDF link annotations + HTML anchors,
|
|
@@ -193,6 +202,23 @@ bullets and indents), layout/master placeholders, pictures, shapes, DrawingML
|
|
|
193
202
|
tables, embedded charts, theme colours, slide backgrounds, grouped shapes and
|
|
194
203
|
hyperlinks — then converts onward to PDF, SVG, HTML or DOCX like any source.
|
|
195
204
|
|
|
205
|
+
**Reads legacy `.doc`, `.xls` and `.ppt`, too.** The binary Word / Excel /
|
|
206
|
+
PowerPoint 97–2003 formats (OLE2/CFB) parse through a shared container reader: a
|
|
207
|
+
`.doc` yields its text with run and paragraph formatting, tables (with cell
|
|
208
|
+
borders, vertical merges and background shading), inline images, fields,
|
|
209
|
+
headers/footers and lists (numbered or bulleted, in their number format);
|
|
210
|
+
an `.xls` yields the grid with styling, embedded
|
|
211
|
+
images, charts, drawing shapes, cell hyperlinks, the page-setup print model,
|
|
212
|
+
defined names (named ranges, print area, repeated titles), cell comments, data
|
|
213
|
+
validation, frozen panes, custom row heights and conditional formatting (the
|
|
214
|
+
classic cellIs / expression rules **and** the 2007 colour-scale / data-bar /
|
|
215
|
+
icon-set extensions); a `.ppt` yields each slide's
|
|
216
|
+
text (with run and paragraph formatting), embedded images, per-shape placement
|
|
217
|
+
(anchored text boxes and pictures at their slide rectangles) and decorative
|
|
218
|
+
autoshapes (preset or exact freeform geometry, with fill / line colours resolved
|
|
219
|
+
through the slide's colour scheme), one page per slide — all convert onward to PDF,
|
|
220
|
+
SVG, HTML, or back to `.docx` / `.xlsx` like any source.
|
|
221
|
+
|
|
196
222
|
See [`CHANGELOG.md`](./CHANGELOG.md) for the release history; the docs
|
|
197
223
|
[**Scope**](https://reamkit.dev/guides/scope/) guide has the full feature matrix
|
|
198
224
|
and known limitations.
|
|
@@ -5,6 +5,7 @@ import { SheetDoc } from '../ir/sheet.js';
|
|
|
5
5
|
import { FontProvider } from '../fonts/provider.js';
|
|
6
6
|
import { Loss, LossReport } from '../ir/index.js';
|
|
7
7
|
import { FontBytesByVariant } from '../font/index.js';
|
|
8
|
+
import { ProjectSheetOptions } from '../../excel/sheet-to-flow.js';
|
|
8
9
|
export interface ConvertOptions extends ConvertDocxOptions {
|
|
9
10
|
/**
|
|
10
11
|
* Target: 'pdf' (default), 'svg' (page-stack preview), 'html'/'docx' (flowed),
|
|
@@ -16,6 +17,12 @@ export interface ConvertOptions extends ConvertDocxOptions {
|
|
|
16
17
|
* recorded loss instead of returning it in the report.
|
|
17
18
|
*/
|
|
18
19
|
readonly strict?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* E-SHEET W9 — reference date for conditional-format `timePeriod` rules and
|
|
22
|
+
* TODAY()/NOW() in `expression` rules (spreadsheet input). An explicit input,
|
|
23
|
+
* never the wall clock; omitted ⇒ those clock-relative rules no-op.
|
|
24
|
+
*/
|
|
25
|
+
readonly now?: Date;
|
|
19
26
|
/**
|
|
20
27
|
* Font resolution chain (ir-design §8). When set (and no caller `fonts`),
|
|
21
28
|
* the facade resolves the default font set through these providers — e.g.
|
|
@@ -31,7 +38,7 @@ export interface ConvertResult {
|
|
|
31
38
|
readonly losses: LossReport;
|
|
32
39
|
}
|
|
33
40
|
export type SourceDoc = FlowDoc | SheetDoc;
|
|
34
|
-
export declare function toFlowDoc(doc: SourceDoc): FlowDoc;
|
|
41
|
+
export declare function toFlowDoc(doc: SourceDoc, options?: ProjectSheetOptions): FlowDoc;
|
|
35
42
|
export interface Converter {
|
|
36
43
|
/** The registered readers, in sniffing order. */
|
|
37
44
|
readonly readers: ReadonlyArray<DocumentReader<SourceDoc>>;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { fetchFontSet } from "../fonts/remote-fonts.js";
|
|
1
2
|
import { FontRegistry } from "../font/font-registry.js";
|
|
2
3
|
import { FEATURES } from "../ir/features.js";
|
|
3
4
|
import { ConversionLossError } from "../ir/loss.js";
|
|
4
5
|
import { layoutStyledDocument } from "../../layout/styled-layout.js";
|
|
6
|
+
import { renderStyledPdf } from "../../pdf/styled-page-renderer.js";
|
|
7
|
+
import "../../pdf/index.js";
|
|
5
8
|
import { pptxReader } from "../../pptx/pptx-reader.js";
|
|
6
9
|
import { docxReader } from "../../word/docx-reader.js";
|
|
7
10
|
import { projectSheetDoc } from "../../excel/sheet-to-flow.js";
|
|
@@ -14,22 +17,28 @@ import { writeHtml } from "../../html/html-writer.js";
|
|
|
14
17
|
import { writeSvg } from "../../svg/svg-writer.js";
|
|
15
18
|
import { convertDocxToPdf } from "../../word/docx-to-pdf.js";
|
|
16
19
|
import { convertXlsxToPdf } from "../../excel/xlsx-to-pdf.js";
|
|
20
|
+
import { docReader } from "../../word/doc/doc-reader.js";
|
|
21
|
+
import { xlsReader } from "../../excel/xls/xls-reader.js";
|
|
22
|
+
import { pptReader } from "../../pptx/ppt/ppt-reader.js";
|
|
17
23
|
import { pdfReader } from "../../pdf-reader/reader.js";
|
|
18
24
|
//#region src/core/converter/facade.ts
|
|
19
|
-
function toFlowDoc(doc) {
|
|
20
|
-
return doc.kind === "sheet" ? projectSheetDoc(doc) : doc;
|
|
25
|
+
function toFlowDoc(doc, options = {}) {
|
|
26
|
+
return doc.kind === "sheet" ? projectSheetDoc(doc, options) : doc;
|
|
21
27
|
}
|
|
22
|
-
function readToFlow(reader, bytes) {
|
|
28
|
+
function readToFlow(reader, bytes, options = {}) {
|
|
23
29
|
const { doc, losses } = reader.read(bytes);
|
|
24
30
|
return {
|
|
25
|
-
doc: toFlowDoc(doc),
|
|
31
|
+
doc: toFlowDoc(doc, options),
|
|
26
32
|
losses
|
|
27
33
|
};
|
|
28
34
|
}
|
|
29
35
|
var DEFAULT_READERS = [
|
|
30
36
|
docxReader,
|
|
37
|
+
docReader,
|
|
31
38
|
xlsxReader,
|
|
39
|
+
xlsReader,
|
|
32
40
|
pptxReader,
|
|
41
|
+
pptReader,
|
|
33
42
|
pdfReader
|
|
34
43
|
];
|
|
35
44
|
function createConverter(opts = {}) {
|
|
@@ -41,7 +50,7 @@ function createConverter(opts = {}) {
|
|
|
41
50
|
if (!reader) throw new Error("Unrecognized input format (no registered reader sniffs these bytes)");
|
|
42
51
|
const losses = [];
|
|
43
52
|
if (to === "docx") {
|
|
44
|
-
const { doc: flow, losses: readLosses } = readToFlow(reader, bytes);
|
|
53
|
+
const { doc: flow, losses: readLosses } = readToFlow(reader, bytes, rest.now ? { now: rest.now } : void 0);
|
|
45
54
|
losses.push(...readLosses);
|
|
46
55
|
const out = writeDocx(flow);
|
|
47
56
|
losses.push(...out.losses);
|
|
@@ -52,7 +61,7 @@ function createConverter(opts = {}) {
|
|
|
52
61
|
};
|
|
53
62
|
}
|
|
54
63
|
if (to === "html") {
|
|
55
|
-
const { doc: flow, losses: readLosses } = readToFlow(reader, bytes);
|
|
64
|
+
const { doc: flow, losses: readLosses } = readToFlow(reader, bytes, rest.now ? { now: rest.now } : void 0);
|
|
56
65
|
losses.push(...readLosses);
|
|
57
66
|
const html = writeHtml(flow);
|
|
58
67
|
losses.push(...html.losses);
|
|
@@ -86,7 +95,7 @@ function createConverter(opts = {}) {
|
|
|
86
95
|
if (to === "svg") {
|
|
87
96
|
const fonts = conv.fonts ?? (conv.fontBytes ? { regular: conv.fontBytes } : void 0);
|
|
88
97
|
if (!fonts) throw new Error("to: 'svg' requires options.fonts/fontBytes or fontProviders");
|
|
89
|
-
const { doc: flow } = readToFlow(reader, bytes);
|
|
98
|
+
const { doc: flow } = readToFlow(reader, bytes, rest.now ? { now: rest.now } : void 0);
|
|
90
99
|
const svg = writeSvg(layoutStyledDocument(flow.body, {
|
|
91
100
|
registry: FontRegistry.fromBytes(fonts),
|
|
92
101
|
...flowRenderOptions(flow)
|
|
@@ -98,7 +107,10 @@ function createConverter(opts = {}) {
|
|
|
98
107
|
losses
|
|
99
108
|
};
|
|
100
109
|
}
|
|
101
|
-
|
|
110
|
+
let pdf;
|
|
111
|
+
if (reader.id === "xlsx") pdf = await convertXlsxToPdf(bytes, conv);
|
|
112
|
+
else if (reader.produces === "sheet") pdf = await renderSheetReaderToPdf(reader, bytes, conv, rest.now);
|
|
113
|
+
else pdf = await convertDocxToPdf(bytes, conv);
|
|
102
114
|
if (strict && losses.length > 0) throw new ConversionLossError(losses[0]);
|
|
103
115
|
return {
|
|
104
116
|
bytes: pdf,
|
|
@@ -111,6 +123,20 @@ function createConverter(opts = {}) {
|
|
|
111
123
|
convert
|
|
112
124
|
};
|
|
113
125
|
}
|
|
126
|
+
async function renderSheetReaderToPdf(reader, bytes, conv, now) {
|
|
127
|
+
const fonts = conv.fonts ?? (conv.fontBytes ? { regular: conv.fontBytes } : await fetchFontSet({
|
|
128
|
+
...conv.fontFamily ? { family: conv.fontFamily } : {},
|
|
129
|
+
...conv.fontFetch ? { fetch: conv.fontFetch } : {}
|
|
130
|
+
}));
|
|
131
|
+
const { doc: flow } = readToFlow(reader, bytes, now ? { now } : void 0);
|
|
132
|
+
return renderStyledPdf(flow.body, {
|
|
133
|
+
registry: FontRegistry.fromBytes(fonts),
|
|
134
|
+
...flowRenderOptions(flow),
|
|
135
|
+
...conv.pdfA ? { pdfA: conv.pdfA } : {},
|
|
136
|
+
...conv.info ? { info: conv.info } : {},
|
|
137
|
+
...conv.section ? { section: conv.section } : {}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
114
140
|
async function resolveFontsViaChain(providers) {
|
|
115
141
|
const chain = chainProviders(providers);
|
|
116
142
|
const ask = (bold, italic) => chain.resolve({
|
|
@@ -21,6 +21,7 @@ export interface ReamConvertOptions extends Omit<StyledRenderOptions, 'registry'
|
|
|
21
21
|
readonly strict?: boolean;
|
|
22
22
|
readonly embedSource?: boolean;
|
|
23
23
|
readonly signature?: SignatureOptions;
|
|
24
|
+
readonly now?: Date;
|
|
24
25
|
}
|
|
25
26
|
export declare class Ream {
|
|
26
27
|
readonly flow: FlowDoc;
|
|
@@ -5,6 +5,7 @@ import { layoutStyledDocument } from "../../layout/styled-layout.js";
|
|
|
5
5
|
import { signPdf } from "../../pdf/signature.js";
|
|
6
6
|
import { renderStyledPdf, renderStyledPdfEncrypted } from "../../pdf/styled-page-renderer.js";
|
|
7
7
|
import "../../pdf/index.js";
|
|
8
|
+
import { projectSheetDoc } from "../../excel/sheet-to-flow.js";
|
|
8
9
|
import { flowRenderOptions } from "./project.js";
|
|
9
10
|
import { writeDocx } from "../../word/docx-writer.js";
|
|
10
11
|
import { writeXlsx } from "../../excel/xlsx-writer.js";
|
|
@@ -15,8 +16,11 @@ import { DEFAULT_READERS, resolveFontsViaChain, toFlowDoc } from "./facade.js";
|
|
|
15
16
|
//#region src/core/converter/ream.ts
|
|
16
17
|
var SOURCE_MIME = {
|
|
17
18
|
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
19
|
+
doc: "application/msword",
|
|
18
20
|
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
21
|
+
xls: "application/vnd.ms-excel",
|
|
19
22
|
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
23
|
+
ppt: "application/vnd.ms-powerpoint",
|
|
20
24
|
pdf: "application/pdf"
|
|
21
25
|
};
|
|
22
26
|
var Ream = class Ream {
|
|
@@ -43,8 +47,9 @@ var Ream = class Ream {
|
|
|
43
47
|
}
|
|
44
48
|
async convertWithReport(to, options = {}) {
|
|
45
49
|
const losses = [...this.losses];
|
|
50
|
+
const flow = this.sheet && options.now ? projectSheetDoc(this.sheet, { now: options.now }) : this.flow;
|
|
46
51
|
if (to === "html") {
|
|
47
|
-
const html = writeHtml(
|
|
52
|
+
const html = writeHtml(flow);
|
|
48
53
|
losses.push(...html.losses);
|
|
49
54
|
this.enforceStrict(options, losses);
|
|
50
55
|
return {
|
|
@@ -53,7 +58,7 @@ var Ream = class Ream {
|
|
|
53
58
|
};
|
|
54
59
|
}
|
|
55
60
|
if (to === "docx") {
|
|
56
|
-
const docx = writeDocx(
|
|
61
|
+
const docx = writeDocx(flow);
|
|
57
62
|
losses.push(...docx.losses);
|
|
58
63
|
this.enforceStrict(options, losses);
|
|
59
64
|
return {
|
|
@@ -74,9 +79,9 @@ var Ream = class Ream {
|
|
|
74
79
|
const { fonts, registriesByFamily } = await this.resolveFonts(options, losses);
|
|
75
80
|
const registry = FontRegistry.fromBytes(fonts);
|
|
76
81
|
if (to === "svg") {
|
|
77
|
-
const svg = writeSvg(layoutStyledDocument(
|
|
82
|
+
const svg = writeSvg(layoutStyledDocument(flow.body, {
|
|
78
83
|
registry,
|
|
79
|
-
...flowRenderOptions(
|
|
84
|
+
...flowRenderOptions(flow)
|
|
80
85
|
}));
|
|
81
86
|
losses.push(...svg.losses);
|
|
82
87
|
this.enforceStrict(options, losses);
|
|
@@ -86,8 +91,8 @@ var Ream = class Ream {
|
|
|
86
91
|
};
|
|
87
92
|
}
|
|
88
93
|
const { fonts: _a, fontBytes: _b, fontFamily: _c, fontFetch: _d, fontProviders: _e, strict: _f, embedSource, signature, info: callerInfo, attachments: callerAttachments, ...renderOptions } = options;
|
|
89
|
-
const info =
|
|
90
|
-
...
|
|
94
|
+
const info = flow.info || callerInfo ? {
|
|
95
|
+
...flow.info,
|
|
91
96
|
...callerInfo
|
|
92
97
|
} : void 0;
|
|
93
98
|
const attachments = [...callerAttachments ?? []];
|
|
@@ -101,13 +106,13 @@ var Ream = class Ream {
|
|
|
101
106
|
const styled = {
|
|
102
107
|
registry,
|
|
103
108
|
...registriesByFamily ? { registriesByFamily } : {},
|
|
104
|
-
...flowRenderOptions(
|
|
109
|
+
...flowRenderOptions(flow),
|
|
105
110
|
...info ? { info } : {},
|
|
106
111
|
...attachments.length > 0 ? { attachments } : {},
|
|
107
112
|
...signature ? { signaturePlaceholder: signature } : {},
|
|
108
113
|
...renderOptions
|
|
109
114
|
};
|
|
110
|
-
let pdf = styled.encrypt ? await renderStyledPdfEncrypted(
|
|
115
|
+
let pdf = styled.encrypt ? await renderStyledPdfEncrypted(flow.body, styled) : renderStyledPdf(flow.body, styled);
|
|
111
116
|
if (signature) pdf = await signPdf(pdf, signature);
|
|
112
117
|
this.enforceStrict(options, losses);
|
|
113
118
|
return {
|
|
@@ -235,6 +235,8 @@ export interface CellBorders {
|
|
|
235
235
|
readonly left?: Border;
|
|
236
236
|
readonly insideH?: Border;
|
|
237
237
|
readonly insideV?: Border;
|
|
238
|
+
readonly diagonalDown?: Border;
|
|
239
|
+
readonly diagonalUp?: Border;
|
|
238
240
|
}
|
|
239
241
|
export interface CellMargins {
|
|
240
242
|
readonly top?: Pt;
|
|
@@ -3,6 +3,7 @@ export declare const FEATURES: {
|
|
|
3
3
|
readonly text: "text";
|
|
4
4
|
readonly tables: "tables";
|
|
5
5
|
readonly tablesNested: "tables.nested";
|
|
6
|
+
readonly cellFormatting: "cellFormatting";
|
|
6
7
|
readonly lists: "lists";
|
|
7
8
|
readonly sections: "sections";
|
|
8
9
|
readonly headersFooters: "headersFooters";
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { Chart, DocumentInfo } from '../document-model/index.js';
|
|
2
|
-
import { DefinedName, ParsedWorksheet, XlsxStyles } from '../spreadsheet-model/index.js';
|
|
3
|
-
import { ResourceStore } from './resources.js';
|
|
1
|
+
import { Chart, DocumentInfo, ShapeBlock } from '../document-model/index.js';
|
|
2
|
+
import { DefinedName, MergedRange, ParsedWorksheet, SheetRichRun, XlsxStyles } from '../spreadsheet-model/index.js';
|
|
3
|
+
import { ResourceId, ResourceStore } from './resources.js';
|
|
4
4
|
export interface SheetChartRef {
|
|
5
5
|
readonly chartPartPath: string;
|
|
6
6
|
readonly widthPt: number;
|
|
7
7
|
readonly heightPt: number;
|
|
8
8
|
}
|
|
9
|
+
export interface SheetImageRef {
|
|
10
|
+
readonly resourceId: ResourceId;
|
|
11
|
+
readonly widthPt: number;
|
|
12
|
+
readonly heightPt: number;
|
|
13
|
+
}
|
|
9
14
|
export interface SheetSlicerItem {
|
|
10
15
|
readonly label: string;
|
|
11
16
|
readonly selected: boolean;
|
|
@@ -19,11 +24,39 @@ export interface SheetSlicer {
|
|
|
19
24
|
readonly headerTextHex?: string;
|
|
20
25
|
readonly selectedTextHex?: string;
|
|
21
26
|
}
|
|
27
|
+
export interface SheetHyperlink {
|
|
28
|
+
readonly ref: MergedRange;
|
|
29
|
+
readonly url: string;
|
|
30
|
+
}
|
|
31
|
+
export interface SheetComment {
|
|
32
|
+
readonly ref: string;
|
|
33
|
+
readonly author?: string;
|
|
34
|
+
readonly text: string;
|
|
35
|
+
readonly threaded: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface SheetFormControl {
|
|
38
|
+
readonly name?: string;
|
|
39
|
+
readonly objectType?: string;
|
|
40
|
+
readonly checked?: boolean;
|
|
41
|
+
readonly value?: number;
|
|
42
|
+
}
|
|
43
|
+
export interface SheetActiveXControl {
|
|
44
|
+
readonly type: string;
|
|
45
|
+
readonly caption?: string;
|
|
46
|
+
readonly value?: string;
|
|
47
|
+
readonly groupName?: string;
|
|
48
|
+
}
|
|
22
49
|
export interface Sheet {
|
|
23
50
|
readonly name: string;
|
|
24
51
|
readonly grid: ParsedWorksheet;
|
|
25
52
|
readonly charts?: ReadonlyArray<SheetChartRef>;
|
|
53
|
+
readonly images?: ReadonlyArray<SheetImageRef>;
|
|
54
|
+
readonly shapes?: ReadonlyArray<ShapeBlock>;
|
|
55
|
+
readonly hyperlinks?: ReadonlyArray<SheetHyperlink>;
|
|
26
56
|
readonly slicers?: ReadonlyArray<SheetSlicer>;
|
|
57
|
+
readonly comments?: ReadonlyArray<SheetComment>;
|
|
58
|
+
readonly formControls?: ReadonlyArray<SheetFormControl>;
|
|
59
|
+
readonly activeXControls?: ReadonlyArray<SheetActiveXControl>;
|
|
27
60
|
}
|
|
28
61
|
export interface SheetDoc {
|
|
29
62
|
readonly kind: 'sheet';
|
|
@@ -32,6 +65,12 @@ export interface SheetDoc {
|
|
|
32
65
|
readonly styles: XlsxStyles;
|
|
33
66
|
/** §18.4 shared string table; a cell with type 's' indexes into it. */
|
|
34
67
|
readonly sharedStrings: ReadonlyArray<string>;
|
|
68
|
+
/**
|
|
69
|
+
* §18.4.4 per-index rich-text runs (E-SHEET W6), parallel to sharedStrings —
|
|
70
|
+
* `[i]` is defined only when shared string i carries per-run formatting.
|
|
71
|
+
* Render-only: the writer flattens to plain text, so it is absent on round-trip.
|
|
72
|
+
*/
|
|
73
|
+
readonly sharedStringRuns?: ReadonlyArray<ReadonlyArray<SheetRichRun> | undefined>;
|
|
35
74
|
/** §18.2.5 workbook defined names (print areas/titles, named ranges). */
|
|
36
75
|
readonly definedNames: ReadonlyArray<DefinedName>;
|
|
37
76
|
/** §18.2.28 1904 date system (serial-to-date epoch). */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type CfbEntryType = 'root' | 'storage' | 'stream' | 'unknown';
|
|
2
|
+
export interface CfbEntry {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly type: CfbEntryType;
|
|
5
|
+
readonly startSector: number;
|
|
6
|
+
readonly size: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class CfbError extends Error {
|
|
9
|
+
}
|
|
10
|
+
export interface Cfb {
|
|
11
|
+
/** Every directory entry, in directory order (index 0 is the root storage). */
|
|
12
|
+
readonly entries: ReadonlyArray<CfbEntry>;
|
|
13
|
+
/** Read a stream's bytes by entry name (case-insensitive), or undefined. */
|
|
14
|
+
readStream: (name: string) => Uint8Array | undefined;
|
|
15
|
+
/** Whether a stream entry with that name exists (case-insensitive). */
|
|
16
|
+
hasStream: (name: string) => boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function isCfb(bytes: Uint8Array): boolean;
|
|
19
|
+
export declare function openCfb(bytes: Uint8Array): Cfb;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
//#region src/core/ole/cfb.ts
|
|
2
|
+
var SIGNATURE = [
|
|
3
|
+
208,
|
|
4
|
+
207,
|
|
5
|
+
17,
|
|
6
|
+
224,
|
|
7
|
+
161,
|
|
8
|
+
177,
|
|
9
|
+
26,
|
|
10
|
+
225
|
|
11
|
+
];
|
|
12
|
+
var ENDOFCHAIN = 4294967294;
|
|
13
|
+
var FREESECT = 4294967295;
|
|
14
|
+
var HEADER_SIZE = 512;
|
|
15
|
+
var DIR_ENTRY_SIZE = 128;
|
|
16
|
+
var MAX_TOTAL_BYTES = 512 * 1024 * 1024;
|
|
17
|
+
var CfbError = class extends Error {};
|
|
18
|
+
function isCfb(bytes) {
|
|
19
|
+
if (bytes.length < HEADER_SIZE) return false;
|
|
20
|
+
for (let i = 0; i < SIGNATURE.length; i++) if (bytes[i] !== SIGNATURE[i]) return false;
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
function openCfb(bytes) {
|
|
24
|
+
if (!isCfb(bytes)) throw new CfbError("not a compound file (bad signature)");
|
|
25
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
26
|
+
const sectorShift = view.getUint16(30, true);
|
|
27
|
+
const miniSectorShift = view.getUint16(32, true);
|
|
28
|
+
if (sectorShift !== 9 && sectorShift !== 12) throw new CfbError(`unsupported sector shift ${sectorShift}`);
|
|
29
|
+
if (miniSectorShift !== 6) throw new CfbError(`unsupported mini-sector shift ${miniSectorShift}`);
|
|
30
|
+
const sectorSize = 1 << sectorShift;
|
|
31
|
+
const miniSectorSize = 1 << miniSectorShift;
|
|
32
|
+
const miniCutoff = view.getUint32(56, true);
|
|
33
|
+
const totalSectors = Math.floor((bytes.length - HEADER_SIZE) / sectorSize);
|
|
34
|
+
const sectorOffset = (sector) => {
|
|
35
|
+
if (sector < 0 || sector >= totalSectors) throw new CfbError(`sector ${sector} out of range`);
|
|
36
|
+
return HEADER_SIZE + sector * sectorSize;
|
|
37
|
+
};
|
|
38
|
+
const firstDirSector = view.getUint32(48, true);
|
|
39
|
+
const firstMiniFatSector = view.getUint32(60, true);
|
|
40
|
+
const firstDifatSector = view.getUint32(68, true);
|
|
41
|
+
const numDifatSectors = view.getUint32(72, true);
|
|
42
|
+
const fatSectorLocs = [];
|
|
43
|
+
for (let i = 0; i < 109; i++) {
|
|
44
|
+
const loc = view.getUint32(76 + i * 4, true);
|
|
45
|
+
if (loc !== FREESECT && loc < totalSectors) fatSectorLocs.push(loc);
|
|
46
|
+
}
|
|
47
|
+
const perDifat = sectorSize / 4 - 1;
|
|
48
|
+
let difatSector = firstDifatSector;
|
|
49
|
+
for (let guard = 0; difatSector !== ENDOFCHAIN && difatSector !== FREESECT; guard++) {
|
|
50
|
+
if (guard > numDifatSectors || guard > totalSectors) throw new CfbError("DIFAT chain too long (corrupt)");
|
|
51
|
+
const off = sectorOffset(difatSector);
|
|
52
|
+
for (let i = 0; i < perDifat; i++) {
|
|
53
|
+
const loc = view.getUint32(off + i * 4, true);
|
|
54
|
+
if (loc !== FREESECT && loc < totalSectors) fatSectorLocs.push(loc);
|
|
55
|
+
}
|
|
56
|
+
difatSector = view.getUint32(off + perDifat * 4, true);
|
|
57
|
+
}
|
|
58
|
+
const entriesPerSector = sectorSize / 4;
|
|
59
|
+
const fat = new Uint32Array(fatSectorLocs.length * entriesPerSector);
|
|
60
|
+
for (let s = 0; s < fatSectorLocs.length; s++) {
|
|
61
|
+
const off = sectorOffset(fatSectorLocs[s]);
|
|
62
|
+
for (let i = 0; i < entriesPerSector; i++) fat[s * entriesPerSector + i] = view.getUint32(off + i * 4, true);
|
|
63
|
+
}
|
|
64
|
+
const followChain = (start) => {
|
|
65
|
+
const out = [];
|
|
66
|
+
let s = start;
|
|
67
|
+
while (s !== ENDOFCHAIN && s !== FREESECT) {
|
|
68
|
+
if (s >= totalSectors || out.length > totalSectors) throw new CfbError("FAT chain corrupt (out of range or cyclic)");
|
|
69
|
+
out.push(s);
|
|
70
|
+
s = fat[s] ?? ENDOFCHAIN;
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
};
|
|
74
|
+
const readChainBytes = (start, limit) => {
|
|
75
|
+
const sectors = followChain(start);
|
|
76
|
+
const total = sectors.length * sectorSize;
|
|
77
|
+
if (total > limit) throw new CfbError("stream exceeds size limit");
|
|
78
|
+
const out = new Uint8Array(total);
|
|
79
|
+
for (let i = 0; i < sectors.length; i++) {
|
|
80
|
+
const off = sectorOffset(sectors[i]);
|
|
81
|
+
out.set(bytes.subarray(off, off + sectorSize), i * sectorSize);
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
};
|
|
85
|
+
const dirBytes = readChainBytes(firstDirSector, MAX_TOTAL_BYTES);
|
|
86
|
+
const dirView = new DataView(dirBytes.buffer, dirBytes.byteOffset, dirBytes.byteLength);
|
|
87
|
+
const entries = [];
|
|
88
|
+
const entryCount = Math.floor(dirBytes.length / DIR_ENTRY_SIZE);
|
|
89
|
+
for (let i = 0; i < entryCount; i++) {
|
|
90
|
+
const base = i * DIR_ENTRY_SIZE;
|
|
91
|
+
const objType = dirView.getUint8(base + 66);
|
|
92
|
+
if (objType !== 1 && objType !== 2 && objType !== 5) continue;
|
|
93
|
+
const name = decodeName(dirBytes, base, dirView.getUint16(base + 64, true));
|
|
94
|
+
const sizeLow = dirView.getUint32(base + 120, true);
|
|
95
|
+
const size = dirView.getUint32(base + 124, true) * 4294967296 + sizeLow;
|
|
96
|
+
entries.push({
|
|
97
|
+
name,
|
|
98
|
+
type: objType === 5 ? "root" : objType === 1 ? "storage" : "stream",
|
|
99
|
+
startSector: dirView.getUint32(base + 116, true),
|
|
100
|
+
size
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const root = entries.find((e) => e.type === "root");
|
|
104
|
+
if (!root) throw new CfbError("compound file has no root entry");
|
|
105
|
+
let miniStream;
|
|
106
|
+
let miniFat;
|
|
107
|
+
const ensureMini = () => {
|
|
108
|
+
if (miniStream && miniFat) return;
|
|
109
|
+
miniStream = readChainBytes(root.startSector, MAX_TOTAL_BYTES).subarray(0, root.size);
|
|
110
|
+
const mfBytes = firstMiniFatSector === ENDOFCHAIN ? new Uint8Array(0) : readChainBytes(firstMiniFatSector, MAX_TOTAL_BYTES);
|
|
111
|
+
const mfView = new DataView(mfBytes.buffer, mfBytes.byteOffset, mfBytes.byteLength);
|
|
112
|
+
miniFat = new Uint32Array(Math.floor(mfBytes.length / 4));
|
|
113
|
+
for (let i = 0; i < miniFat.length; i++) miniFat[i] = mfView.getUint32(i * 4, true);
|
|
114
|
+
};
|
|
115
|
+
const readMiniStream = (entry) => {
|
|
116
|
+
ensureMini();
|
|
117
|
+
const stream = miniStream;
|
|
118
|
+
const mfat = miniFat;
|
|
119
|
+
const maxMini = Math.floor(stream.length / miniSectorSize);
|
|
120
|
+
const out = new Uint8Array(entry.size);
|
|
121
|
+
let mini = entry.startSector;
|
|
122
|
+
let written = 0;
|
|
123
|
+
let guard = 0;
|
|
124
|
+
while (mini !== ENDOFCHAIN && mini !== FREESECT && written < entry.size) {
|
|
125
|
+
if (mini >= maxMini || guard++ > mfat.length) throw new CfbError("mini-FAT chain corrupt (out of range or cyclic)");
|
|
126
|
+
const off = mini * miniSectorSize;
|
|
127
|
+
const n = Math.min(miniSectorSize, entry.size - written);
|
|
128
|
+
out.set(stream.subarray(off, off + n), written);
|
|
129
|
+
written += n;
|
|
130
|
+
mini = mfat[mini] ?? ENDOFCHAIN;
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
};
|
|
134
|
+
const readEntry = (entry) => {
|
|
135
|
+
if (entry.size === 0) return new Uint8Array(0);
|
|
136
|
+
if (entry.size > MAX_TOTAL_BYTES) throw new CfbError("stream exceeds size limit");
|
|
137
|
+
if (entry.size >= miniCutoff) return readChainBytes(entry.startSector, MAX_TOTAL_BYTES).subarray(0, entry.size);
|
|
138
|
+
return readMiniStream(entry);
|
|
139
|
+
};
|
|
140
|
+
const byName = /* @__PURE__ */ new Map();
|
|
141
|
+
for (const e of entries) if (e.type === "stream" && !byName.has(e.name.toLowerCase())) byName.set(e.name.toLowerCase(), e);
|
|
142
|
+
return {
|
|
143
|
+
entries,
|
|
144
|
+
hasStream: (name) => byName.has(name.toLowerCase()),
|
|
145
|
+
readStream: (name) => {
|
|
146
|
+
const e = byName.get(name.toLowerCase());
|
|
147
|
+
return e ? readEntry(e) : void 0;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function decodeName(dir, base, nameLen) {
|
|
152
|
+
const len = Math.max(0, Math.min(64, nameLen) - 2);
|
|
153
|
+
let out = "";
|
|
154
|
+
for (let i = 0; i < len; i += 2) out += String.fromCharCode(dir[base + i] | dir[base + i + 1] << 8);
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
export { isCfb, openCfb };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { CellType, WorksheetCell, ColumnWidth, MergedRange, RowHeight, XlsxPageMargins, XlsxPageSetup, XlsxPrintOptions, ParsedWorksheet, XlsxFont, XlsxFill, XlsxBorderStyleName, XlsxBorderEdge, XlsxBorder, XlsxHorizontalAlign, XlsxVerticalAlign, XlsxCellAlignment, XlsxCellXf, XlsxStyles, DefinedName, Dxf, CfOperator, CfRuleCellIs, CfvoType, Cfvo, CfRuleColorScale, CfRuleDataBar, CfRuleIconSet, CfRule, ConditionalFormat, DataValidationType, DataValidation, SparklineKind, ParsedSparkline, ExcelTable, PivotTable, SheetPane, } from './types.js';
|
|
1
|
+
export type { CellType, WorksheetCell, ColumnWidth, MergedRange, RowHeight, XlsxPageMargins, XlsxPageSetup, XlsxPrintOptions, ParsedWorksheet, XlsxFont, XlsxFill, XlsxBorderStyleName, XlsxBorderEdge, XlsxBorder, XlsxHorizontalAlign, XlsxVerticalAlign, XlsxCellAlignment, XlsxCellXf, XlsxStyles, DefinedName, Dxf, CfOperator, CfRuleCellIs, CfvoType, Cfvo, CfRuleColorScale, CfRuleDataBar, CfRuleIconSet, CfRuleTop10, CfRuleAboveAverage, CfRuleDupUnique, CfRuleText, CfRuleExpression, CfRuleTimePeriod, TimePeriodKind, CfRule, ConditionalFormat, DataValidationType, DataValidation, HyperlinkRef, HeaderFooter, FormControlRef, OleObjectRef, SheetRichRun, SparklineKind, ParsedSparkline, ExcelTable, PivotTable, SheetPane, } from './types.js';
|