reamkit 1.0.2 → 1.2.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 +12 -6
- package/dist/esm/{layout → core}/arc-to-bezier.d.ts +1 -1
- package/dist/esm/{layout → core}/arc-to-bezier.js +2 -2
- package/dist/esm/core/bytes.d.ts +1 -0
- package/dist/esm/core/bytes.js +6 -1
- package/dist/esm/core/converter/facade.d.ts +2 -2
- package/dist/esm/core/converter/facade.js +12 -0
- package/dist/esm/core/converter/project.js +2 -0
- package/dist/esm/core/converter/ream.d.ts +1 -1
- package/dist/esm/core/converter/ream.js +10 -0
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +58 -0
- package/dist/esm/{layout → core/drawingml}/chart-geometry.d.ts +1 -1
- package/dist/esm/{layout → core/drawingml}/preset-geometry.d.ts +2 -2
- package/dist/esm/{layout → core/drawingml}/preset-geometry.js +3 -3
- package/dist/esm/core/drawingml/shape-render.d.ts +8 -0
- package/dist/esm/core/drawingml/shape-render.js +71 -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/flow.d.ts +3 -0
- package/dist/esm/core/links.d.ts +6 -0
- package/dist/esm/core/links.js +19 -0
- package/dist/esm/core/numbering/apply.js +3 -1
- package/dist/esm/core/style-cascade/index.d.ts +1 -0
- package/dist/esm/core/style-cascade/index.js +1 -0
- package/dist/esm/core/style-cascade/table.d.ts +2 -0
- package/dist/esm/core/style-cascade/table.js +161 -0
- package/dist/esm/core/vector.d.ts +2 -0
- package/dist/esm/core/vector.js +19 -1
- package/dist/esm/html/html-writer.d.ts +4 -0
- package/dist/esm/html/html-writer.js +464 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/layout/page-doc.d.ts +3 -0
- package/dist/esm/layout/styled-layout.d.ts +19 -3
- package/dist/esm/layout/styled-layout.js +533 -197
- package/dist/esm/pdf/struct-tree.d.ts +7 -1
- package/dist/esm/pdf/struct-tree.js +22 -6
- package/dist/esm/pdf/styled-page-emitter.d.ts +1 -1
- package/dist/esm/pdf/styled-page-emitter.js +161 -29
- package/dist/esm/pdf/xmp.d.ts +1 -0
- package/dist/esm/pdf/xmp.js +4 -0
- package/dist/esm/svg/svg-writer.js +3 -11
- package/dist/esm/word/document-parser.d.ts +3 -0
- package/dist/esm/word/document-parser.js +210 -23
- package/dist/esm/word/docx-reader.js +30 -4
- package/dist/esm/word/drawing-parser.d.ts +4 -1
- package/dist/esm/word/drawing-parser.js +53 -2
- package/dist/esm/word/index.d.ts +2 -2
- package/dist/esm/word/styles-parser.js +134 -1
- package/dist/esm/word/table-parser.js +35 -0
- package/package.json +1 -1
- /package/dist/esm/{layout → core/drawingml}/chart-geometry.js +0 -0
package/README.md
CHANGED
|
@@ -33,6 +33,7 @@ const bytes = new Uint8Array(await file.arrayBuffer());
|
|
|
33
33
|
const doc = Ream.parse(bytes); // docx or xlsx — sniffed
|
|
34
34
|
const pdf = await doc.convert('pdf'); // async — fetches a font if needed
|
|
35
35
|
const svg = await doc.convert('svg'); // same parse, different target
|
|
36
|
+
const html = await doc.convert('html'); // flowed HTML — needs no fonts at all
|
|
36
37
|
|
|
37
38
|
// Hand the bytes to the browser: preview, download, upload, …
|
|
38
39
|
const url = URL.createObjectURL(new Blob([pdf], { type: 'application/pdf' }));
|
|
@@ -84,7 +85,9 @@ are always used first — glyph-exact, no substitution.
|
|
|
84
85
|
### Archival PDF/A + embedded source
|
|
85
86
|
|
|
86
87
|
The whole PDF/A family is supported (1a/1b, 2a/2b/2u, 3a/3b/3u —
|
|
87
|
-
veraPDF-validated)
|
|
88
|
+
veraPDF-validated), plus accessible **PDF/UA-1** (`pdfUA: true`, also
|
|
89
|
+
veraPDF-validated and combinable with PDF/A in one file). PDF/A-3 can carry
|
|
90
|
+
the source document inside the PDF:
|
|
88
91
|
|
|
89
92
|
```ts
|
|
90
93
|
const { bytes: pdfa, losses } = await doc.convertWithReport('pdf', {
|
|
@@ -147,7 +150,7 @@ automatically from the document's `docProps/core.xml`), `attachments`
|
|
|
147
150
|
|
|
148
151
|
### Lower-level APIs
|
|
149
152
|
|
|
150
|
-
- `docxReader` / `xlsxReader`, `svgWriter` — the `@experimental` reader/writer
|
|
153
|
+
- `docxReader` / `xlsxReader`, `svgWriter`, `htmlWriter` — the `@experimental` reader/writer
|
|
151
154
|
interfaces of the interlayer, for building custom pipelines (and keeping
|
|
152
155
|
unused formats out of your bundle); `layoutStyledDocument` produces the
|
|
153
156
|
frozen page model (`PageItem` pages in a top-left `Pt` frame) they consume.
|
|
@@ -156,14 +159,17 @@ automatically from the document's `docProps/core.xml`), `attachments`
|
|
|
156
159
|
|
|
157
160
|
## Scope
|
|
158
161
|
|
|
159
|
-
Implemented: WordprocessingML text/styles/tables/lists/
|
|
160
|
-
layout/headers-footers
|
|
162
|
+
Implemented: WordprocessingML text/styles/tables (incl. table styles)/lists/
|
|
163
|
+
multi-section and multi-column layout/headers-footers (incl. PAGE/NUMPAGES
|
|
164
|
+
fields)/footnotes and endnotes/hyperlinks and bookmarks/floating drawings/
|
|
165
|
+
images/tracked changes, SpreadsheetML grids,
|
|
161
166
|
number formats and the print model (gridlines, print area, fit-to-page,
|
|
162
167
|
repeated titles, page breaks), DrawingML shapes and charts, OMML math,
|
|
163
168
|
Type0+CIDFontType2 embedding with subsetting, Knuth-Plass line breaking,
|
|
164
169
|
Liang hyphenation, OpenType ligatures/kerning + Arabic cursive joining,
|
|
165
|
-
BiDi (UAX #9),
|
|
166
|
-
|
|
170
|
+
BiDi (UAX #9), hyperlinks (PDF link annotations + HTML anchors,
|
|
171
|
+
scheme-allowlisted), tagged PDF, PDF/A-1/2/3 (a/b/u), digital signatures
|
|
172
|
+
(PKCS#7/ECDSA/PAdES/RFC 3161), SVG page preview, flowed HTML export.
|
|
167
173
|
|
|
168
174
|
See `handoff.md` for the full feature matrix and known limitations.
|
|
169
175
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PathSegment } from '
|
|
1
|
+
import { PathSegment } from './vector.js';
|
|
2
2
|
export declare function arcPoint(cx: number, cy: number, rx: number, ry: number, angleRad: number): readonly [number, number];
|
|
3
3
|
export declare function arcToBeziers(cx: number, cy: number, rx: number, ry: number, startAngleRad: number, sweepAngleRad: number): Array<PathSegment>;
|
|
4
4
|
export declare function ellipseSegments(w: number, h: number): ReadonlyArray<PathSegment>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PathBuilder } from "
|
|
2
|
-
//#region src/
|
|
1
|
+
import { PathBuilder } from "./vector.js";
|
|
2
|
+
//#region src/core/arc-to-bezier.ts
|
|
3
3
|
function arcPoint(cx, cy, rx, ry, angleRad) {
|
|
4
4
|
return [cx + rx * Math.cos(angleRad), cy + ry * Math.sin(angleRad)];
|
|
5
5
|
}
|
package/dist/esm/core/bytes.d.ts
CHANGED
package/dist/esm/core/bytes.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
//#region src/core/bytes.ts
|
|
2
|
+
function toBase64(bytes) {
|
|
3
|
+
let bin = "";
|
|
4
|
+
for (const b of bytes) bin += String.fromCharCode(b);
|
|
5
|
+
return btoa(bin);
|
|
6
|
+
}
|
|
2
7
|
function bytesInclude(haystack, needle) {
|
|
3
8
|
const n = new TextEncoder().encode(needle);
|
|
4
9
|
outer: for (let i = 0; i + n.length <= haystack.length; i++) {
|
|
@@ -8,4 +13,4 @@ function bytesInclude(haystack, needle) {
|
|
|
8
13
|
return false;
|
|
9
14
|
}
|
|
10
15
|
//#endregion
|
|
11
|
-
export { bytesInclude };
|
|
16
|
+
export { bytesInclude, toBase64 };
|
|
@@ -5,8 +5,8 @@ import { FontProvider } from '../fonts/provider.js';
|
|
|
5
5
|
import { Loss, LossReport } from '../ir/index.js';
|
|
6
6
|
import { FontBytesByVariant } from '../font/index.js';
|
|
7
7
|
export interface ConvertOptions extends ConvertDocxOptions {
|
|
8
|
-
/** Target
|
|
9
|
-
readonly to?: 'pdf' | 'svg';
|
|
8
|
+
/** Target: 'pdf' (default), 'svg' (page-stack preview) or 'html' (flowed). */
|
|
9
|
+
readonly to?: 'pdf' | 'svg' | 'html';
|
|
10
10
|
/**
|
|
11
11
|
* Strict mode (handoff v1 §5): throw ConversionLossError on the first
|
|
12
12
|
* recorded loss instead of returning it in the report.
|
|
@@ -6,6 +6,7 @@ import { docxReader } from "../../word/docx-reader.js";
|
|
|
6
6
|
import { xlsxReader } from "../../excel/xlsx-reader.js";
|
|
7
7
|
import { chainProviders } from "../fonts/provider.js";
|
|
8
8
|
import { flowRenderOptions } from "./project.js";
|
|
9
|
+
import { writeHtml } from "../../html/html-writer.js";
|
|
9
10
|
import { writeSvg } from "../../svg/svg-writer.js";
|
|
10
11
|
import { convertDocxToPdf } from "../../word/docx-to-pdf.js";
|
|
11
12
|
import { convertXlsxToPdf } from "../../excel/xlsx-to-pdf.js";
|
|
@@ -19,6 +20,17 @@ function createConverter(opts = {}) {
|
|
|
19
20
|
const reader = detect(bytes);
|
|
20
21
|
if (!reader) throw new Error("Unrecognized input format (no registered reader sniffs these bytes)");
|
|
21
22
|
const losses = [];
|
|
23
|
+
if (to === "html") {
|
|
24
|
+
const { doc: flow, losses: readLosses } = reader.read(bytes);
|
|
25
|
+
losses.push(...readLosses);
|
|
26
|
+
const html = writeHtml(flow);
|
|
27
|
+
losses.push(...html.losses);
|
|
28
|
+
if (strict && losses.length > 0) throw new ConversionLossError(losses[0]);
|
|
29
|
+
return {
|
|
30
|
+
bytes: html.bytes,
|
|
31
|
+
losses
|
|
32
|
+
};
|
|
33
|
+
}
|
|
22
34
|
let conv = rest;
|
|
23
35
|
if (fontProviders && fontProviders.length > 0 && !rest.fonts && !rest.fontBytes) {
|
|
24
36
|
const { fonts, loss } = await resolveFontsViaChain(fontProviders);
|
|
@@ -7,6 +7,8 @@ function flowRenderOptions(flow) {
|
|
|
7
7
|
...flow.sections.length > 0 ? { sections: flow.sections } : {},
|
|
8
8
|
...flow.section ? { section: flow.section } : {},
|
|
9
9
|
...flow.headersFooters ? { headersFooters: flow.headersFooters } : {},
|
|
10
|
+
...flow.footnotes ? { footnotes: flow.footnotes } : {},
|
|
11
|
+
...flow.endnotes ? { endnotes: flow.endnotes } : {},
|
|
10
12
|
resources: flow.resources,
|
|
11
13
|
...flow.charts ? { charts: flow.charts } : {},
|
|
12
14
|
...flow.embeddedFonts ? { embeddedFonts: flow.embeddedFonts } : {},
|
|
@@ -6,7 +6,7 @@ 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 { SignatureOptions, StyledRenderOptions } from '../../pdf/index.js';
|
|
9
|
-
export type ReamTarget = 'pdf' | 'svg';
|
|
9
|
+
export type ReamTarget = 'pdf' | 'svg' | 'html';
|
|
10
10
|
export interface ReamParseOptions {
|
|
11
11
|
readonly readers?: ReadonlyArray<DocumentReader>;
|
|
12
12
|
}
|
|
@@ -6,6 +6,7 @@ import { signPdf } from "../../pdf/signature.js";
|
|
|
6
6
|
import { renderStyledPdf } from "../../pdf/styled-page-renderer.js";
|
|
7
7
|
import "../../pdf/index.js";
|
|
8
8
|
import { flowRenderOptions } from "./project.js";
|
|
9
|
+
import { writeHtml } from "../../html/html-writer.js";
|
|
9
10
|
import { writeSvg } from "../../svg/svg-writer.js";
|
|
10
11
|
import { resolveDocxAutoFonts } from "../../word/docx-to-pdf.js";
|
|
11
12
|
import { DEFAULT_READERS, resolveFontsViaChain } from "./facade.js";
|
|
@@ -36,6 +37,15 @@ var Ream = class Ream {
|
|
|
36
37
|
}
|
|
37
38
|
async convertWithReport(to, options = {}) {
|
|
38
39
|
const losses = [...this.losses];
|
|
40
|
+
if (to === "html") {
|
|
41
|
+
const html = writeHtml(this.flow);
|
|
42
|
+
losses.push(...html.losses);
|
|
43
|
+
this.enforceStrict(options, losses);
|
|
44
|
+
return {
|
|
45
|
+
bytes: html.bytes,
|
|
46
|
+
losses
|
|
47
|
+
};
|
|
48
|
+
}
|
|
39
49
|
const { fonts, registriesByFamily } = await this.resolveFonts(options, losses);
|
|
40
50
|
const registry = FontRegistry.fromBytes(fonts);
|
|
41
51
|
if (to === "svg") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { AbstractNumbering, Alignment, Border, BorderStyle, BodyElement, CellBorders, CellMerge, DocumentInfo, CellMargins, CellProperties, CellShading, Chart, ChartBlock, ChartDataPoint, ChartSeries, ChartType, CustomGeometry, CustomPathCmd, DocumentModel, FontFamilyMap, HeaderFooterReference, HeaderFooterType, ImageBlock, InlineImage, MathAccent, MathBar, MathDelimiter, MathEqArray, MathFraction, MathFunc, MathGroupChr, MathLimit, MathMatrix, MathNary, MathNode, MathRadical, MathRow, MathRun, MathScript, Numbering, NumberingFormat, NumberingInstance, NumberingLevel, NumberingReference, PageMargins, PageSize, Paragraph, ParagraphProperties, Run, RunProperties, RowProperties, Section, SectionProperties, ShapeBlock, ShapeDash, ShapeFill, ShapeFillKind, ShapeGeometry, ShapeLine, ShapeTextBody, ShapeTransform, Style, StyleSheet, StyleType, Table, TableCell, TableProperties, TableRow, UnderlineStyle, VerticalAlign, } from './types.js';
|
|
1
|
+
export type { AbstractNumbering, Alignment, Border, BorderStyle, BodyElement, CellBorders, CellMerge, DocumentInfo, CellMargins, CellProperties, CellShading, Chart, ChartBlock, ChartDataPoint, ChartSeries, ChartType, CustomGeometry, CustomPathCmd, DocumentModel, FontFamilyMap, HeaderFooterReference, HeaderFooterType, ImageBlock, InlineImage, MathAccent, MathBar, MathDelimiter, MathEqArray, MathFraction, MathFunc, MathGroupChr, MathLimit, MathMatrix, MathNary, MathNode, MathRadical, MathRow, MathRun, MathScript, Numbering, NumberingFormat, NumberingInstance, NumberingLevel, NumberingReference, PageMargins, PageSize, Paragraph, ParagraphProperties, Run, RunProperties, RowProperties, Section, FloatAnchor, SectionColumns, SectionProperties, ShapeBlock, ShapeDash, ShapeFill, ShapeFillKind, ShapeGeometry, ShapeLine, ShapeTextBody, ShapeTransform, Style, StyleSheet, StyleType, Table, TableCell, TableLook, TableProperties, TableStyleCondition, TableStyleConditionType, TableStyleLayer, TableRow, UnderlineStyle, VerticalAlign, } from './types.js';
|
|
@@ -126,6 +126,12 @@ export interface Run {
|
|
|
126
126
|
readonly native?: NativeBag;
|
|
127
127
|
readonly text: string;
|
|
128
128
|
readonly properties: RunProperties;
|
|
129
|
+
readonly href?: string;
|
|
130
|
+
readonly field?: 'PAGE' | 'NUMPAGES';
|
|
131
|
+
readonly anchor?: string;
|
|
132
|
+
readonly footnoteRef?: string;
|
|
133
|
+
readonly endnoteRef?: string;
|
|
134
|
+
readonly noteNumber?: true;
|
|
129
135
|
readonly inlineImage?: InlineImage;
|
|
130
136
|
readonly math?: MathNode;
|
|
131
137
|
readonly pageBreak?: boolean;
|
|
@@ -134,6 +140,7 @@ export interface Paragraph {
|
|
|
134
140
|
readonly native?: NativeBag;
|
|
135
141
|
readonly properties: ParagraphProperties;
|
|
136
142
|
readonly runs: ReadonlyArray<Run>;
|
|
143
|
+
readonly bookmarks?: ReadonlyArray<string>;
|
|
137
144
|
}
|
|
138
145
|
export type NumberingFormat = 'decimal' | 'lowerLetter' | 'upperLetter' | 'lowerRoman' | 'upperRoman' | 'bullet' | 'none';
|
|
139
146
|
export interface NumberingReference {
|
|
@@ -161,6 +168,18 @@ export interface Numbering {
|
|
|
161
168
|
readonly numInstances: ReadonlyMap<string, NumberingInstance>;
|
|
162
169
|
}
|
|
163
170
|
export type StyleType = 'paragraph' | 'character' | 'table' | 'numbering';
|
|
171
|
+
export interface TableStyleLayer {
|
|
172
|
+
readonly borders?: CellBorders;
|
|
173
|
+
readonly cellMargins?: CellMargins;
|
|
174
|
+
readonly shading?: CellShading;
|
|
175
|
+
readonly runProperties?: RunProperties;
|
|
176
|
+
readonly paragraphProperties?: ParagraphProperties;
|
|
177
|
+
}
|
|
178
|
+
export type TableStyleConditionType = 'wholeTable' | 'band1Vert' | 'band2Vert' | 'band1Horz' | 'band2Horz' | 'firstCol' | 'lastCol' | 'firstRow' | 'lastRow' | 'nwCell' | 'neCell' | 'swCell' | 'seCell';
|
|
179
|
+
export interface TableStyleCondition {
|
|
180
|
+
readonly type: TableStyleConditionType;
|
|
181
|
+
readonly layer: TableStyleLayer;
|
|
182
|
+
}
|
|
164
183
|
export interface Style {
|
|
165
184
|
readonly id: string;
|
|
166
185
|
readonly type: StyleType;
|
|
@@ -168,6 +187,10 @@ export interface Style {
|
|
|
168
187
|
readonly isDefault: boolean;
|
|
169
188
|
readonly runProperties: RunProperties;
|
|
170
189
|
readonly paragraphProperties: ParagraphProperties;
|
|
190
|
+
readonly tableLayer?: TableStyleLayer;
|
|
191
|
+
readonly tableConditions?: ReadonlyArray<TableStyleCondition>;
|
|
192
|
+
readonly rowBandSize?: number;
|
|
193
|
+
readonly colBandSize?: number;
|
|
171
194
|
}
|
|
172
195
|
export interface StyleSheet {
|
|
173
196
|
readonly defaultRunProperties: RunProperties;
|
|
@@ -213,7 +236,17 @@ export interface RowProperties {
|
|
|
213
236
|
readonly isHeader?: boolean;
|
|
214
237
|
readonly pageBreakBefore?: boolean;
|
|
215
238
|
}
|
|
239
|
+
export interface TableLook {
|
|
240
|
+
readonly firstRow?: boolean;
|
|
241
|
+
readonly lastRow?: boolean;
|
|
242
|
+
readonly firstColumn?: boolean;
|
|
243
|
+
readonly lastColumn?: boolean;
|
|
244
|
+
readonly noHBand?: boolean;
|
|
245
|
+
readonly noVBand?: boolean;
|
|
246
|
+
}
|
|
216
247
|
export interface TableProperties {
|
|
248
|
+
readonly styleId?: string;
|
|
249
|
+
readonly look?: TableLook;
|
|
217
250
|
readonly widthPt?: Pt;
|
|
218
251
|
readonly widthFraction?: number;
|
|
219
252
|
readonly widthType?: 'auto' | 'dxa' | 'pct' | 'nil';
|
|
@@ -237,6 +270,7 @@ export interface Table {
|
|
|
237
270
|
readonly rows: ReadonlyArray<TableRow>;
|
|
238
271
|
}
|
|
239
272
|
export interface ImageBlock {
|
|
273
|
+
readonly float?: FloatAnchor;
|
|
240
274
|
readonly resource?: ResourceId;
|
|
241
275
|
readonly width: Pt;
|
|
242
276
|
readonly height: Pt;
|
|
@@ -312,6 +346,7 @@ export interface ShapeTextBody {
|
|
|
312
346
|
readonly anchor?: 't' | 'ctr' | 'b';
|
|
313
347
|
}
|
|
314
348
|
export interface ShapeBlock {
|
|
349
|
+
readonly float?: FloatAnchor;
|
|
315
350
|
readonly width: Pt;
|
|
316
351
|
readonly height: Pt;
|
|
317
352
|
readonly geometry: ShapeGeometry;
|
|
@@ -349,6 +384,7 @@ export interface Chart {
|
|
|
349
384
|
readonly valAxisTitle?: string;
|
|
350
385
|
}
|
|
351
386
|
export interface ChartBlock {
|
|
387
|
+
readonly float?: FloatAnchor;
|
|
352
388
|
readonly chartRelId: string;
|
|
353
389
|
readonly width: Pt;
|
|
354
390
|
readonly height: Pt;
|
|
@@ -396,6 +432,28 @@ export interface SectionProperties {
|
|
|
396
432
|
readonly footers: ReadonlyArray<HeaderFooterReference>;
|
|
397
433
|
readonly titlePg?: boolean;
|
|
398
434
|
readonly evenAndOddHeaders?: boolean;
|
|
435
|
+
readonly columns?: SectionColumns;
|
|
436
|
+
}
|
|
437
|
+
export interface FloatAnchor {
|
|
438
|
+
readonly wrap: 'none' | 'square' | 'tight' | 'through' | 'topAndBottom';
|
|
439
|
+
readonly behind?: boolean;
|
|
440
|
+
readonly posH?: {
|
|
441
|
+
readonly relativeFrom: 'margin' | 'page' | 'column';
|
|
442
|
+
readonly offsetPt?: Pt;
|
|
443
|
+
readonly align?: 'left' | 'center' | 'right';
|
|
444
|
+
};
|
|
445
|
+
readonly posV?: {
|
|
446
|
+
readonly relativeFrom: 'margin' | 'page' | 'paragraph' | 'line';
|
|
447
|
+
readonly offsetPt?: Pt;
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
export interface SectionColumns {
|
|
451
|
+
readonly count: number;
|
|
452
|
+
readonly spacePt: number;
|
|
453
|
+
readonly explicit?: ReadonlyArray<{
|
|
454
|
+
readonly widthPt: number;
|
|
455
|
+
readonly spacePt: number;
|
|
456
|
+
}>;
|
|
399
457
|
}
|
|
400
458
|
export interface Section {
|
|
401
459
|
readonly properties: SectionProperties;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CustomGeometry } from '../
|
|
2
|
-
import { PathSegment, VectorPath } from '../
|
|
1
|
+
import { CustomGeometry } from '../document-model/index.js';
|
|
2
|
+
import { PathSegment, VectorPath } from '../vector.js';
|
|
3
3
|
export declare function rectSegments(w: number, h: number): ReadonlyArray<PathSegment>;
|
|
4
4
|
export declare function rectPath(w: number, h: number): VectorPath;
|
|
5
5
|
export declare function presetPaths(preset: string, w: number, h: number, adjust: ReadonlyMap<string, number>): Array<VectorPath> | null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { PathBuilder } from "../
|
|
2
|
-
import { arcToBeziers, ellipseSegments, roundRectSegments } from "
|
|
3
|
-
//#region src/
|
|
1
|
+
import { PathBuilder } from "../vector.js";
|
|
2
|
+
import { arcToBeziers, ellipseSegments, roundRectSegments } from "../arc-to-bezier.js";
|
|
3
|
+
//#region src/core/drawingml/preset-geometry.ts
|
|
4
4
|
function rectSegments(w, h) {
|
|
5
5
|
return new PathBuilder().moveTo(0, 0).lineTo(w, 0).lineTo(w, h).lineTo(0, h).close().build().segments;
|
|
6
6
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ShapeGeometry, ShapeLine } from '../document-model/index.js';
|
|
2
|
+
import { StrokeStyle, VectorPath } from '../vector.js';
|
|
3
|
+
export declare const EMU_PER_PT = 12700;
|
|
4
|
+
export declare const DEFAULT_INSET_LR_PT: number;
|
|
5
|
+
export declare const DEFAULT_INSET_TB_PT: number;
|
|
6
|
+
export declare function buildShapePaths(geometry: ShapeGeometry, widthPt: number, heightPt: number): Array<VectorPath>;
|
|
7
|
+
export declare function buildStroke(line: ShapeLine | undefined): StrokeStyle | undefined;
|
|
8
|
+
export declare function buildShapeTransform(pageX: number, pageY: number, widthPt: number, heightPt: number, rotation60k: number, flipH: boolean, flipV: boolean): [number, number, number, number, number, number];
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { customPaths, presetPaths, rectPath } from "./preset-geometry.js";
|
|
2
|
+
//#region src/core/drawingml/shape-render.ts
|
|
3
|
+
var EMU_PER_PT = 12700;
|
|
4
|
+
var DEFAULT_LINE_WIDTH_EMU = 9525;
|
|
5
|
+
var DEFAULT_INSET_LR_PT = 91440 / EMU_PER_PT;
|
|
6
|
+
var DEFAULT_INSET_TB_PT = 45720 / EMU_PER_PT;
|
|
7
|
+
function buildShapePaths(geometry, widthPt, heightPt) {
|
|
8
|
+
if (geometry.kind === "preset") return presetPaths(geometry.preset ?? "rect", widthPt, heightPt, geometry.adjust ?? /* @__PURE__ */ new Map()) ?? [rectPath(widthPt, heightPt)];
|
|
9
|
+
if (geometry.custom) return customPaths(geometry.custom, widthPt, heightPt);
|
|
10
|
+
return [rectPath(widthPt, heightPt)];
|
|
11
|
+
}
|
|
12
|
+
function buildStroke(line) {
|
|
13
|
+
if (!line || line.fill === "none") return void 0;
|
|
14
|
+
const widthPt = line.width ?? DEFAULT_LINE_WIDTH_EMU / 12700;
|
|
15
|
+
const dash = line.dash && line.dash !== "solid" ? dashPattern(line.dash, widthPt) : void 0;
|
|
16
|
+
const cap = line.cap === "flat" ? "butt" : line.cap;
|
|
17
|
+
return {
|
|
18
|
+
colorHex: line.colorHex ?? "000000",
|
|
19
|
+
widthPt,
|
|
20
|
+
...dash ? { dash } : {},
|
|
21
|
+
...cap ? { cap } : {}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function dashPattern(dash, w) {
|
|
25
|
+
const u = Math.max(w, .1);
|
|
26
|
+
switch (dash) {
|
|
27
|
+
case "solid": return;
|
|
28
|
+
case "dot": return [u, 2 * u];
|
|
29
|
+
case "dash": return [4 * u, 3 * u];
|
|
30
|
+
case "dashDot": return [
|
|
31
|
+
4 * u,
|
|
32
|
+
3 * u,
|
|
33
|
+
u,
|
|
34
|
+
3 * u
|
|
35
|
+
];
|
|
36
|
+
case "lgDash": return [8 * u, 3 * u];
|
|
37
|
+
case "lgDashDot": return [
|
|
38
|
+
8 * u,
|
|
39
|
+
3 * u,
|
|
40
|
+
u,
|
|
41
|
+
3 * u
|
|
42
|
+
];
|
|
43
|
+
case "sysDash": return [3 * u, u];
|
|
44
|
+
case "sysDot": return [u, u];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function buildShapeTransform(pageX, pageY, widthPt, heightPt, rotation60k, flipH, flipV) {
|
|
48
|
+
const theta = -rotation60k / 6e4 * (Math.PI / 180);
|
|
49
|
+
const sx = flipH ? -1 : 1;
|
|
50
|
+
const sy = flipV ? -1 : 1;
|
|
51
|
+
const cos = Math.cos(theta);
|
|
52
|
+
const sin = Math.sin(theta);
|
|
53
|
+
const a = sx * cos;
|
|
54
|
+
const b = sx * sin;
|
|
55
|
+
const c = -sy * sin;
|
|
56
|
+
const d = sy * cos;
|
|
57
|
+
const cxL = widthPt / 2;
|
|
58
|
+
const cyL = heightPt / 2;
|
|
59
|
+
const centerX = pageX + cxL;
|
|
60
|
+
const centerY = pageY + cyL;
|
|
61
|
+
return [
|
|
62
|
+
a,
|
|
63
|
+
b,
|
|
64
|
+
c,
|
|
65
|
+
d,
|
|
66
|
+
centerX - (a * cxL + c * cyL),
|
|
67
|
+
centerY - (b * cxL + d * cyL)
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { DEFAULT_INSET_LR_PT, DEFAULT_INSET_TB_PT, buildShapePaths, buildShapeTransform, buildStroke };
|
|
@@ -8,6 +8,7 @@ export declare const FEATURES: {
|
|
|
8
8
|
readonly headersFooters: "headersFooters";
|
|
9
9
|
readonly images: "images";
|
|
10
10
|
readonly imagesJp2: "images.jp2";
|
|
11
|
+
readonly hyperlinks: "hyperlinks";
|
|
11
12
|
readonly shapes: "shapes";
|
|
12
13
|
readonly charts: "charts";
|
|
13
14
|
readonly math: "math";
|
|
@@ -11,6 +11,9 @@ export interface FlowDoc {
|
|
|
11
11
|
readonly styles: StyleSheet;
|
|
12
12
|
readonly numbering?: Numbering;
|
|
13
13
|
readonly headersFooters?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|
|
14
|
+
/** §17.11 footnotes/endnotes content by id (separator stubs excluded). */
|
|
15
|
+
readonly footnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|
|
16
|
+
readonly endnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|
|
14
17
|
/** Parsed charts keyed by relationship id (ChartBlock.chartRelId). */
|
|
15
18
|
readonly charts?: ReadonlyMap<string, Chart>;
|
|
16
19
|
/** Content-addressed binary resources (images). */
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the trimmed URL when its scheme is on the allowlist, undefined
|
|
3
|
+
* otherwise (including scheme-less/relative targets — those are not external
|
|
4
|
+
* links for a converted document).
|
|
5
|
+
*/
|
|
6
|
+
export declare function sanitizeHref(href: string): string | undefined;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/core/links.ts
|
|
2
|
+
var ALLOWED_SCHEMES = new Set([
|
|
3
|
+
"http",
|
|
4
|
+
"https",
|
|
5
|
+
"mailto"
|
|
6
|
+
]);
|
|
7
|
+
/**
|
|
8
|
+
* Returns the trimmed URL when its scheme is on the allowlist, undefined
|
|
9
|
+
* otherwise (including scheme-less/relative targets — those are not external
|
|
10
|
+
* links for a converted document).
|
|
11
|
+
*/
|
|
12
|
+
function sanitizeHref(href) {
|
|
13
|
+
const url = href.trim();
|
|
14
|
+
const m = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(url);
|
|
15
|
+
if (!m) return void 0;
|
|
16
|
+
return ALLOWED_SCHEMES.has(m[1].toLowerCase()) ? url : void 0;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { sanitizeHref };
|
|
@@ -14,8 +14,10 @@ function applyNumbering(body, numbering) {
|
|
|
14
14
|
text: `${marker}\t`,
|
|
15
15
|
properties: level?.runProperties ?? {}
|
|
16
16
|
};
|
|
17
|
+
const newProps = mergeIndentFromLevel(p.properties, level?.paragraphProperties);
|
|
17
18
|
return {
|
|
18
|
-
|
|
19
|
+
...p,
|
|
20
|
+
properties: newProps,
|
|
19
21
|
runs: [markerRun, ...p.runs]
|
|
20
22
|
};
|
|
21
23
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { EMPTY_STYLE_SHEET, resolveBodyStyles, resolveHeadersFootersStyles, resolveParagraphProperties, resolveRunProperties, } from './resolver.js';
|
|
2
2
|
export type { ResolvedRunProperties, ResolvedParagraphProperties, } from './types.js';
|
|
3
3
|
export { DEFAULT_RESOLVED_RUN, DEFAULT_RESOLVED_PARAGRAPH } from './types.js';
|
|
4
|
+
export { resolveTableStyles } from './table.js';
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
//#region src/core/style-cascade/table.ts
|
|
2
|
+
function resolveTableStyles(body, sheet) {
|
|
3
|
+
const visit = (el) => {
|
|
4
|
+
if (el.kind === "table") {
|
|
5
|
+
applyTableStyle(el.table, sheet);
|
|
6
|
+
for (const row of el.table.rows) for (const cell of row.cells) for (const child of cell.content) visit(child);
|
|
7
|
+
} else if (el.kind === "shape" && el.shape.text) for (const child of el.shape.text.content) visit(child);
|
|
8
|
+
};
|
|
9
|
+
for (const el of body) visit(el);
|
|
10
|
+
return body;
|
|
11
|
+
}
|
|
12
|
+
function applyTableStyle(table, sheet) {
|
|
13
|
+
const styleId = table.properties.styleId;
|
|
14
|
+
if (!styleId) return;
|
|
15
|
+
const folded = foldTableStyleChain(styleId, sheet);
|
|
16
|
+
if (!folded) return;
|
|
17
|
+
const tp = table.properties;
|
|
18
|
+
if (!tp.borders && folded.base.borders) tp.borders = folded.base.borders;
|
|
19
|
+
if (!tp.defaultCellMargins && folded.base.cellMargins) tp.defaultCellMargins = folded.base.cellMargins;
|
|
20
|
+
const look = table.properties.look ?? {};
|
|
21
|
+
const rows = table.rows;
|
|
22
|
+
const colCount = Math.max(table.grid.length, ...rows.map((r) => r.cells.reduce((s, c) => s + (c.properties.colSpan ?? 1), 0)));
|
|
23
|
+
for (let r = 0; r < rows.length; r++) {
|
|
24
|
+
const row = rows[r];
|
|
25
|
+
let colStart = 0;
|
|
26
|
+
for (const cell of row.cells) {
|
|
27
|
+
const span = cell.properties.colSpan ?? 1;
|
|
28
|
+
const layer = cellLayer(folded, look, {
|
|
29
|
+
firstRow: r === 0,
|
|
30
|
+
lastRow: r === rows.length - 1,
|
|
31
|
+
firstCol: colStart === 0,
|
|
32
|
+
lastCol: colStart + span >= colCount,
|
|
33
|
+
rowBand: bandIndex(r, look.firstRow === true, folded.rowBandSize),
|
|
34
|
+
colBand: bandIndex(colStart, look.firstColumn === true, folded.colBandSize),
|
|
35
|
+
noHBand: look.noHBand === true,
|
|
36
|
+
noVBand: look.noVBand === true
|
|
37
|
+
});
|
|
38
|
+
colStart += span;
|
|
39
|
+
if (!layer) continue;
|
|
40
|
+
applyLayerToCell(cell, layer);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function applyLayerToCell(tableCell, layer) {
|
|
45
|
+
const cell = tableCell;
|
|
46
|
+
if (layer.shading && !cell.properties.shading) cell.properties.shading = layer.shading;
|
|
47
|
+
if (layer.cellMargins && !cell.properties.margins) cell.properties.margins = layer.cellMargins;
|
|
48
|
+
if (layer.borders) {
|
|
49
|
+
const own = cell.properties.borders ?? {};
|
|
50
|
+
const merged = {
|
|
51
|
+
...layer.borders,
|
|
52
|
+
...definedOnly(own)
|
|
53
|
+
};
|
|
54
|
+
cell.properties.borders = merged;
|
|
55
|
+
}
|
|
56
|
+
if (layer.runProperties || layer.paragraphProperties) for (const el of cell.content) {
|
|
57
|
+
if (el.kind !== "paragraph") continue;
|
|
58
|
+
const p = el.paragraph;
|
|
59
|
+
if (layer.paragraphProperties) p.properties = {
|
|
60
|
+
...layer.paragraphProperties,
|
|
61
|
+
...definedOnly(p.properties)
|
|
62
|
+
};
|
|
63
|
+
if (layer.runProperties) for (const run of p.runs) run.properties = {
|
|
64
|
+
...layer.runProperties,
|
|
65
|
+
...definedOnly(run.properties)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function bandIndex(index, skipFirst, bandSize) {
|
|
70
|
+
const adjusted = index - (skipFirst ? 1 : 0);
|
|
71
|
+
if (adjusted < 0) return -1;
|
|
72
|
+
return Math.floor(adjusted / Math.max(1, bandSize));
|
|
73
|
+
}
|
|
74
|
+
function cellLayer(folded, look, pos) {
|
|
75
|
+
const layers = [folded.base];
|
|
76
|
+
const cond = (t) => folded.conditions.get(t);
|
|
77
|
+
if (!pos.noVBand && pos.colBand >= 0) layers.push(pos.colBand % 2 === 0 ? cond("band1Vert") : cond("band2Vert"));
|
|
78
|
+
if (!pos.noHBand && pos.rowBand >= 0) layers.push(pos.rowBand % 2 === 0 ? cond("band1Horz") : cond("band2Horz"));
|
|
79
|
+
if (look.firstColumn && pos.firstCol) layers.push(cond("firstCol"));
|
|
80
|
+
if (look.lastColumn && pos.lastCol) layers.push(cond("lastCol"));
|
|
81
|
+
if (look.firstRow && pos.firstRow) layers.push(cond("firstRow"));
|
|
82
|
+
if (look.lastRow && pos.lastRow) layers.push(cond("lastRow"));
|
|
83
|
+
if (look.firstRow && look.firstColumn && pos.firstRow && pos.firstCol) layers.push(cond("nwCell"));
|
|
84
|
+
if (look.firstRow && look.lastColumn && pos.firstRow && pos.lastCol) layers.push(cond("neCell"));
|
|
85
|
+
if (look.lastRow && look.firstColumn && pos.lastRow && pos.firstCol) layers.push(cond("swCell"));
|
|
86
|
+
if (look.lastRow && look.lastColumn && pos.lastRow && pos.lastCol) layers.push(cond("seCell"));
|
|
87
|
+
let out;
|
|
88
|
+
for (const layer of layers) {
|
|
89
|
+
if (!layer) continue;
|
|
90
|
+
out = out ? mergeLayer(out, layer) : layer;
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
function foldTableStyleChain(styleId, sheet) {
|
|
95
|
+
const chain = [];
|
|
96
|
+
const visited = /* @__PURE__ */ new Set();
|
|
97
|
+
let cursor = styleId;
|
|
98
|
+
while (cursor && !visited.has(cursor)) {
|
|
99
|
+
visited.add(cursor);
|
|
100
|
+
const style = sheet.styles.get(cursor);
|
|
101
|
+
if (!style || style.type !== "table") break;
|
|
102
|
+
chain.unshift(style);
|
|
103
|
+
cursor = style.basedOn;
|
|
104
|
+
}
|
|
105
|
+
if (chain.length === 0) return void 0;
|
|
106
|
+
let base = {};
|
|
107
|
+
const conditions = /* @__PURE__ */ new Map();
|
|
108
|
+
let rowBandSize = 1;
|
|
109
|
+
let colBandSize = 1;
|
|
110
|
+
for (const style of chain) {
|
|
111
|
+
const own = {
|
|
112
|
+
...style.tableLayer ?? {},
|
|
113
|
+
...Object.keys(style.runProperties).length > 0 && !style.tableLayer?.runProperties ? { runProperties: style.runProperties } : {},
|
|
114
|
+
...Object.keys(style.paragraphProperties).length > 0 && !style.tableLayer?.paragraphProperties ? { paragraphProperties: style.paragraphProperties } : {}
|
|
115
|
+
};
|
|
116
|
+
base = mergeLayer(base, own);
|
|
117
|
+
for (const c of style.tableConditions ?? []) {
|
|
118
|
+
const prev = conditions.get(c.type);
|
|
119
|
+
conditions.set(c.type, prev ? mergeLayer(prev, c.layer) : c.layer);
|
|
120
|
+
}
|
|
121
|
+
if (style.rowBandSize !== void 0) rowBandSize = style.rowBandSize;
|
|
122
|
+
if (style.colBandSize !== void 0) colBandSize = style.colBandSize;
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
base,
|
|
126
|
+
conditions,
|
|
127
|
+
rowBandSize,
|
|
128
|
+
colBandSize
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function mergeLayer(base, override) {
|
|
132
|
+
return {
|
|
133
|
+
...override.borders ?? base.borders ? { borders: {
|
|
134
|
+
...base.borders,
|
|
135
|
+
...definedOnly(override.borders ?? {})
|
|
136
|
+
} } : {},
|
|
137
|
+
...override.cellMargins ?? base.cellMargins ? { cellMargins: {
|
|
138
|
+
...base.cellMargins,
|
|
139
|
+
...definedOnly(override.cellMargins ?? {})
|
|
140
|
+
} } : {},
|
|
141
|
+
...override.shading ?? base.shading ? { shading: override.shading ?? base.shading } : {},
|
|
142
|
+
...override.runProperties ?? base.runProperties ? { runProperties: {
|
|
143
|
+
...base.runProperties,
|
|
144
|
+
...definedOnly(override.runProperties ?? {})
|
|
145
|
+
} } : {},
|
|
146
|
+
...override.paragraphProperties ?? base.paragraphProperties ? { paragraphProperties: {
|
|
147
|
+
...base.paragraphProperties,
|
|
148
|
+
...definedOnly(override.paragraphProperties ?? {})
|
|
149
|
+
} } : {}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function definedOnly(obj) {
|
|
153
|
+
const out = {};
|
|
154
|
+
for (const key of Object.keys(obj)) {
|
|
155
|
+
const v = obj[key];
|
|
156
|
+
if (v !== void 0) out[key] = v;
|
|
157
|
+
}
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
export { resolveTableStyles };
|