reamkit 1.23.0 → 1.25.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 +22 -11
- package/dist/esm/core/bmp.d.ts +33 -0
- package/dist/esm/core/bmp.js +276 -0
- package/dist/esm/core/converter/facade.d.ts +3 -3
- package/dist/esm/core/converter/facade.js +12 -0
- package/dist/esm/core/converter/ream.d.ts +48 -4
- package/dist/esm/core/converter/ream.js +25 -4
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +54 -0
- package/dist/esm/core/drawingml/chart-geometry.js +3 -2
- package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
- package/dist/esm/core/drawingml/diagram/colors.js +125 -0
- package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
- package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
- package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
- package/dist/esm/core/drawingml/diagram/run.js +50 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
- package/dist/esm/core/drawingml/preset-geometry.js +116 -6
- package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
- package/dist/esm/core/drawingml/text-warp.js +355 -0
- package/dist/esm/core/drawingml/theme-parser.js +9 -3
- package/dist/esm/core/font/measure.d.ts +12 -0
- package/dist/esm/core/font/measure.js +36 -0
- package/dist/esm/core/images.d.ts +30 -2
- package/dist/esm/core/images.js +121 -5
- package/dist/esm/core/metafile/emf.js +144 -16
- package/dist/esm/core/metafile/picture.d.ts +49 -0
- package/dist/esm/core/metafile/picture.js +70 -1
- package/dist/esm/core/metafile/wmf.js +53 -4
- package/dist/esm/core/ole/escher-blip.js +11 -1
- package/dist/esm/core/outline.d.ts +17 -0
- package/dist/esm/core/outline.js +30 -0
- package/dist/esm/core/style-cascade/resolver.js +1 -0
- package/dist/esm/core/style-cascade/types.d.ts +3 -1
- package/dist/esm/excel/print-model.js +2 -2
- package/dist/esm/excel/sheet-drawing.js +1 -1
- package/dist/esm/excel/sheet-to-flow.d.ts +10 -0
- package/dist/esm/excel/sheet-to-flow.js +30 -3
- package/dist/esm/html/html-writer.js +5 -3
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/layout/page-doc.d.ts +35 -0
- package/dist/esm/layout/page-doc.js +14 -1
- package/dist/esm/layout/styled-layout.js +217 -30
- package/dist/esm/markdown/markdown-writer.d.ts +41 -0
- package/dist/esm/markdown/markdown-writer.js +733 -0
- package/dist/esm/pdf/shading.js +8 -6
- package/dist/esm/pdf/styled-page-emitter.js +62 -4
- package/dist/esm/pdf/vector-graphics.js +1 -1
- package/dist/esm/pdf-reader/annots.d.ts +24 -0
- package/dist/esm/pdf-reader/annots.js +126 -0
- package/dist/esm/pdf-reader/content.d.ts +131 -5
- package/dist/esm/pdf-reader/content.js +169 -12
- package/dist/esm/pdf-reader/display.d.ts +56 -0
- package/dist/esm/pdf-reader/display.js +162 -0
- package/dist/esm/pdf-reader/document.d.ts +36 -1
- package/dist/esm/pdf-reader/document.js +92 -25
- package/dist/esm/pdf-reader/embedded-fonts.d.ts +31 -0
- package/dist/esm/pdf-reader/embedded-fonts.js +94 -0
- package/dist/esm/pdf-reader/flow-build.d.ts +61 -6
- package/dist/esm/pdf-reader/flow-build.js +128 -22
- package/dist/esm/pdf-reader/font.js +185 -4
- package/dist/esm/pdf-reader/image-decode.js +56 -5
- package/dist/esm/pdf-reader/images.d.ts +6 -0
- package/dist/esm/pdf-reader/images.js +25 -5
- package/dist/esm/pdf-reader/jpeg.d.ts +18 -0
- package/dist/esm/pdf-reader/jpeg.js +419 -0
- package/dist/esm/pdf-reader/layout.d.ts +1 -1
- package/dist/esm/pdf-reader/layout.js +221 -32
- package/dist/esm/pdf-reader/pattern-tint.d.ts +17 -0
- package/dist/esm/pdf-reader/pattern-tint.js +181 -0
- package/dist/esm/pdf-reader/reader.d.ts +9 -1
- package/dist/esm/pdf-reader/reader.js +22 -6
- package/dist/esm/pdf-reader/shading.d.ts +14 -0
- package/dist/esm/pdf-reader/shading.js +27 -1
- package/dist/esm/pdf-reader/tagged.js +156 -17
- package/dist/esm/pdf-reader/text.d.ts +13 -1
- package/dist/esm/pdf-reader/text.js +70 -3
- package/dist/esm/pdf-reader/vector.d.ts +25 -1
- package/dist/esm/pdf-reader/vector.js +168 -12
- package/dist/esm/pptx/ppt/ppt-reader.js +95 -27
- package/dist/esm/pptx/ppt/ppt-text.d.ts +41 -0
- package/dist/esm/pptx/ppt/ppt-text.js +327 -37
- package/dist/esm/pptx/pptx-reader.js +90 -10
- package/dist/esm/pptx/slide-parser.d.ts +4 -1
- package/dist/esm/pptx/slide-parser.js +104 -13
- package/dist/esm/pptx/sp-helpers.d.ts +7 -3
- package/dist/esm/pptx/sp-helpers.js +45 -7
- package/dist/esm/pptx/table-style.d.ts +7 -0
- package/dist/esm/pptx/table-style.js +59 -7
- package/dist/esm/svg/svg-writer.js +1 -0
- package/dist/esm/word/document-parser.d.ts +4 -1
- package/dist/esm/word/document-parser.js +4 -1
- package/dist/esm/word/docx-reader.js +26 -13
- package/dist/esm/word/docx-writer.js +15 -1
- package/dist/esm/word/drawing-parser.d.ts +20 -0
- package/dist/esm/word/drawing-parser.js +44 -5
- package/dist/esm/word/table-parser.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Ream
|
|
2
2
|
|
|
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.
|
|
3
|
+
> Read Word, Excel, PowerPoint and PDF — and convert any of them to PDF, SVG, HTML, Markdown, DOCX or XLSX. From scratch, in the browser. No LibreOffice, no headless Office, no commercial SDK.
|
|
4
4
|
|
|
5
5
|
Ream parses **seven document formats** — the modern Office Open XML trio (`.docx`,
|
|
6
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**.
|
|
7
|
+
format-neutral interlayer, then renders that to **PDF, SVG, HTML, Markdown, DOCX or XLSX**.
|
|
8
8
|
It is implemented directly from the **ECMA-376** (OOXML), **ISO 32000 / 19005**
|
|
9
9
|
(PDF / PDF/A), and Microsoft's binary-format specifications — no wrapper around
|
|
10
10
|
LibreOffice, headless Office, or any commercial SDK. Pure TypeScript/JavaScript on
|
|
@@ -14,7 +14,7 @@ Node.js, serverless and edge runtimes.
|
|
|
14
14
|
| | |
|
|
15
15
|
| ---------- | ------------------------------------------------------------------------------------------- |
|
|
16
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` |
|
|
17
|
+
| **Writes** | `.pdf` (incl. PDF/A-1/2/3, PDF/UA-1, signed, encrypted) · `.svg` · `.html` · `.md` · `.docx` · `.xlsx` |
|
|
18
18
|
|
|
19
19
|
## Install
|
|
20
20
|
|
|
@@ -42,6 +42,7 @@ const doc = Ream.parse(bytes); // docx, xlsx, pptx or pdf — sniffed
|
|
|
42
42
|
const pdf = await doc.convert('pdf'); // async — fetches a font if needed
|
|
43
43
|
const svg = await doc.convert('svg'); // same parse, different target
|
|
44
44
|
const html = await doc.convert('html'); // flowed HTML — needs no fonts at all
|
|
45
|
+
const md = await doc.convert('md'); // GitHub-Flavored Markdown — same, narrower
|
|
45
46
|
const docx = await doc.convert('docx'); // write WordprocessingML back out
|
|
46
47
|
const xlsx = await doc.convert('xlsx'); // write SpreadsheetML back (xlsx source)
|
|
47
48
|
|
|
@@ -160,7 +161,7 @@ automatically from the document's `docProps/core.xml`), `attachments`
|
|
|
160
161
|
|
|
161
162
|
### Lower-level APIs
|
|
162
163
|
|
|
163
|
-
- `docxReader` / `xlsxReader`, `svgWriter`, `htmlWriter`, `docxWriter` — the `@experimental`
|
|
164
|
+
- `docxReader` / `xlsxReader`, `svgWriter`, `htmlWriter`, `markdownWriter`, `docxWriter` — the `@experimental`
|
|
164
165
|
reader/writer interfaces of the interlayer, for building custom pipelines (and
|
|
165
166
|
keeping unused formats out of your bundle); `layoutStyledDocument` produces the
|
|
166
167
|
frozen page model (`PageItem` pages in a top-left `Pt` frame) the page-based
|
|
@@ -190,17 +191,27 @@ preview, flowed HTML export, and **docx + xlsx output** (write WordprocessingML
|
|
|
190
191
|
tagged PDF from its structure tree (headings, tables, lists, reading order), an
|
|
191
192
|
untagged one heuristically from glyph positions (lines, paragraphs, headings,
|
|
192
193
|
and a clean two-column split). It lifts back the text (via each font's
|
|
193
|
-
`/ToUnicode
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
194
|
+
`/ToUnicode`, or the embedded program's own `cmap` where there is none), the
|
|
195
|
+
font programs themselves, raster images (JPEG verbatim; PNG/Flate/LZW/CCITT-fax
|
|
196
|
+
decoded and re-encoded), `/Link` hyperlinks, form-XObject content, annotation
|
|
197
|
+
appearances, and the page's artwork: filled / stroked / gradient shapes,
|
|
198
|
+
clipping paths, tiling patterns, constant alpha, and the Type 3 glyphs that are
|
|
199
|
+
drawings rather than letters. It reads modern compressed files (cross-reference
|
|
200
|
+
+ object streams) and encrypted ones (RC4 / AES — the user password is passed to
|
|
201
|
+
`Ream.parse(bytes, { password })`, defaulting to the permissions-only case); a
|
|
202
|
+
filter it does not carry can be handed to it through
|
|
203
|
+
`Ream.parse(bytes, { filters })`.
|
|
204
|
+
|
|
205
|
+
A form or a drawing is not a reflowable document, so
|
|
206
|
+
`Ream.parse(bytes, { pdfLayout: 'positional' })` reads the page instead: every
|
|
207
|
+
line stands where its glyphs stand, beside the artwork, the way up the page is
|
|
208
|
+
shown.
|
|
198
209
|
|
|
199
210
|
**Reads PowerPoint, too.** `Ream.parse` accepts a `.pptx` and turns each slide
|
|
200
211
|
into a page at the deck size — text boxes (with run formatting, alignment,
|
|
201
212
|
bullets and indents), layout/master placeholders, pictures, shapes, DrawingML
|
|
202
213
|
tables, embedded charts, theme colours, slide backgrounds, grouped shapes and
|
|
203
|
-
hyperlinks — then converts onward to PDF, SVG, HTML or DOCX like any source.
|
|
214
|
+
hyperlinks — then converts onward to PDF, SVG, HTML, Markdown or DOCX like any source.
|
|
204
215
|
|
|
205
216
|
**Reads legacy `.doc`, `.xls` and `.ppt`, too.** The binary Word / Excel /
|
|
206
217
|
PowerPoint 97–2003 formats (OLE2/CFB) parse through a shared container reader: a
|
|
@@ -217,7 +228,7 @@ text (with run and paragraph formatting), embedded images, per-shape placement
|
|
|
217
228
|
(anchored text boxes and pictures at their slide rectangles) and decorative
|
|
218
229
|
autoshapes (preset or exact freeform geometry, with fill / line colours resolved
|
|
219
230
|
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.
|
|
231
|
+
SVG, HTML, Markdown, or back to `.docx` / `.xlsx` like any source.
|
|
221
232
|
|
|
222
233
|
See [`CHANGELOG.md`](./CHANGELOG.md) for the release history; the docs
|
|
223
234
|
[**Scope**](https://reamkit.dev/guides/scope/) guide has the full feature matrix
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** One decoded bitmap: chunky 8-bit samples, plus alpha when the file carries it. */
|
|
2
|
+
export interface DecodedBmp {
|
|
3
|
+
readonly width: number;
|
|
4
|
+
readonly height: number;
|
|
5
|
+
/** Row-major RGB samples, `width * height * 3` long. */
|
|
6
|
+
readonly data: Uint8Array;
|
|
7
|
+
/** One byte per pixel, present only when the file states real transparency. */
|
|
8
|
+
readonly alpha?: Uint8Array;
|
|
9
|
+
/** `biXPelsPerMeter`/`biYPelsPerMeter` turned into pixels per inch. */
|
|
10
|
+
readonly dpiX?: number;
|
|
11
|
+
readonly dpiY?: number;
|
|
12
|
+
}
|
|
13
|
+
/** Whether these bytes open with the `BM` signature of a bitmap FILE. */
|
|
14
|
+
export declare function isBmp(bytes: Uint8Array): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* MS-ODRAW §2.2.28 — a `BlipDIB`'s payload is a bitmap without its 14-byte
|
|
17
|
+
* BITMAPFILEHEADER, which the Escher record makes redundant. Put it back.
|
|
18
|
+
*
|
|
19
|
+
* @param dib The bytes from the DIB header onwards.
|
|
20
|
+
* @returns A complete BMP file, or `undefined` when the header does not read
|
|
21
|
+
* as a bitmap at all.
|
|
22
|
+
*/
|
|
23
|
+
export declare function dibToBmp(dib: Uint8Array): Uint8Array | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Decode a Windows bitmap into 8-bit RGB samples.
|
|
26
|
+
*
|
|
27
|
+
* @param bytes A complete BMP file (`BM` signature included).
|
|
28
|
+
* @returns The samples, their size and any alpha channel.
|
|
29
|
+
* @throws Error when the file is malformed, or carries a bitmap this does not
|
|
30
|
+
* read — a JPEG or PNG smuggled inside a DIB (`BI_JPEG`/`BI_PNG`),
|
|
31
|
+
* which is a whole other file wearing a bitmap header.
|
|
32
|
+
*/
|
|
33
|
+
export declare function decodeBmp(bytes: Uint8Array): DecodedBmp;
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
//#region src/core/bmp.ts
|
|
2
|
+
var FILE_HEADER_BYTES = 14;
|
|
3
|
+
var CORE_HEADER_BYTES = 12;
|
|
4
|
+
var INFO_HEADER_BYTES = 40;
|
|
5
|
+
var MAX_PIXELS = 4e7;
|
|
6
|
+
var BI_RGB = 0;
|
|
7
|
+
var BI_RLE8 = 1;
|
|
8
|
+
var BI_RLE4 = 2;
|
|
9
|
+
var BI_BITFIELDS = 3;
|
|
10
|
+
var BI_ALPHABITFIELDS = 6;
|
|
11
|
+
/** Whether these bytes open with the `BM` signature of a bitmap FILE. */
|
|
12
|
+
function isBmp(bytes) {
|
|
13
|
+
return bytes.length >= FILE_HEADER_BYTES && bytes[0] === 66 && bytes[1] === 77;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* MS-ODRAW §2.2.28 — a `BlipDIB`'s payload is a bitmap without its 14-byte
|
|
17
|
+
* BITMAPFILEHEADER, which the Escher record makes redundant. Put it back.
|
|
18
|
+
*
|
|
19
|
+
* @param dib The bytes from the DIB header onwards.
|
|
20
|
+
* @returns A complete BMP file, or `undefined` when the header does not read
|
|
21
|
+
* as a bitmap at all.
|
|
22
|
+
*/
|
|
23
|
+
function dibToBmp(dib) {
|
|
24
|
+
const head = readDibHeader(dib, 0);
|
|
25
|
+
if (!head) return void 0;
|
|
26
|
+
const offBits = FILE_HEADER_BYTES + head.headerBytes + head.maskBytes + head.paletteBytes;
|
|
27
|
+
const out = new Uint8Array(FILE_HEADER_BYTES + dib.length);
|
|
28
|
+
const view = new DataView(out.buffer);
|
|
29
|
+
out[0] = 66;
|
|
30
|
+
out[1] = 77;
|
|
31
|
+
view.setUint32(2, out.length, true);
|
|
32
|
+
view.setUint32(10, offBits, true);
|
|
33
|
+
out.set(dib, FILE_HEADER_BYTES);
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Decode a Windows bitmap into 8-bit RGB samples.
|
|
38
|
+
*
|
|
39
|
+
* @param bytes A complete BMP file (`BM` signature included).
|
|
40
|
+
* @returns The samples, their size and any alpha channel.
|
|
41
|
+
* @throws Error when the file is malformed, or carries a bitmap this does not
|
|
42
|
+
* read — a JPEG or PNG smuggled inside a DIB (`BI_JPEG`/`BI_PNG`),
|
|
43
|
+
* which is a whole other file wearing a bitmap header.
|
|
44
|
+
*/
|
|
45
|
+
function decodeBmp(bytes) {
|
|
46
|
+
if (!isBmp(bytes)) throw new Error("BMP: not a bitmap");
|
|
47
|
+
const file = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
48
|
+
const head = readDibHeader(bytes, FILE_HEADER_BYTES);
|
|
49
|
+
if (!head) throw new Error("BMP: unreadable header");
|
|
50
|
+
const { width, height, topDown, bitCount, compression } = head;
|
|
51
|
+
if (width <= 0 || height <= 0) throw new Error("BMP: empty bitmap");
|
|
52
|
+
if (width * height > MAX_PIXELS) throw new Error("BMP: image too large");
|
|
53
|
+
if (compression === 4 || compression === 5) throw new Error("BMP: embedded JPEG/PNG bitmaps are not supported");
|
|
54
|
+
const stated = file.getUint32(10, true);
|
|
55
|
+
const computed = FILE_HEADER_BYTES + head.headerBytes + head.maskBytes + head.paletteBytes;
|
|
56
|
+
const pixelsAt = stated >= computed && stated < bytes.length ? stated : computed;
|
|
57
|
+
const palette = readPalette(bytes, FILE_HEADER_BYTES + head.headerBytes, head);
|
|
58
|
+
const count = width * height;
|
|
59
|
+
const data = new Uint8Array(count * 3);
|
|
60
|
+
const rowOf = (y) => topDown ? y : height - 1 - y;
|
|
61
|
+
if (compression === BI_RLE8 || compression === BI_RLE4) {
|
|
62
|
+
const indices = decodeRle(bytes.subarray(pixelsAt), width, height, compression === BI_RLE4);
|
|
63
|
+
for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
|
|
64
|
+
const at = (rowOf(y) * width + x) * 3;
|
|
65
|
+
const p = (indices[y * width + x] ?? 0) * 3;
|
|
66
|
+
data[at] = palette[p] ?? 0;
|
|
67
|
+
data[at + 1] = palette[p + 1] ?? 0;
|
|
68
|
+
data[at + 2] = palette[p + 2] ?? 0;
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
width,
|
|
72
|
+
height,
|
|
73
|
+
data,
|
|
74
|
+
...density(head)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const stride = ((width * bitCount + 31) / 32 | 0) * 4;
|
|
78
|
+
const alpha = head.alphaMask !== 0 ? new Uint8Array(count) : void 0;
|
|
79
|
+
const shifts = {
|
|
80
|
+
r: maskShift(head.redMask),
|
|
81
|
+
g: maskShift(head.greenMask),
|
|
82
|
+
b: maskShift(head.blueMask),
|
|
83
|
+
a: maskShift(head.alphaMask)
|
|
84
|
+
};
|
|
85
|
+
for (let y = 0; y < height; y++) {
|
|
86
|
+
const row = pixelsAt + y * stride;
|
|
87
|
+
if (row + stride > bytes.length + stride) break;
|
|
88
|
+
for (let x = 0; x < width; x++) {
|
|
89
|
+
const at = (rowOf(y) * width + x) * 3;
|
|
90
|
+
if (bitCount <= 8) {
|
|
91
|
+
const p = paletteIndex(bytes, row, x, bitCount) * 3;
|
|
92
|
+
data[at] = palette[p] ?? 0;
|
|
93
|
+
data[at + 1] = palette[p + 1] ?? 0;
|
|
94
|
+
data[at + 2] = palette[p + 2] ?? 0;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (bitCount === 24) {
|
|
98
|
+
const o = row + x * 3;
|
|
99
|
+
data[at] = bytes[o + 2] ?? 0;
|
|
100
|
+
data[at + 1] = bytes[o + 1] ?? 0;
|
|
101
|
+
data[at + 2] = bytes[o] ?? 0;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const raw = bitCount === 16 ? (bytes[row + x * 2] ?? 0) | (bytes[row + x * 2 + 1] ?? 0) << 8 : ((bytes[row + x * 4] ?? 0) | (bytes[row + x * 4 + 1] ?? 0) << 8 | (bytes[row + x * 4 + 2] ?? 0) << 16 | (bytes[row + x * 4 + 3] ?? 0) << 24) >>> 0;
|
|
105
|
+
data[at] = channel(raw, head.redMask, shifts.r);
|
|
106
|
+
data[at + 1] = channel(raw, head.greenMask, shifts.g);
|
|
107
|
+
data[at + 2] = channel(raw, head.blueMask, shifts.b);
|
|
108
|
+
if (alpha) alpha[rowOf(y) * width + x] = channel(raw, head.alphaMask, shifts.a);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const opaque = alpha?.every((v) => v === 0) ?? true;
|
|
112
|
+
return {
|
|
113
|
+
width,
|
|
114
|
+
height,
|
|
115
|
+
data,
|
|
116
|
+
...alpha && !opaque ? { alpha } : {},
|
|
117
|
+
...density(head)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function readDibHeader(bytes, at) {
|
|
121
|
+
if (bytes.length < at + 4) return void 0;
|
|
122
|
+
const v = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
123
|
+
const headerBytes = v.getUint32(at, true);
|
|
124
|
+
const core = headerBytes === CORE_HEADER_BYTES;
|
|
125
|
+
if (!core && headerBytes < INFO_HEADER_BYTES) return void 0;
|
|
126
|
+
if (bytes.length < at + headerBytes) return void 0;
|
|
127
|
+
const width = core ? v.getInt16(at + 4, true) : v.getInt32(at + 4, true);
|
|
128
|
+
const rawHeight = core ? v.getInt16(at + 6, true) : v.getInt32(at + 8, true);
|
|
129
|
+
const planes = core ? v.getUint16(at + 6, true) : v.getUint16(at + 12, true);
|
|
130
|
+
const bitCount = core ? v.getUint16(at + 8, true) : v.getUint16(at + 14, true);
|
|
131
|
+
const compression = core ? BI_RGB : v.getUint32(at + 16, true);
|
|
132
|
+
if (planes !== 1) return void 0;
|
|
133
|
+
if (![
|
|
134
|
+
1,
|
|
135
|
+
2,
|
|
136
|
+
4,
|
|
137
|
+
8,
|
|
138
|
+
16,
|
|
139
|
+
24,
|
|
140
|
+
32
|
|
141
|
+
].includes(bitCount)) return void 0;
|
|
142
|
+
if (compression > 6) return void 0;
|
|
143
|
+
if (width <= 0 || rawHeight === 0) return void 0;
|
|
144
|
+
const clrUsed = core ? 0 : v.getUint32(at + 32, true);
|
|
145
|
+
const inHeader = headerBytes >= 52;
|
|
146
|
+
const maskBytes = !inHeader && (compression === BI_BITFIELDS || compression === BI_ALPHABITFIELDS) ? compression === BI_ALPHABITFIELDS ? 16 : 12 : 0;
|
|
147
|
+
const maskAt = inHeader ? at + 40 : at + headerBytes;
|
|
148
|
+
const stated = compression === BI_BITFIELDS || compression === BI_ALPHABITFIELDS || inHeader;
|
|
149
|
+
const readMask = (i) => stated && bytes.length >= maskAt + (i + 1) * 4 ? v.getUint32(maskAt + i * 4, true) : 0;
|
|
150
|
+
const defaults = bitCount === 16 ? [
|
|
151
|
+
31744,
|
|
152
|
+
992,
|
|
153
|
+
31
|
|
154
|
+
] : [
|
|
155
|
+
16711680,
|
|
156
|
+
65280,
|
|
157
|
+
255
|
|
158
|
+
];
|
|
159
|
+
const red = readMask(0) || defaults[0];
|
|
160
|
+
const green = readMask(1) || defaults[1];
|
|
161
|
+
const blue = readMask(2) || defaults[2];
|
|
162
|
+
const alphaMask = headerBytes >= 56 && bytes.length >= maskAt + 16 ? v.getUint32(maskAt + 12, true) : 0;
|
|
163
|
+
const paletteEntryBytes = core ? 3 : 4;
|
|
164
|
+
return {
|
|
165
|
+
headerBytes,
|
|
166
|
+
maskBytes,
|
|
167
|
+
paletteBytes: (bitCount <= 8 ? clrUsed || 1 << bitCount : 0) * paletteEntryBytes,
|
|
168
|
+
paletteEntryBytes,
|
|
169
|
+
width,
|
|
170
|
+
height: Math.abs(rawHeight),
|
|
171
|
+
topDown: rawHeight < 0,
|
|
172
|
+
bitCount,
|
|
173
|
+
compression,
|
|
174
|
+
redMask: red,
|
|
175
|
+
greenMask: green,
|
|
176
|
+
blueMask: blue,
|
|
177
|
+
alphaMask,
|
|
178
|
+
pxPerMeterX: core ? 0 : v.getInt32(at + 24, true),
|
|
179
|
+
pxPerMeterY: core ? 0 : v.getInt32(at + 28, true)
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/** The palette as flat RGB triples, whichever entry width the header uses. */
|
|
183
|
+
function readPalette(bytes, at, head) {
|
|
184
|
+
const entries = head.paletteBytes / head.paletteEntryBytes;
|
|
185
|
+
const out = new Uint8Array(entries * 3);
|
|
186
|
+
for (let i = 0; i < entries; i++) {
|
|
187
|
+
const o = at + i * head.paletteEntryBytes;
|
|
188
|
+
out[i * 3] = bytes[o + 2] ?? 0;
|
|
189
|
+
out[i * 3 + 1] = bytes[o + 1] ?? 0;
|
|
190
|
+
out[i * 3 + 2] = bytes[o] ?? 0;
|
|
191
|
+
}
|
|
192
|
+
return out;
|
|
193
|
+
}
|
|
194
|
+
/** The palette index of one pixel of a 1/2/4/8-bit row. */
|
|
195
|
+
function paletteIndex(bytes, row, x, bitCount) {
|
|
196
|
+
if (bitCount === 8) return bytes[row + x] ?? 0;
|
|
197
|
+
const perByte = 8 / bitCount;
|
|
198
|
+
return (bytes[row + (x / perByte | 0)] ?? 0) >> 8 - bitCount * (x % perByte + 1) & (1 << bitCount) - 1;
|
|
199
|
+
}
|
|
200
|
+
/** How far right a mask's field sits, so its bits can be read off. */
|
|
201
|
+
function maskShift(mask) {
|
|
202
|
+
if (mask === 0) return 0;
|
|
203
|
+
let shift = 0;
|
|
204
|
+
while ((mask >>> shift & 1) === 0) shift++;
|
|
205
|
+
return shift;
|
|
206
|
+
}
|
|
207
|
+
/** One channel of a packed pixel, stretched to the full byte range. */
|
|
208
|
+
function channel(raw, mask, shift) {
|
|
209
|
+
if (mask === 0) return 0;
|
|
210
|
+
const width = (mask >>> shift).toString(2).length;
|
|
211
|
+
const value = (raw & mask) >>> shift;
|
|
212
|
+
const max = (1 << width) - 1;
|
|
213
|
+
return max === 0 ? 0 : Math.round(value * 255 / max);
|
|
214
|
+
}
|
|
215
|
+
/** §biXPelsPerMeter — the resolution the bitmap claims, in pixels per inch. */
|
|
216
|
+
function density(head) {
|
|
217
|
+
if (head.pxPerMeterX <= 0 || head.pxPerMeterY <= 0) return {};
|
|
218
|
+
return {
|
|
219
|
+
dpiX: head.pxPerMeterX * .0254,
|
|
220
|
+
dpiY: head.pxPerMeterY * .0254
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* §BI_RLE8 / §BI_RLE4 — run-length rows of palette indices.
|
|
225
|
+
*
|
|
226
|
+
* Each pair is either a run (a non-zero count and the index to repeat) or an
|
|
227
|
+
* escape (a zero count and a code): 0 ends the row, 1 ends the bitmap, 2 is a
|
|
228
|
+
* delta that skips forward, and anything else is that many literal pixels,
|
|
229
|
+
* padded to a two-byte boundary.
|
|
230
|
+
*
|
|
231
|
+
* @param data The bytes from the first run onwards.
|
|
232
|
+
* @param width The bitmap's width in pixels.
|
|
233
|
+
* @param height Its height in rows.
|
|
234
|
+
* @param four Whether this is the four-bit form, which packs two indices per byte.
|
|
235
|
+
* @returns One palette index per pixel, top row first.
|
|
236
|
+
*/
|
|
237
|
+
function decodeRle(data, width, height, four) {
|
|
238
|
+
const out = new Uint8Array(width * height);
|
|
239
|
+
let x = 0;
|
|
240
|
+
let y = 0;
|
|
241
|
+
let at = 0;
|
|
242
|
+
const put = (index) => {
|
|
243
|
+
if (x < width && y < height) out[y * width + x] = index;
|
|
244
|
+
x++;
|
|
245
|
+
};
|
|
246
|
+
while (at + 1 < data.length && y < height) {
|
|
247
|
+
const count = data[at];
|
|
248
|
+
const value = data[at + 1];
|
|
249
|
+
at += 2;
|
|
250
|
+
if (count > 0) {
|
|
251
|
+
for (let i = 0; i < count; i++) put(four ? i % 2 === 0 ? value >> 4 : value & 15 : value);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (value === 0) {
|
|
255
|
+
x = 0;
|
|
256
|
+
y++;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (value === 1) break;
|
|
260
|
+
if (value === 2) {
|
|
261
|
+
x += data[at] ?? 0;
|
|
262
|
+
y += data[at + 1] ?? 0;
|
|
263
|
+
at += 2;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const bytesUsed = four ? (value + 1) / 2 | 0 : value;
|
|
267
|
+
for (let i = 0; i < value; i++) {
|
|
268
|
+
const byte = data[at + (four ? i / 2 | 0 : i)] ?? 0;
|
|
269
|
+
put(four ? i % 2 === 0 ? byte >> 4 : byte & 15 : byte);
|
|
270
|
+
}
|
|
271
|
+
at += bytesUsed + bytesUsed % 2;
|
|
272
|
+
}
|
|
273
|
+
return out;
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
export { decodeBmp, dibToBmp, isBmp };
|
|
@@ -9,10 +9,10 @@ import { ProjectSheetOptions } from '../../excel/sheet-to-flow.js';
|
|
|
9
9
|
/** Options for a {@link Converter.convert} call (extends the docx PDF options). */
|
|
10
10
|
export interface ConvertOptions extends ConvertDocxOptions {
|
|
11
11
|
/**
|
|
12
|
-
* Target: 'pdf' (default), 'svg' (page-stack preview), 'html'/'docx'
|
|
13
|
-
* or 'xlsx' (the native grid writer — spreadsheet input only).
|
|
12
|
+
* Target: 'pdf' (default), 'svg' (page-stack preview), 'html'/'md'/'docx'
|
|
13
|
+
* (flowed), or 'xlsx' (the native grid writer — spreadsheet input only).
|
|
14
14
|
*/
|
|
15
|
-
readonly to?: 'pdf' | 'svg' | 'html' | 'docx' | 'xlsx';
|
|
15
|
+
readonly to?: 'pdf' | 'svg' | 'html' | 'md' | 'docx' | 'xlsx';
|
|
16
16
|
/**
|
|
17
17
|
* Strict mode (handoff v1 §5): throw ConversionLossError on the first
|
|
18
18
|
* recorded loss instead of returning it in the report.
|
|
@@ -14,6 +14,7 @@ import { flowRenderOptions } from "./project.js";
|
|
|
14
14
|
import { writeDocx } from "../../word/docx-writer.js";
|
|
15
15
|
import { writeXlsx } from "../../excel/xlsx-writer.js";
|
|
16
16
|
import { writeHtml } from "../../html/html-writer.js";
|
|
17
|
+
import { writeMarkdown } from "../../markdown/markdown-writer.js";
|
|
17
18
|
import { writeSvg } from "../../svg/svg-writer.js";
|
|
18
19
|
import { convertDocxToPdf } from "../../word/docx-to-pdf.js";
|
|
19
20
|
import { convertXlsxToPdf } from "../../excel/xlsx-to-pdf.js";
|
|
@@ -87,6 +88,17 @@ function createConverter(opts = {}) {
|
|
|
87
88
|
losses
|
|
88
89
|
};
|
|
89
90
|
}
|
|
91
|
+
if (to === "md") {
|
|
92
|
+
const { doc: flow, losses: readLosses } = readToFlow(reader, bytes, rest.now ? { now: rest.now } : void 0);
|
|
93
|
+
losses.push(...readLosses);
|
|
94
|
+
const markdown = writeMarkdown(flow);
|
|
95
|
+
losses.push(...markdown.losses);
|
|
96
|
+
if (strict && losses.length > 0) throw new ConversionLossError(losses[0]);
|
|
97
|
+
return {
|
|
98
|
+
bytes: markdown.bytes,
|
|
99
|
+
losses
|
|
100
|
+
};
|
|
101
|
+
}
|
|
90
102
|
if (to === "xlsx") {
|
|
91
103
|
const { doc, losses: readLosses } = reader.read(bytes);
|
|
92
104
|
losses.push(...readLosses);
|
|
@@ -6,9 +6,10 @@ import { Loss } from '../ir/index.js';
|
|
|
6
6
|
import { DocumentReader } from '../ir/adapters.js';
|
|
7
7
|
import { FlowDoc } from '../ir/flow.js';
|
|
8
8
|
import { SheetDoc } from '../ir/sheet.js';
|
|
9
|
+
import { StreamFilters } from '../../pdf-reader/document.js';
|
|
9
10
|
import { SignatureOptions, StyledRenderOptions } from '../../pdf/index.js';
|
|
10
11
|
/** The output formats {@link Ream.convert} can produce. */
|
|
11
|
-
export type ReamTarget = 'pdf' | 'svg' | 'html' | 'docx' | 'xlsx';
|
|
12
|
+
export type ReamTarget = 'pdf' | 'svg' | 'html' | 'md' | 'docx' | 'xlsx';
|
|
12
13
|
/** Options for {@link Ream.parse}. */
|
|
13
14
|
export interface ReamParseOptions {
|
|
14
15
|
/** Reader registry override; defaults to the built-in docx + xlsx readers. */
|
|
@@ -20,6 +21,30 @@ export interface ReamParseOptions {
|
|
|
20
21
|
* encryption (EP14); an encrypted OOXML package always names a password.
|
|
21
22
|
*/
|
|
22
23
|
readonly password?: string;
|
|
24
|
+
/**
|
|
25
|
+
* PDF only: what to read out of the page. `'flow'` (the default) reconstructs
|
|
26
|
+
* a re-flowable document — paragraphs and tables in reading order, from the
|
|
27
|
+
* structure tree where the file has one. `'positional'` keeps the page as a
|
|
28
|
+
* page: every line stands where its glyphs stand, beside the rules and fills
|
|
29
|
+
* the page draws. A form or a drawing needs the second; a report to be edited
|
|
30
|
+
* or converted to markdown needs the first.
|
|
31
|
+
*/
|
|
32
|
+
readonly pdfLayout?: 'flow' | 'positional';
|
|
33
|
+
/**
|
|
34
|
+
* PDF only: decoders for `/Filter` names the reader does not implement
|
|
35
|
+
* (§7.4). A filter it cannot undo leaves that stream unread — and when the
|
|
36
|
+
* unread one is the cross-reference, the whole document is missing — so a
|
|
37
|
+
* caller who needs such a file supplies the decoder rather than the library
|
|
38
|
+
* carrying one for every filter anyone might write:
|
|
39
|
+
*
|
|
40
|
+
* ```ts
|
|
41
|
+
* import { brotliDecompressSync } from 'node:zlib';
|
|
42
|
+
* Ream.parse(pdf, { filters: { BrotliDecode: (b) => brotliDecompressSync(b) } });
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Absent, or throwing, the filter is reported unreadable by name.
|
|
46
|
+
*/
|
|
47
|
+
readonly filters?: StreamFilters;
|
|
23
48
|
}
|
|
24
49
|
/**
|
|
25
50
|
* Options for {@link Ream.convert} and {@link Ream.convertWithReport}. Extends
|
|
@@ -62,6 +87,25 @@ export interface ReamConvertOptions extends Omit<StyledRenderOptions, 'registry'
|
|
|
62
87
|
* the code resolves, and omitted it is dropped exactly as before.
|
|
63
88
|
*/
|
|
64
89
|
readonly fileName?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Markdown only: how a picture reaches the output — inlined as a `data:` URI
|
|
92
|
+
* (the default), named under `./media/` for a caller that writes the bytes
|
|
93
|
+
* itself, or dropped. See {@link MarkdownWriteOptions}.
|
|
94
|
+
*/
|
|
95
|
+
readonly images?: 'dataUri' | 'link' | 'drop';
|
|
96
|
+
/**
|
|
97
|
+
* Markdown only: what a page break becomes — nothing (the default), or the
|
|
98
|
+
* `---` thematic break a slide deck wants between its slides. See
|
|
99
|
+
* {@link MarkdownWriteOptions}.
|
|
100
|
+
*/
|
|
101
|
+
readonly pageBreaks?: 'rule' | 'drop';
|
|
102
|
+
/**
|
|
103
|
+
* Markdown from a SPREADSHEET only: open each sheet with a heading carrying
|
|
104
|
+
* its tab name. On by default — markdown has no pages to tell one sheet from
|
|
105
|
+
* the next by, so without them a workbook is a pile of tables with nothing to
|
|
106
|
+
* say which is which. Set `false` for the bare tables.
|
|
107
|
+
*/
|
|
108
|
+
readonly sheetNames?: boolean;
|
|
65
109
|
}
|
|
66
110
|
/**
|
|
67
111
|
* The object face of the library: parse a document once into the format-neutral
|
|
@@ -121,9 +165,9 @@ export declare class Ream {
|
|
|
121
165
|
/**
|
|
122
166
|
* Convert the parsed document to `to`, returning the output bytes together with
|
|
123
167
|
* the accumulated {@link Loss} report (read-time losses plus any added while
|
|
124
|
-
* writing). HTML, DOCX and XLSX are produced straight from the
|
|
125
|
-
* layout, no fonts, zero I/O; SVG and PDF run the layout
|
|
126
|
-
* fonts first.
|
|
168
|
+
* writing). HTML, Markdown, DOCX and XLSX are produced straight from the
|
|
169
|
+
* interlayer — no layout, no fonts, zero I/O; SVG and PDF run the layout
|
|
170
|
+
* engine and resolve fonts first.
|
|
127
171
|
*
|
|
128
172
|
* @param to The target format. `'xlsx'` requires a spreadsheet source.
|
|
129
173
|
* @param options Font resolution and target-specific options.
|
|
@@ -12,6 +12,7 @@ import { flowRenderOptions } from "./project.js";
|
|
|
12
12
|
import { writeDocx } from "../../word/docx-writer.js";
|
|
13
13
|
import { writeXlsx } from "../../excel/xlsx-writer.js";
|
|
14
14
|
import { writeHtml } from "../../html/html-writer.js";
|
|
15
|
+
import { writeMarkdown } from "../../markdown/markdown-writer.js";
|
|
15
16
|
import { writeSvg } from "../../svg/svg-writer.js";
|
|
16
17
|
import { DEFAULT_READERS, resolveFontsViaChain, toFlowDoc } from "./facade.js";
|
|
17
18
|
import { familiesInFlow } from "../fonts/families.js";
|
|
@@ -85,7 +86,11 @@ var Ream = class Ream {
|
|
|
85
86
|
}
|
|
86
87
|
const reader = readers.find((r) => r.sniff(source));
|
|
87
88
|
if (!reader) throw new Error(`Unrecognized document format (readers: ${readers.map((r) => r.id).join(", ")})`);
|
|
88
|
-
const { doc, losses } = reader.read(source, {
|
|
89
|
+
const { doc, losses } = reader.read(source, {
|
|
90
|
+
password: options.password,
|
|
91
|
+
...options.pdfLayout ? { pdfLayout: options.pdfLayout } : {},
|
|
92
|
+
...options.filters ? { filters: options.filters } : {}
|
|
93
|
+
});
|
|
89
94
|
const sheet = doc.kind === "sheet" ? doc : void 0;
|
|
90
95
|
return new Ream(toFlowDoc(doc), sheet, losses, bytes, reader.id);
|
|
91
96
|
}
|
|
@@ -107,9 +112,9 @@ var Ream = class Ream {
|
|
|
107
112
|
/**
|
|
108
113
|
* Convert the parsed document to `to`, returning the output bytes together with
|
|
109
114
|
* the accumulated {@link Loss} report (read-time losses plus any added while
|
|
110
|
-
* writing). HTML, DOCX and XLSX are produced straight from the
|
|
111
|
-
* layout, no fonts, zero I/O; SVG and PDF run the layout
|
|
112
|
-
* fonts first.
|
|
115
|
+
* writing). HTML, Markdown, DOCX and XLSX are produced straight from the
|
|
116
|
+
* interlayer — no layout, no fonts, zero I/O; SVG and PDF run the layout
|
|
117
|
+
* engine and resolve fonts first.
|
|
113
118
|
*
|
|
114
119
|
* @param to The target format. `'xlsx'` requires a spreadsheet source.
|
|
115
120
|
* @param options Font resolution and target-specific options.
|
|
@@ -132,6 +137,22 @@ var Ream = class Ream {
|
|
|
132
137
|
losses
|
|
133
138
|
};
|
|
134
139
|
}
|
|
140
|
+
if (to === "md") {
|
|
141
|
+
const markdown = writeMarkdown(this.sheet && options.sheetNames !== false ? projectSheetDoc(this.sheet, {
|
|
142
|
+
...options.now ? { now: options.now } : {},
|
|
143
|
+
...options.fileName ? { fileName: options.fileName } : {},
|
|
144
|
+
sheetHeadings: true
|
|
145
|
+
}) : flow, {
|
|
146
|
+
...options.images ? { images: options.images } : {},
|
|
147
|
+
...options.pageBreaks ? { pageBreaks: options.pageBreaks } : {}
|
|
148
|
+
});
|
|
149
|
+
losses.push(...markdown.losses);
|
|
150
|
+
this.enforceStrict(options, losses);
|
|
151
|
+
return {
|
|
152
|
+
bytes: markdown.bytes,
|
|
153
|
+
losses
|
|
154
|
+
};
|
|
155
|
+
}
|
|
135
156
|
if (to === "docx") {
|
|
136
157
|
const docx = writeDocx(flow);
|
|
137
158
|
losses.push(...docx.losses);
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @packageDocumentation
|
|
7
7
|
*/
|
|
8
|
-
export type { AbstractNumbering, Alignment, Border, BorderStyle, BodyElement, Comment, CellBorders, CellMerge, DocumentInfo, CellMargins, CellProperties, CellShading, CellDataBar, CellIcon, CellIconShape, CellSparkline, Chart, ChartBlock, ChartDataPoint, ChartLineStyle, ChartMarker, ChartMarkerSymbol, ChartSeries, ChartType, CustomGeometry, CustomPathCmd, DocumentModel, FontFamilyMap, HeaderFooterReference, HeaderFooterType, ImageBlock, ImageCrop, InlineImage, MathAccent, MathBar, MathDelimiter, MathEqArray, MathFraction, MathFunc, MathGroupChr, MathLimit, MathMatrix, MathNary, MathNode, MathRadical, MathRow, MathRun, MathScript, Numbering, NumberingFormat, NumberingInstance, NumberingLevel, PictureBullet, PictureOutline, NumberingReference, PageMargins, PageSize, Paragraph, ParagraphProperties, FrameProperties, RelativeSize, TabStop, Run, RunProperties, RowConditionalFormat, RowProperties, Section, FloatAnchor, SectionColumns, SectionProperties, ShapeBlock, ShapeShadow, ShapeDash, ShapeFill, ShapeFillKind, ShapeGeometry, ShapeGroupChild, ShapeLine, LineEnd, ShapeTextBody, ShapeTransform, Style, StyleSheet, StyleType, Table, TableCell, TableLook, TableProperties, TableStyleCondition, TableStyleConditionType, TableStyleLayer, TableRow, UnderlineStyle, VerticalAlign, } from './types.js';
|
|
8
|
+
export type { AbstractNumbering, Alignment, Border, BorderStyle, BodyElement, Comment, CellBorders, CellMerge, DocumentInfo, CellMargins, CellProperties, CellShading, CellDataBar, CellIcon, CellIconShape, CellSparkline, Chart, ChartBlock, ChartDataPoint, ChartLineStyle, ChartMarker, ChartMarkerSymbol, ChartSeries, ChartType, CustomGeometry, CustomPathCmd, DocumentModel, FontFamilyMap, HeaderFooterReference, HeaderFooterType, ImageBlock, ImageCrop, InlineImage, MathAccent, MathBar, MathDelimiter, MathEqArray, MathFraction, MathFunc, MathGroupChr, MathLimit, MathMatrix, MathNary, MathNode, MathRadical, MathRow, MathRun, MathScript, Numbering, NumberingFormat, NumberingInstance, NumberingLevel, PictureBullet, PictureOutline, NumberingReference, PageMargins, PageSize, Paragraph, ParagraphProperties, FrameProperties, RelativeSize, TabStop, Run, RunProperties, TextOutline, RowConditionalFormat, RowProperties, Section, FloatAnchor, SectionColumns, SectionProperties, ShapeBlock, ShapeShadow, ShapeDash, ShapeFill, ShapeFillKind, ShapeGeometry, ShapeGroupChild, ShapeLine, LineEnd, ShapeTextBody, ShapeTransform, Style, StyleSheet, StyleType, Table, TableCell, TableLook, TableProperties, TableStyleCondition, TableStyleConditionType, TableStyleLayer, TableRow, UnderlineStyle, VerticalAlign, } from './types.js';
|
|
@@ -63,6 +63,17 @@ export interface RunProperties {
|
|
|
63
63
|
* (negative tightens). Word states it in twentieths of a point.
|
|
64
64
|
*/
|
|
65
65
|
readonly letterSpacingPt?: Pt;
|
|
66
|
+
/**
|
|
67
|
+
* §21.1.2.3.9 `a:rPr/a:ln` — a line drawn round the glyphs themselves, which
|
|
68
|
+
* DrawingML puts on a run and ISO 32000-1 §9.3.6 calls a text rendering mode
|
|
69
|
+
* that strokes as well as fills.
|
|
70
|
+
*/
|
|
71
|
+
readonly textOutline?: TextOutline;
|
|
72
|
+
}
|
|
73
|
+
/** A line drawn round a run's glyphs: its colour and how wide the pen is. */
|
|
74
|
+
export interface TextOutline {
|
|
75
|
+
readonly colorHex: string;
|
|
76
|
+
readonly widthPt: Pt;
|
|
66
77
|
}
|
|
67
78
|
/**
|
|
68
79
|
* ECMA-376 Part 1 §17.3.1 — Paragraph Properties (`pPr`). All lengths in
|
|
@@ -787,6 +798,13 @@ export interface CellProperties {
|
|
|
787
798
|
* is a word-processor table's default.
|
|
788
799
|
*/
|
|
789
800
|
readonly verticalAlign?: 'top' | 'center' | 'bottom';
|
|
801
|
+
/**
|
|
802
|
+
* §17.4.71 `w:textDirection` / §21.1.3.17 `a:tcPr@vert` — the cell's text is
|
|
803
|
+
* turned a quarter: `vert` reads top-to-bottom (turned clockwise), `vert270`
|
|
804
|
+
* bottom-to-top. Its lines then run along the cell's HEIGHT and stack across
|
|
805
|
+
* its width, which is what makes a narrow header column readable.
|
|
806
|
+
*/
|
|
807
|
+
readonly textDirection?: 'vert' | 'vert270';
|
|
790
808
|
}
|
|
791
809
|
/** §17.4.81 `w:trPr` — a table row's properties: height, split/header flags. */
|
|
792
810
|
export interface RowProperties {
|
|
@@ -1043,6 +1061,16 @@ export interface ShapeFill {
|
|
|
1043
1061
|
readonly sx: number;
|
|
1044
1062
|
readonly sy: number;
|
|
1045
1063
|
};
|
|
1064
|
+
/**
|
|
1065
|
+
* Where the grid of copies is anchored. DrawingML pins it to the shape's
|
|
1066
|
+
* top-left corner (§20.1.8.58 `a:tile @algn`, which defaults to `tl`); an
|
|
1067
|
+
* MS-ODRAW texture fill (§2.3.7.13 `fillOriginX`/`fillOriginY`, both
|
|
1068
|
+
* defaulting to nothing) centres it on the shape instead. It only shows when
|
|
1069
|
+
* the copies do not divide the box evenly — and it shows most when ONE copy
|
|
1070
|
+
* is larger than the box, where the difference is which part of the picture
|
|
1071
|
+
* is visible at all.
|
|
1072
|
+
*/
|
|
1073
|
+
readonly tileFromCentre?: boolean;
|
|
1046
1074
|
/**
|
|
1047
1075
|
* §20.1.8.14 `a:blipFill` — the picture painted across the shape's box. A
|
|
1048
1076
|
* DrawingML picture IS a shape with one of these, which is how a `pic:pic`
|
|
@@ -1152,6 +1180,12 @@ export interface ShapeTextBody {
|
|
|
1152
1180
|
* quarter clockwise), `vert270` bottom-to-top.
|
|
1153
1181
|
*/
|
|
1154
1182
|
readonly vertical?: 'vert' | 'vert270';
|
|
1183
|
+
/**
|
|
1184
|
+
* §20.1.10.55 `a:bodyPr @upright` — the words stay level however far the
|
|
1185
|
+
* shape is turned. bnc762542.xlsx turns each legend label a quarter and asks
|
|
1186
|
+
* for this, and every reader draws those labels lying flat.
|
|
1187
|
+
*/
|
|
1188
|
+
readonly upright?: boolean;
|
|
1155
1189
|
/**
|
|
1156
1190
|
* §20.1.10.28 `a:spAutoFit` — the SHAPE follows its text: its height is
|
|
1157
1191
|
* whatever the text needs, whatever the stated box says.
|
|
@@ -1162,6 +1196,26 @@ export interface ShapeTextBody {
|
|
|
1162
1196
|
* WordArt is set at whatever size fills the box it was drawn in.
|
|
1163
1197
|
*/
|
|
1164
1198
|
readonly fitToBox?: boolean;
|
|
1199
|
+
/**
|
|
1200
|
+
* §20.1.9.10 `a:prstTxWarp` — DrawingML WordArt: the preset curve the text is
|
|
1201
|
+
* bent through, and the `a:avLst` `adj` guide when the file states one. The
|
|
1202
|
+
* warped text does not wrap and is stretched to fill the shape's box, so the
|
|
1203
|
+
* size its runs state stops deciding how large it is drawn. `textNoShape` is
|
|
1204
|
+
* not carried: it is the enumeration's "no warp" member, and a body under it
|
|
1205
|
+
* is an ordinary text box.
|
|
1206
|
+
*/
|
|
1207
|
+
readonly warp?: {
|
|
1208
|
+
readonly preset: string;
|
|
1209
|
+
readonly adjust?: number;
|
|
1210
|
+
};
|
|
1211
|
+
/**
|
|
1212
|
+
* §20.1.10.42 `a:normAutofit` — the text SHRINKS to fit the box, and only
|
|
1213
|
+
* shrinks: a size that already fits is left alone. PowerPoint writes the
|
|
1214
|
+
* scale it settled on into `@fontScale` and that is applied at parse; this
|
|
1215
|
+
* is for the text that arrives without one, which is every box of a diagram
|
|
1216
|
+
* laid out from its layout part rather than from a cached drawing.
|
|
1217
|
+
*/
|
|
1218
|
+
readonly shrinkToFit?: boolean;
|
|
1165
1219
|
/**
|
|
1166
1220
|
* `wps:txbx @id` / `wps:linkedTxbx @id @seq` — the chain of boxes this one
|
|
1167
1221
|
* belongs to. Text that overruns a box continues in the next of its chain;
|