reamkit 1.1.0 → 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.
Files changed (44) hide show
  1. package/README.md +9 -4
  2. package/dist/esm/{layout → core}/arc-to-bezier.d.ts +1 -1
  3. package/dist/esm/{layout → core}/arc-to-bezier.js +2 -2
  4. package/dist/esm/core/converter/project.js +2 -0
  5. package/dist/esm/core/document-model/index.d.ts +1 -1
  6. package/dist/esm/core/document-model/types.d.ts +58 -0
  7. package/dist/esm/{layout → core/drawingml}/chart-geometry.d.ts +1 -1
  8. package/dist/esm/{layout → core/drawingml}/preset-geometry.d.ts +2 -2
  9. package/dist/esm/{layout → core/drawingml}/preset-geometry.js +3 -3
  10. package/dist/esm/core/drawingml/shape-render.d.ts +8 -0
  11. package/dist/esm/core/drawingml/shape-render.js +71 -0
  12. package/dist/esm/core/ir/features.d.ts +1 -0
  13. package/dist/esm/core/ir/features.js +1 -0
  14. package/dist/esm/core/ir/flow.d.ts +3 -0
  15. package/dist/esm/core/links.d.ts +6 -0
  16. package/dist/esm/core/links.js +19 -0
  17. package/dist/esm/core/numbering/apply.js +3 -1
  18. package/dist/esm/core/style-cascade/index.d.ts +1 -0
  19. package/dist/esm/core/style-cascade/index.js +1 -0
  20. package/dist/esm/core/style-cascade/table.d.ts +2 -0
  21. package/dist/esm/core/style-cascade/table.js +161 -0
  22. package/dist/esm/core/vector.d.ts +2 -0
  23. package/dist/esm/core/vector.js +19 -1
  24. package/dist/esm/html/html-writer.js +188 -16
  25. package/dist/esm/layout/page-doc.d.ts +3 -0
  26. package/dist/esm/layout/styled-layout.d.ts +19 -3
  27. package/dist/esm/layout/styled-layout.js +533 -197
  28. package/dist/esm/pdf/struct-tree.d.ts +7 -1
  29. package/dist/esm/pdf/struct-tree.js +22 -6
  30. package/dist/esm/pdf/styled-page-emitter.d.ts +1 -1
  31. package/dist/esm/pdf/styled-page-emitter.js +161 -29
  32. package/dist/esm/pdf/xmp.d.ts +1 -0
  33. package/dist/esm/pdf/xmp.js +4 -0
  34. package/dist/esm/svg/svg-writer.js +2 -6
  35. package/dist/esm/word/document-parser.d.ts +3 -0
  36. package/dist/esm/word/document-parser.js +210 -23
  37. package/dist/esm/word/docx-reader.js +30 -4
  38. package/dist/esm/word/drawing-parser.d.ts +4 -1
  39. package/dist/esm/word/drawing-parser.js +53 -2
  40. package/dist/esm/word/index.d.ts +2 -2
  41. package/dist/esm/word/styles-parser.js +134 -1
  42. package/dist/esm/word/table-parser.js +35 -0
  43. package/package.json +1 -1
  44. /package/dist/esm/{layout → core/drawingml}/chart-geometry.js +0 -0
package/README.md CHANGED
@@ -85,7 +85,9 @@ are always used first — glyph-exact, no substitution.
85
85
  ### Archival PDF/A + embedded source
86
86
 
87
87
  The whole PDF/A family is supported (1a/1b, 2a/2b/2u, 3a/3b/3u —
88
- veraPDF-validated). PDF/A-3 can carry the source document inside the PDF:
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:
89
91
 
90
92
  ```ts
91
93
  const { bytes: pdfa, losses } = await doc.convertWithReport('pdf', {
@@ -157,13 +159,16 @@ automatically from the document's `docProps/core.xml`), `attachments`
157
159
 
158
160
  ## Scope
159
161
 
160
- Implemented: WordprocessingML text/styles/tables/lists/multi-section
161
- layout/headers-footers/images/tracked changes, SpreadsheetML grids,
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,
162
166
  number formats and the print model (gridlines, print area, fit-to-page,
163
167
  repeated titles, page breaks), DrawingML shapes and charts, OMML math,
164
168
  Type0+CIDFontType2 embedding with subsetting, Knuth-Plass line breaking,
165
169
  Liang hyphenation, OpenType ligatures/kerning + Arabic cursive joining,
166
- BiDi (UAX #9), tagged PDF, PDF/A-1/2/3 (a/b/u), digital signatures
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
167
172
  (PKCS#7/ECDSA/PAdES/RFC 3161), SVG page preview, flowed HTML export.
168
173
 
169
174
  See `handoff.md` for the full feature matrix and known limitations.
@@ -1,4 +1,4 @@
1
- import { PathSegment } from '../core/vector.js';
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 "../core/vector.js";
2
- //#region src/layout/arc-to-bezier.ts
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
  }
@@ -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 } : {},
@@ -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,4 +1,4 @@
1
- import { Chart, ChartSeries } from '../core/document-model/index.js';
1
+ import { Chart, ChartSeries } from '../document-model/index.js';
2
2
  export interface ChartRect {
3
3
  readonly x: number;
4
4
  readonly y: number;
@@ -1,5 +1,5 @@
1
- import { CustomGeometry } from '../core/document-model/index.js';
2
- import { PathSegment, VectorPath } from '../core/vector.js';
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 "../core/vector.js";
2
- import { arcToBeziers, ellipseSegments, roundRectSegments } from "./arc-to-bezier.js";
3
- //#region src/layout/preset-geometry.ts
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";
@@ -8,6 +8,7 @@ var FEATURES = {
8
8
  headersFooters: "headersFooters",
9
9
  images: "images",
10
10
  imagesJp2: "images.jp2",
11
+ hyperlinks: "hyperlinks",
11
12
  shapes: "shapes",
12
13
  charts: "charts",
13
14
  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
- properties: mergeIndentFromLevel(p.properties, level?.paragraphProperties),
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';
@@ -1,2 +1,3 @@
1
1
  import "./types.js";
2
2
  import "./resolver.js";
3
+ import "./table.js";
@@ -0,0 +1,2 @@
1
+ import { BodyElement, StyleSheet } from '../document-model/index.js';
2
+ export declare function resolveTableStyles(body: ReadonlyArray<BodyElement>, sheet: StyleSheet): ReadonlyArray<BodyElement>;
@@ -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 };
@@ -43,3 +43,5 @@ export declare class PathBuilder {
43
43
  close(): this;
44
44
  build(fillRule?: 'nonzero' | 'evenodd'): VectorPath;
45
45
  }
46
+ export declare function flipTransform(m: readonly [number, number, number, number, number, number], pageHeight: number): [number, number, number, number, number, number];
47
+ export declare function svgPathData(segments: ReadonlyArray<PathSegment>, fmt: (n: number) => string): string;
@@ -44,5 +44,23 @@ var PathBuilder = class {
44
44
  };
45
45
  }
46
46
  };
47
+ function flipTransform(m, pageHeight) {
48
+ return [
49
+ m[0],
50
+ -m[1],
51
+ m[2],
52
+ -m[3],
53
+ m[4],
54
+ pageHeight - m[5]
55
+ ];
56
+ }
57
+ function svgPathData(segments, fmt) {
58
+ const parts = [];
59
+ for (const s of segments) if (s.op === "move") parts.push(`M ${fmt(s.x)} ${fmt(s.y)}`);
60
+ else if (s.op === "line") parts.push(`L ${fmt(s.x)} ${fmt(s.y)}`);
61
+ else if (s.op === "cubic") parts.push(`C ${fmt(s.x1)} ${fmt(s.y1)} ${fmt(s.x2)} ${fmt(s.y2)} ${fmt(s.x)} ${fmt(s.y)}`);
62
+ else parts.push("Z");
63
+ return parts.join(" ");
64
+ }
47
65
  //#endregion
48
- export { PathBuilder };
66
+ export { PathBuilder, flipTransform, svgPathData };