reamkit 1.24.0 → 1.25.1
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/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 +17 -0
- 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/sheet-to-flow.d.ts +10 -0
- package/dist/esm/excel/sheet-to-flow.js +14 -1
- package/dist/esm/html/html-writer.js +3 -2
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/layout/page-doc.js +1 -1
- package/dist/esm/layout/styled-layout.js +41 -15
- package/dist/esm/markdown/markdown-writer.d.ts +41 -0
- package/dist/esm/markdown/markdown-writer.js +733 -0
- package/dist/esm/pdf/styled-page-emitter.js +20 -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 +55 -4
- 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 +23 -7
- 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/slide-parser.js +5 -0
- package/dist/esm/word/docx-writer.js +79 -17
- package/dist/esm/word/drawing-parser.js +7 -1
- 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
|
|
@@ -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
|
|
@@ -1169,6 +1180,12 @@ export interface ShapeTextBody {
|
|
|
1169
1180
|
* quarter clockwise), `vert270` bottom-to-top.
|
|
1170
1181
|
*/
|
|
1171
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;
|
|
1172
1189
|
/**
|
|
1173
1190
|
* §20.1.10.28 `a:spAutoFit` — the SHAPE follows its text: its height is
|
|
1174
1191
|
* whatever the text needs, whatever the stated box says.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ResolvedParagraphProperties } from './style-cascade/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* The heading level (1–6) a paragraph's resolved properties describe, or
|
|
4
|
+
* `undefined` when it is body text.
|
|
5
|
+
*
|
|
6
|
+
* @param resolved The paragraph's fully-resolved properties.
|
|
7
|
+
* @returns The heading level 1–6, or `undefined` for body text.
|
|
8
|
+
*/
|
|
9
|
+
export declare function headingLevelOf(resolved: ResolvedParagraphProperties): number | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* The heading level a `"Heading N"` / `"Title"` / `"Subtitle"` style id names,
|
|
12
|
+
* for a heading style that declares no outline level of its own.
|
|
13
|
+
*
|
|
14
|
+
* @param styleId The paragraph's style id, if it has one.
|
|
15
|
+
* @returns The heading level 1–6, or `undefined` when the id names no heading.
|
|
16
|
+
*/
|
|
17
|
+
export declare function headingLevelFromStyleId(styleId: string | undefined): number | undefined;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/core/outline.ts
|
|
2
|
+
/** The deepest heading level the consuming media can express. */
|
|
3
|
+
var MAX_HEADING_LEVEL = 6;
|
|
4
|
+
/**
|
|
5
|
+
* The heading level (1–6) a paragraph's resolved properties describe, or
|
|
6
|
+
* `undefined` when it is body text.
|
|
7
|
+
*
|
|
8
|
+
* @param resolved The paragraph's fully-resolved properties.
|
|
9
|
+
* @returns The heading level 1–6, or `undefined` for body text.
|
|
10
|
+
*/
|
|
11
|
+
function headingLevelOf(resolved) {
|
|
12
|
+
const lvl = resolved.outlineLevel;
|
|
13
|
+
if (lvl !== void 0 && lvl >= 0 && lvl <= 8) return Math.min(lvl + 1, MAX_HEADING_LEVEL);
|
|
14
|
+
return headingLevelFromStyleId(resolved.styleId);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The heading level a `"Heading N"` / `"Title"` / `"Subtitle"` style id names,
|
|
18
|
+
* for a heading style that declares no outline level of its own.
|
|
19
|
+
*
|
|
20
|
+
* @param styleId The paragraph's style id, if it has one.
|
|
21
|
+
* @returns The heading level 1–6, or `undefined` when the id names no heading.
|
|
22
|
+
*/
|
|
23
|
+
function headingLevelFromStyleId(styleId) {
|
|
24
|
+
if (styleId === void 0) return void 0;
|
|
25
|
+
const m = /^Heading\s*([1-9])$/i.exec(styleId);
|
|
26
|
+
if (m) return Math.min(Number(m[1]), MAX_HEADING_LEVEL);
|
|
27
|
+
if (/^(Title|Subtitle)$/i.test(styleId)) return 1;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { headingLevelOf };
|
|
@@ -136,6 +136,7 @@ function mergeRun(base, override) {
|
|
|
136
136
|
rtl: override.rtl ?? base.rtl,
|
|
137
137
|
...lang !== void 0 ? { lang } : {},
|
|
138
138
|
...(override.shadingColorHex ?? base.shadingColorHex) !== void 0 ? { shadingColorHex: override.shadingColorHex ?? base.shadingColorHex } : {},
|
|
139
|
+
...(override.textOutline ?? base.textOutline) !== void 0 ? { textOutline: override.textOutline ?? base.textOutline } : {},
|
|
139
140
|
...(override.letterSpacingPt ?? base.letterSpacingPt) !== void 0 ? { letterSpacingPt: override.letterSpacingPt ?? base.letterSpacingPt } : {}
|
|
140
141
|
};
|
|
141
142
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Alignment, CellBorders, CellShading, FontFamilyMap, FrameProperties, NumberingReference, RunProperties, TabStop, UnderlineStyle, VerticalAlign } from '../document-model/index.js';
|
|
1
|
+
import { Alignment, CellBorders, CellShading, FontFamilyMap, FrameProperties, NumberingReference, RunProperties, TabStop, TextOutline, UnderlineStyle, VerticalAlign } from '../document-model/index.js';
|
|
2
2
|
import { Pt } from '../ir/index.js';
|
|
3
3
|
/**
|
|
4
4
|
* A run's fully-resolved properties: every field required because the cascade
|
|
@@ -30,6 +30,8 @@ export interface ResolvedRunProperties {
|
|
|
30
30
|
readonly shadingColorHex?: string;
|
|
31
31
|
/** §17.3.2.35 — extra space between the run's characters, in points. */
|
|
32
32
|
readonly letterSpacingPt?: Pt;
|
|
33
|
+
/** §21.1.2.3.9 — a line drawn round the glyphs themselves. */
|
|
34
|
+
readonly textOutline?: TextOutline;
|
|
33
35
|
}
|
|
34
36
|
/**
|
|
35
37
|
* A paragraph's fully-resolved properties: every field required, the cascade
|
|
@@ -42,6 +42,16 @@ export interface ProjectSheetOptions {
|
|
|
42
42
|
* supplies it; absent, the code is dropped exactly as before.
|
|
43
43
|
*/
|
|
44
44
|
readonly fileName?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Open each printed sheet with a heading carrying its NAME.
|
|
47
|
+
*
|
|
48
|
+
* Off by default, and deliberately: a printed page shows no tab name — Excel
|
|
49
|
+
* and Calc both emit it nowhere — so the paginated targets must not see one.
|
|
50
|
+
* A flowed target has no pages to tell one sheet from the next by, and a
|
|
51
|
+
* workbook rendered without them is a pile of tables with nothing to say
|
|
52
|
+
* which is which; markdown asks for this, and gets `# Sheet1`.
|
|
53
|
+
*/
|
|
54
|
+
readonly sheetHeadings?: boolean;
|
|
45
55
|
}
|
|
46
56
|
/**
|
|
47
57
|
* Project a {@link SheetDoc} into a {@link FlowDoc} (E-SHEET SA2): each grid sheet
|
|
@@ -67,7 +67,20 @@ function projectSheetDoc(sheet, options = {}) {
|
|
|
67
67
|
const sheetSection = withHeaderFooter(sectionFromWorksheet(ws.grid), ws, headersFooters, scaleSink.value, sheet.styles.fonts[0]?.sizePt, printed, options.fileName, sheet.themePalette, options.now);
|
|
68
68
|
if (printed === 0) firstSheetSection = sheetSection;
|
|
69
69
|
sheetSections.push(sheetSection);
|
|
70
|
-
if (
|
|
70
|
+
if (options.sheetHeadings === true) body.push({
|
|
71
|
+
kind: "paragraph",
|
|
72
|
+
paragraph: {
|
|
73
|
+
properties: {
|
|
74
|
+
outlineLevel: 0,
|
|
75
|
+
...printed > 0 ? { pageBreakBefore: true } : {}
|
|
76
|
+
},
|
|
77
|
+
runs: [{
|
|
78
|
+
text: ws.name,
|
|
79
|
+
properties: {}
|
|
80
|
+
}]
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
else if (printed > 0) body.push({
|
|
71
84
|
kind: "paragraph",
|
|
72
85
|
paragraph: PAGE_BREAK_PARAGRAPH
|
|
73
86
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FEATURES } from "../core/ir/features.js";
|
|
2
|
+
import { headingLevelOf } from "../core/outline.js";
|
|
2
3
|
import { detectImageFormat } from "../core/images.js";
|
|
3
4
|
import { PathBuilder, flipTransform, svgPathData } from "../core/vector.js";
|
|
4
5
|
import { EMPTY_STYLE_SHEET, resolveParagraphProperties, resolveRunProperties } from "../core/style-cascade/resolver.js";
|
|
@@ -321,8 +322,8 @@ function emitOneShape(out, shape, ctx) {
|
|
|
321
322
|
}
|
|
322
323
|
function emitParagraph(out, p, ctx) {
|
|
323
324
|
const resolved = resolveParagraphProperties(p.properties, EMPTY_STYLE_SHEET);
|
|
324
|
-
const lvl = resolved
|
|
325
|
-
const tag = lvl !== void 0
|
|
325
|
+
const lvl = headingLevelOf(resolved);
|
|
326
|
+
const tag = lvl !== void 0 ? `h${String(lvl)}` : "p";
|
|
326
327
|
const style = paragraphCss(resolved);
|
|
327
328
|
const dir = resolved.bidi ? " dir=\"rtl\"" : "";
|
|
328
329
|
const anchored = (p.bookmarks ?? []).filter((b) => ctx.referencedAnchors.has(b));
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -49,6 +49,9 @@ export type { FontProvider, FontRequest, FontAnswer } from './core/fonts/provide
|
|
|
49
49
|
export { svgWriter, writeSvg } from './svg/svg-writer.js';
|
|
50
50
|
export type { SvgWriteOptions } from './svg/svg-writer.js';
|
|
51
51
|
export { htmlWriter, writeHtml } from './html/html-writer.js';
|
|
52
|
+
export { markdownWriter, writeMarkdown } from './markdown/markdown-writer.js';
|
|
53
|
+
export type { StreamFilter, StreamFilters } from './pdf-reader/document.js';
|
|
54
|
+
export type { MarkdownWriteOptions } from './markdown/markdown-writer.js';
|
|
52
55
|
export { docxWriter, writeDocx } from './word/docx-writer.js';
|
|
53
56
|
export { layoutStyledDocument } from './layout/styled-layout.js';
|
|
54
57
|
export type { LaidOutDocument } from './layout/page-doc.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -17,8 +17,9 @@ import { readXlsx, xlsxReader } from "./excel/xlsx-reader.js";
|
|
|
17
17
|
import { NO_FONT, callerFontProvider, chainProviders, embeddedDocFontProvider, isEmbeddingRestricted, localFontProvider, readOs2FsType, remoteFontProvider } from "./core/fonts/provider.js";
|
|
18
18
|
import { docxWriter, writeDocx } from "./word/docx-writer.js";
|
|
19
19
|
import { htmlWriter, writeHtml } from "./html/html-writer.js";
|
|
20
|
+
import { markdownWriter, writeMarkdown } from "./markdown/markdown-writer.js";
|
|
20
21
|
import { svgWriter, writeSvg } from "./svg/svg-writer.js";
|
|
21
22
|
import { createConverter } from "./core/converter/facade.js";
|
|
22
23
|
import { WrongPasswordError, isEncryptedPackage } from "./core/crypto/offcrypto.js";
|
|
23
24
|
import { Ream } from "./core/converter/ream.js";
|
|
24
|
-
export { ConversionLossError, FEATURES, FontRegistry, NO_FONT, Ream, ResourceStore, WrongPasswordError, callerFontProvider, chainProviders, createConverter, createHyphenator, createLanguageHyphenator, docxReader, docxWriter, eighthPtToPt, embeddedDocFontProvider, emuToPt, fetchFontSet, formatLoss, getHyphenator, halfPtToPt, htmlWriter, inchToPt, isEmbeddingRestricted, isEncryptedPackage, layoutStyledDocument, localFontProvider, mmToPt, parseTtf, pt, pxToPt, readDocx, readOs2FsType, readXlsx, remoteFontProvider, renderStyledPdf, resolveFamilyKey, signPdf, splitPatternBundle, subsetTtf, svgWriter, twipsToPt, writeDocx, writeHtml, writeSvg, xlsxReader };
|
|
25
|
+
export { ConversionLossError, FEATURES, FontRegistry, NO_FONT, Ream, ResourceStore, WrongPasswordError, callerFontProvider, chainProviders, createConverter, createHyphenator, createLanguageHyphenator, docxReader, docxWriter, eighthPtToPt, embeddedDocFontProvider, emuToPt, fetchFontSet, formatLoss, getHyphenator, halfPtToPt, htmlWriter, inchToPt, isEmbeddingRestricted, isEncryptedPackage, layoutStyledDocument, localFontProvider, markdownWriter, mmToPt, parseTtf, pt, pxToPt, readDocx, readOs2FsType, readXlsx, remoteFontProvider, renderStyledPdf, resolveFamilyKey, signPdf, splitPatternBundle, subsetTtf, svgWriter, twipsToPt, writeDocx, writeHtml, writeMarkdown, writeSvg, xlsxReader };
|
|
@@ -14,7 +14,7 @@ function paintPlan(commands) {
|
|
|
14
14
|
behind.push(c);
|
|
15
15
|
continue;
|
|
16
16
|
}
|
|
17
|
-
if (c.z !== void 0 && c.type !== "
|
|
17
|
+
if (c.z !== void 0 && c.type !== "fill" && c.type !== "border" && (c.type !== "line" || c.pictureId !== void 0)) {
|
|
18
18
|
const key = c.pictureId !== void 0 ? `p${c.pictureId}` : `i${orderOf.size}`;
|
|
19
19
|
const run = ordered.get(key);
|
|
20
20
|
if (run) run.push(c);
|
|
@@ -4,6 +4,7 @@ import { shapeText } from "../core/font/opentype-layout.js";
|
|
|
4
4
|
import { createFontMeasure } from "../core/font/measure.js";
|
|
5
5
|
import { halfPtToPt, pt } from "../core/ir/units.js";
|
|
6
6
|
import { ResourceStore } from "../core/ir/resources.js";
|
|
7
|
+
import { headingLevelOf } from "../core/outline.js";
|
|
7
8
|
import { prepareImage } from "../core/images.js";
|
|
8
9
|
import { PathBuilder, flipTransform } from "../core/vector.js";
|
|
9
10
|
import { isEmf, readEmf } from "../core/metafile/emf.js";
|
|
@@ -1049,6 +1050,7 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1049
1050
|
const textLineGaps = /* @__PURE__ */ new Map();
|
|
1050
1051
|
let textHeightPt = 0;
|
|
1051
1052
|
const vertical = text?.vertical;
|
|
1053
|
+
const upright = text?.upright;
|
|
1052
1054
|
const warp = text?.warp;
|
|
1053
1055
|
if (text && text.content.length > 0) {
|
|
1054
1056
|
const innerWidth = warp ? Number.MAX_SAFE_INTEGER : vertical ? Math.max(1, heightPt - insetTopPt - insetBottomPt) : Math.max(1, widthPt - insetLeftPt - insetRightPt);
|
|
@@ -1235,6 +1237,7 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1235
1237
|
insetTopPt,
|
|
1236
1238
|
insetBottomPt,
|
|
1237
1239
|
anchor: text?.anchor ?? "t",
|
|
1240
|
+
...upright ? { upright } : {},
|
|
1238
1241
|
...vertical ? { vertical } : {},
|
|
1239
1242
|
...shape.altText ? { altText: shape.altText } : {},
|
|
1240
1243
|
...shape.float ? { float: shape.float } : {}
|
|
@@ -1885,6 +1888,9 @@ function chartPageItems(laid, x, bottomYUp, pageHeight, structId) {
|
|
|
1885
1888
|
var nextPictureId = 1;
|
|
1886
1889
|
function emitShapeText(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
1887
1890
|
if (sh.textLines.length === 0) return;
|
|
1891
|
+
const turnDeg = sh.upright === true || sh.flipH || sh.flipV ? 0 : -(sh.rotation60k || 0) / 6e4;
|
|
1892
|
+
const centreX = x + sh.widthPt / 2;
|
|
1893
|
+
const centreY = bottomYUp + sh.heightPt / 2;
|
|
1888
1894
|
if (sh.vertical) {
|
|
1889
1895
|
const up = sh.vertical === "vert270";
|
|
1890
1896
|
const innerHeight = Math.max(1, sh.heightPt - sh.insetTopPt - sh.insetBottomPt);
|
|
@@ -1894,12 +1900,13 @@ function emitShapeText(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
|
1894
1900
|
const lineOffset = alignmentOffset(line.resolved.alignment, line.contentWidthPt, innerHeight);
|
|
1895
1901
|
const off = lineBaselineOffset(line, line.resolved);
|
|
1896
1902
|
const baselineAcross = up ? across + h - off : across + off;
|
|
1903
|
+
const origin = spinAboutCentre(up ? x + baselineAcross : x + sh.widthPt - baselineAcross, up ? bottomYUp + sh.insetBottomPt + lineOffset : bottomYUp + sh.heightPt - sh.insetTopPt - lineOffset, centreX, centreY, turnDeg);
|
|
1897
1904
|
sink.push({
|
|
1898
1905
|
type: "line",
|
|
1899
1906
|
line,
|
|
1900
|
-
originX: pt(
|
|
1901
|
-
baselineY: pt(pageHeight -
|
|
1902
|
-
rotationDeg: up ? 90 : -90,
|
|
1907
|
+
originX: pt(origin.x),
|
|
1908
|
+
baselineY: pt(pageHeight - origin.y),
|
|
1909
|
+
rotationDeg: (up ? 90 : -90) + turnDeg,
|
|
1903
1910
|
...figId !== void 0 ? { structId: figId } : {}
|
|
1904
1911
|
});
|
|
1905
1912
|
across += h;
|
|
@@ -1939,17 +1946,44 @@ function emitShapeText(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
|
1939
1946
|
return;
|
|
1940
1947
|
}
|
|
1941
1948
|
const indentLeft = line.resolved.indentLeft + (line.firstLine ? line.resolved.indentFirstLine : 0);
|
|
1949
|
+
const origin = spinAboutCentre(x + sh.insetLeftPt + indentLeft + lineOffset, textY + lineBaselineOffset(line, line.resolved), centreX, centreY, turnDeg);
|
|
1942
1950
|
sink.push({
|
|
1943
1951
|
type: "line",
|
|
1944
1952
|
line,
|
|
1945
|
-
originX: pt(x
|
|
1946
|
-
baselineY: pt(pageHeight -
|
|
1953
|
+
originX: pt(origin.x),
|
|
1954
|
+
baselineY: pt(pageHeight - origin.y),
|
|
1955
|
+
...turnDeg !== 0 ? { rotationDeg: turnDeg } : {},
|
|
1947
1956
|
...figId !== void 0 ? { structId: figId } : {}
|
|
1948
1957
|
});
|
|
1949
1958
|
textY -= sh.textLineGaps?.get(i) ?? 0;
|
|
1950
1959
|
});
|
|
1951
1960
|
}
|
|
1952
1961
|
/**
|
|
1962
|
+
* A point spun about another, counter-clockwise in the y-up page frame.
|
|
1963
|
+
*
|
|
1964
|
+
* @param px The point's x.
|
|
1965
|
+
* @param py The point's y (y-up).
|
|
1966
|
+
* @param cx The centre's x.
|
|
1967
|
+
* @param cy The centre's y (y-up).
|
|
1968
|
+
* @param deg How far to turn, counter-clockwise; zero returns the point.
|
|
1969
|
+
* @returns The turned point.
|
|
1970
|
+
*/
|
|
1971
|
+
function spinAboutCentre(px, py, cx, cy, deg) {
|
|
1972
|
+
if (deg === 0) return {
|
|
1973
|
+
x: px,
|
|
1974
|
+
y: py
|
|
1975
|
+
};
|
|
1976
|
+
const rad = deg * Math.PI / 180;
|
|
1977
|
+
const cos = Math.cos(rad);
|
|
1978
|
+
const sin = Math.sin(rad);
|
|
1979
|
+
const dx = px - cx;
|
|
1980
|
+
const dy = py - cy;
|
|
1981
|
+
return {
|
|
1982
|
+
x: cx + dx * cos - dy * sin,
|
|
1983
|
+
y: cy + dx * sin + dy * cos
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
/**
|
|
1953
1987
|
* §20.1.9.10 — place a WordArt body's lines in the block's own frame and hand
|
|
1954
1988
|
* each one the warp that maps that frame onto the shape's box.
|
|
1955
1989
|
*
|
|
@@ -4339,16 +4373,8 @@ function resolveCellBorders(cellBorders, tableBorders, rowIdx, colStart, colEnd,
|
|
|
4339
4373
|
return out;
|
|
4340
4374
|
}
|
|
4341
4375
|
function paragraphStructType(resolved) {
|
|
4342
|
-
const lvl = resolved
|
|
4343
|
-
|
|
4344
|
-
return headingFromStyleId(resolved.styleId) ?? "P";
|
|
4345
|
-
}
|
|
4346
|
-
function headingFromStyleId(styleId) {
|
|
4347
|
-
if (!styleId) return null;
|
|
4348
|
-
const m = /^Heading\s*([1-9])$/i.exec(styleId);
|
|
4349
|
-
if (m) return `H${Math.min(Number(m[1]), 6)}`;
|
|
4350
|
-
if (/^(Title|Subtitle)$/i.test(styleId)) return "H1";
|
|
4351
|
-
return null;
|
|
4376
|
+
const lvl = headingLevelOf(resolved);
|
|
4377
|
+
return lvl !== void 0 ? `H${String(lvl)}` : "P";
|
|
4352
4378
|
}
|
|
4353
4379
|
function dominantParagraphLang(lines) {
|
|
4354
4380
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { DocumentWriter, WriteResult } from '../core/ir/adapters.js';
|
|
2
|
+
import { FlowDoc } from '../core/ir/flow.js';
|
|
3
|
+
/** Options for {@link writeMarkdown}. */
|
|
4
|
+
export interface MarkdownWriteOptions {
|
|
5
|
+
/**
|
|
6
|
+
* How a picture reaches the output. `'dataUri'` (the default) inlines the
|
|
7
|
+
* bytes as a `data:` URI, keeping the single-blob {@link WriteResult}
|
|
8
|
+
* contract and the zero-I/O promise; `'link'` emits a relative
|
|
9
|
+
* `./media/…` path and records that the bytes were not written anywhere;
|
|
10
|
+
* `'drop'` omits pictures entirely.
|
|
11
|
+
*/
|
|
12
|
+
readonly images?: 'dataUri' | 'link' | 'drop';
|
|
13
|
+
/**
|
|
14
|
+
* What a page break becomes. `'drop'` (the default) leaves it out and
|
|
15
|
+
* reports it: a paginated document breaks its pages wherever the layout
|
|
16
|
+
* needs to, and a rule at every one of them would litter the text. `'rule'`
|
|
17
|
+
* writes a `---` thematic break instead, which is what a SLIDE DECK wants —
|
|
18
|
+
* the `.pptx` and `.ppt` readers mark each slide boundary with a page break,
|
|
19
|
+
* and it is the only structure a deck has.
|
|
20
|
+
*/
|
|
21
|
+
readonly pageBreaks?: 'rule' | 'drop';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Render a {@link FlowDoc} to GitHub-Flavored Markdown (ir-design §7).
|
|
25
|
+
*
|
|
26
|
+
* A flow medium: no pagination, no layout engine and no fonts, so this is a
|
|
27
|
+
* pure, zero-I/O transform. Markdown says far less than the document model
|
|
28
|
+
* does — alignment, indents, colour, font metrics, tab stops and page
|
|
29
|
+
* geometry have no expression at all — so those are dropped and reported as
|
|
30
|
+
* {@link Loss} entries, deduplicated so one recurring omission reports once.
|
|
31
|
+
*
|
|
32
|
+
* @param flow The format-neutral interlayer document tree.
|
|
33
|
+
* @param options Picture handling; see {@link MarkdownWriteOptions}.
|
|
34
|
+
* @returns The encoded Markdown bytes plus the recorded {@link Loss} list.
|
|
35
|
+
*/
|
|
36
|
+
export declare function writeMarkdown(flow: FlowDoc, options?: MarkdownWriteOptions): WriteResult;
|
|
37
|
+
/**
|
|
38
|
+
* The flow-medium {@link DocumentWriter} adapter (id `'md'`), wrapping
|
|
39
|
+
* {@link writeMarkdown}, with the set of {@link FEATURES} it renders.
|
|
40
|
+
*/
|
|
41
|
+
export declare const markdownWriter: DocumentWriter<FlowDoc>;
|