reamkit 1.10.0 → 1.12.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 +3 -3
- package/dist/esm/core/converter/facade.js +1 -1
- package/dist/esm/core/converter/project.js +1 -0
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +23 -0
- package/dist/esm/core/font/measure.d.ts +1 -1
- package/dist/esm/core/font/measure.js +5 -3
- package/dist/esm/core/font/ttf-parser.d.ts +9 -0
- package/dist/esm/core/font/ttf-parser.js +27 -2
- package/dist/esm/core/fonts/provider.d.ts +2 -1
- package/dist/esm/core/fonts/provider.js +2 -1
- package/dist/esm/core/fonts/remote-fonts.d.ts +1 -1
- package/dist/esm/core/fonts/remote-fonts.js +32 -11
- 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 -1
- package/dist/esm/core/line-breaker/greedy.d.ts +2 -0
- package/dist/esm/core/line-breaker/greedy.js +67 -0
- package/dist/esm/core/line-breaker/index.d.ts +1 -0
- package/dist/esm/core/line-breaker/knuth-plass.d.ts +1 -0
- package/dist/esm/core/line-breaker/knuth-plass.js +3 -3
- package/dist/esm/core/po-helpers.d.ts +2 -0
- package/dist/esm/core/po-helpers.js +14 -1
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +16 -0
- package/dist/esm/excel/pivot-table-parser.d.ts +2 -0
- package/dist/esm/excel/pivot-table-parser.js +68 -0
- package/dist/esm/excel/print-model.js +28 -6
- package/dist/esm/excel/xlsx-reader.js +33 -2
- package/dist/esm/excel/xlsx-to-pdf.js +1 -1
- package/dist/esm/html/html-writer.js +52 -2
- package/dist/esm/layout/page-doc.d.ts +3 -0
- package/dist/esm/layout/styled-layout.d.ts +10 -1
- package/dist/esm/layout/styled-layout.js +156 -15
- package/dist/esm/pdf/styled-page-emitter.js +38 -0
- package/dist/esm/pptx/pptx-reader.d.ts +2 -0
- package/dist/esm/pptx/pptx-reader.js +30 -3
- package/dist/esm/pptx/slide-parser.d.ts +8 -2
- package/dist/esm/pptx/slide-parser.js +80 -12
- package/dist/esm/word/document-parser.d.ts +17 -2
- package/dist/esm/word/document-parser.js +178 -18
- package/dist/esm/word/docx-reader.js +54 -4
- package/dist/esm/word/docx-to-pdf.js +2 -2
- package/dist/esm/word/docx-writer.js +84 -1
- package/dist/esm/word/drawing-parser.d.ts +7 -0
- package/dist/esm/word/drawing-parser.js +13 -0
- package/dist/esm/word/index.d.ts +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,9 +20,9 @@ Runtime dependencies are minimal: `fflate` (ZIP/Deflate) and `fast-xml-parser`.
|
|
|
20
20
|
|
|
21
21
|
Parse once into the format-neutral interlayer, convert to any target. The
|
|
22
22
|
format (docx/xlsx) is sniffed from the bytes; no fonts to wire up — an open
|
|
23
|
-
substitute font (
|
|
24
|
-
|
|
25
|
-
document's referenced fonts:
|
|
23
|
+
metric-compatible substitute font (Arimo for sans, Tinos for serif, Cousine for
|
|
24
|
+
monospace, plus Carlito/Caladea for Calibri/Cambria — the same families LibreOffice
|
|
25
|
+
substitutes) is fetched automatically based on the document's referenced fonts:
|
|
26
26
|
|
|
27
27
|
```ts
|
|
28
28
|
import { Ream } from 'reamkit';
|
|
@@ -2,6 +2,7 @@ import { FontRegistry } from "../font/font-registry.js";
|
|
|
2
2
|
import { FEATURES } from "../ir/features.js";
|
|
3
3
|
import { ConversionLossError } from "../ir/loss.js";
|
|
4
4
|
import { layoutStyledDocument } from "../../layout/styled-layout.js";
|
|
5
|
+
import { pptxReader } from "../../pptx/pptx-reader.js";
|
|
5
6
|
import { docxReader } from "../../word/docx-reader.js";
|
|
6
7
|
import { projectSheetDoc } from "../../excel/sheet-to-flow.js";
|
|
7
8
|
import { xlsxReader } from "../../excel/xlsx-reader.js";
|
|
@@ -13,7 +14,6 @@ import { writeHtml } from "../../html/html-writer.js";
|
|
|
13
14
|
import { writeSvg } from "../../svg/svg-writer.js";
|
|
14
15
|
import { convertDocxToPdf } from "../../word/docx-to-pdf.js";
|
|
15
16
|
import { convertXlsxToPdf } from "../../excel/xlsx-to-pdf.js";
|
|
16
|
-
import { pptxReader } from "../../pptx/pptx-reader.js";
|
|
17
17
|
import { pdfReader } from "../../pdf-reader/reader.js";
|
|
18
18
|
//#region src/core/converter/facade.ts
|
|
19
19
|
function toFlowDoc(doc) {
|
|
@@ -9,6 +9,7 @@ function flowRenderOptions(flow) {
|
|
|
9
9
|
...flow.headersFooters ? { headersFooters: flow.headersFooters } : {},
|
|
10
10
|
...flow.footnotes ? { footnotes: flow.footnotes } : {},
|
|
11
11
|
...flow.endnotes ? { endnotes: flow.endnotes } : {},
|
|
12
|
+
...flow.comments ? { comments: flow.comments } : {},
|
|
12
13
|
resources: flow.resources,
|
|
13
14
|
...flow.charts ? { charts: flow.charts } : {},
|
|
14
15
|
...flow.embeddedFonts ? { embeddedFonts: flow.embeddedFonts } : {},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { AbstractNumbering, Alignment, Border, BorderStyle, BodyElement, CellBorders, CellMerge, DocumentInfo, CellMargins, CellProperties, CellShading, CellDataBar, CellIcon, CellIconShape, CellSparkline, 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';
|
|
1
|
+
export type { AbstractNumbering, Alignment, Border, BorderStyle, BodyElement, Comment, CellBorders, CellMerge, DocumentInfo, CellMargins, CellProperties, CellShading, CellDataBar, CellIcon, CellIconShape, CellSparkline, 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';
|
|
@@ -132,6 +132,8 @@ export interface Run {
|
|
|
132
132
|
readonly anchor?: string;
|
|
133
133
|
readonly footnoteRef?: string;
|
|
134
134
|
readonly endnoteRef?: string;
|
|
135
|
+
readonly commentRef?: string;
|
|
136
|
+
readonly commentRangeRefs?: ReadonlyArray<string>;
|
|
135
137
|
readonly noteNumber?: true;
|
|
136
138
|
readonly listMarker?: true;
|
|
137
139
|
readonly inlineImage?: InlineImage;
|
|
@@ -144,6 +146,27 @@ export interface Paragraph {
|
|
|
144
146
|
readonly runs: ReadonlyArray<Run>;
|
|
145
147
|
readonly bookmarks?: ReadonlyArray<string>;
|
|
146
148
|
}
|
|
149
|
+
export interface Comment {
|
|
150
|
+
readonly content: ReadonlyArray<BodyElement>;
|
|
151
|
+
readonly author?: string;
|
|
152
|
+
readonly initials?: string;
|
|
153
|
+
/** Raw w:date timestamp (ISO 8601), as authored — not reformatted. */
|
|
154
|
+
readonly date?: string;
|
|
155
|
+
/**
|
|
156
|
+
* The author's resolved identity (usually an email) from word/people.xml
|
|
157
|
+
* (`w15:person` → `w15:presenceInfo/@w15:userId`), matched on the author name.
|
|
158
|
+
* Absent when the file ships no people part or the author is not listed.
|
|
159
|
+
*/
|
|
160
|
+
readonly authorId?: string;
|
|
161
|
+
/**
|
|
162
|
+
* Microsoft commentsExtended (w15) — the id of the comment this one replies
|
|
163
|
+
* to, forming a thread. Set only when word/commentsExtended.xml links this
|
|
164
|
+
* comment's paragraph to a parent (w15:paraIdParent). Top-level comments omit it.
|
|
165
|
+
*/
|
|
166
|
+
readonly parentId?: string;
|
|
167
|
+
/** w15:done — the comment thread was marked resolved. */
|
|
168
|
+
readonly done?: boolean;
|
|
169
|
+
}
|
|
147
170
|
export type NumberingFormat = 'decimal' | 'lowerLetter' | 'upperLetter' | 'lowerRoman' | 'upperRoman' | 'bullet' | 'none';
|
|
148
171
|
export interface NumberingReference {
|
|
149
172
|
readonly numId: string;
|
|
@@ -4,4 +4,4 @@ export interface FontMeasure {
|
|
|
4
4
|
readonly textWidthPt: (text: string, fontSize: number) => number;
|
|
5
5
|
readonly encodeTextAsCidHex: (text: string) => string;
|
|
6
6
|
}
|
|
7
|
-
export declare function createFontMeasure(parsed: ParsedTtf): FontMeasure;
|
|
7
|
+
export declare function createFontMeasure(parsed: ParsedTtf, kern?: boolean): FontMeasure;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { shapeText } from "./opentype-layout.js";
|
|
2
2
|
//#region src/core/font/measure.ts
|
|
3
|
-
|
|
3
|
+
var EMPTY_KERNING = /* @__PURE__ */ new Map();
|
|
4
|
+
function createFontMeasure(parsed, kern = true) {
|
|
4
5
|
const scale = 1e3 / parsed.unitsPerEm;
|
|
6
|
+
const kerning = kern ? parsed.kerning : EMPTY_KERNING;
|
|
5
7
|
const widths = new Array(parsed.numGlyphs);
|
|
6
8
|
for (let i = 0; i < parsed.numGlyphs; i++) widths[i] = Math.round((parsed.advanceWidths[i] ?? 0) * scale);
|
|
7
9
|
const pdfWidthForGid = (gid) => {
|
|
@@ -9,13 +11,13 @@ function createFontMeasure(parsed) {
|
|
|
9
11
|
return widths[gid];
|
|
10
12
|
};
|
|
11
13
|
const textWidthPt = (text, fontSize) => {
|
|
12
|
-
const shaped = shapeText(text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures,
|
|
14
|
+
const shaped = shapeText(text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, kerning, parsed.joiningForms);
|
|
13
15
|
let totalEm = 0;
|
|
14
16
|
for (const a of shaped.advances) totalEm += a;
|
|
15
17
|
return totalEm * fontSize / parsed.unitsPerEm;
|
|
16
18
|
};
|
|
17
19
|
const encodeTextAsCidHex = (text) => {
|
|
18
|
-
const shaped = shapeText(text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures,
|
|
20
|
+
const shaped = shapeText(text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, kerning, parsed.joiningForms);
|
|
19
21
|
let out = "";
|
|
20
22
|
for (const gid of shaped.gids) out += gid.toString(16).padStart(4, "0").toUpperCase();
|
|
21
23
|
return out;
|
|
@@ -12,8 +12,17 @@ export interface ParsedTtf {
|
|
|
12
12
|
readonly fontBBox: readonly [number, number, number, number];
|
|
13
13
|
readonly ascender: number;
|
|
14
14
|
readonly descender: number;
|
|
15
|
+
readonly lineGap: number;
|
|
15
16
|
readonly capHeight: number;
|
|
16
17
|
readonly xHeight: number;
|
|
18
|
+
readonly vmetrics: {
|
|
19
|
+
readonly typoAscent: number;
|
|
20
|
+
readonly typoDescent: number;
|
|
21
|
+
readonly typoLineGap: number;
|
|
22
|
+
readonly winAscent: number;
|
|
23
|
+
readonly winDescent: number;
|
|
24
|
+
readonly useTypoMetrics: boolean;
|
|
25
|
+
};
|
|
17
26
|
readonly italicAngle: number;
|
|
18
27
|
readonly stemV: number;
|
|
19
28
|
readonly flags: number;
|
|
@@ -41,7 +41,7 @@ function parseTtf(raw) {
|
|
|
41
41
|
r.seek(hhea.offset + 4);
|
|
42
42
|
const ascender = r.i16();
|
|
43
43
|
const descender = r.i16();
|
|
44
|
-
r.
|
|
44
|
+
const lineGap = r.i16();
|
|
45
45
|
r.skip(24);
|
|
46
46
|
const numberOfHMetrics = r.u16();
|
|
47
47
|
const maxp = require("maxp");
|
|
@@ -74,6 +74,12 @@ function parseTtf(raw) {
|
|
|
74
74
|
let xHeight = 0;
|
|
75
75
|
let isItalic = false;
|
|
76
76
|
let usWeightClass = 400;
|
|
77
|
+
let typoAscent = ascender;
|
|
78
|
+
let typoDescent = descender;
|
|
79
|
+
let typoLineGap = lineGap;
|
|
80
|
+
let winAscent = Math.abs(ascender);
|
|
81
|
+
let winDescent = Math.abs(descender);
|
|
82
|
+
let useTypoMetrics = false;
|
|
77
83
|
const os2 = tables.get("OS/2");
|
|
78
84
|
if (os2) {
|
|
79
85
|
r.seek(os2.offset);
|
|
@@ -81,7 +87,17 @@ function parseTtf(raw) {
|
|
|
81
87
|
r.skip(2);
|
|
82
88
|
usWeightClass = r.u16();
|
|
83
89
|
r.seek(os2.offset + 62);
|
|
84
|
-
|
|
90
|
+
const fsSelection = r.u16();
|
|
91
|
+
isItalic = (fsSelection & 1) !== 0;
|
|
92
|
+
useTypoMetrics = (fsSelection & 128) !== 0;
|
|
93
|
+
if (os2.length >= 78) {
|
|
94
|
+
r.seek(os2.offset + 68);
|
|
95
|
+
typoAscent = r.i16();
|
|
96
|
+
typoDescent = r.i16();
|
|
97
|
+
typoLineGap = r.i16();
|
|
98
|
+
winAscent = r.u16();
|
|
99
|
+
winDescent = r.u16();
|
|
100
|
+
}
|
|
85
101
|
if (os2Version >= 2 && os2.length >= 90) {
|
|
86
102
|
r.seek(os2.offset + 86);
|
|
87
103
|
xHeight = r.i16();
|
|
@@ -113,8 +129,17 @@ function parseTtf(raw) {
|
|
|
113
129
|
],
|
|
114
130
|
ascender,
|
|
115
131
|
descender,
|
|
132
|
+
lineGap,
|
|
116
133
|
capHeight,
|
|
117
134
|
xHeight,
|
|
135
|
+
vmetrics: {
|
|
136
|
+
typoAscent,
|
|
137
|
+
typoDescent,
|
|
138
|
+
typoLineGap,
|
|
139
|
+
winAscent,
|
|
140
|
+
winDescent,
|
|
141
|
+
useTypoMetrics
|
|
142
|
+
},
|
|
118
143
|
italicAngle,
|
|
119
144
|
stemV,
|
|
120
145
|
flags,
|
|
@@ -35,7 +35,8 @@ export declare function callerFontProvider(fonts: FontBytesByVariant): FontProvi
|
|
|
35
35
|
*/
|
|
36
36
|
export declare function embeddedDocFontProvider(embedded: ReadonlyMap<string, FontRegistry>): FontProvider;
|
|
37
37
|
/**
|
|
38
|
-
* Open CDN substitutes (
|
|
38
|
+
* Open CDN substitutes (Arimo / Tinos / Cousine / Carlito / Caladea — the
|
|
39
|
+
* LibreOffice metric-compatible mapping).
|
|
39
40
|
* Always answers; the chain reports it as a substitution.
|
|
40
41
|
*/
|
|
41
42
|
export declare function remoteFontProvider(options?: {
|
|
@@ -62,7 +62,8 @@ function embeddedDocFontProvider(embedded) {
|
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
|
-
* Open CDN substitutes (
|
|
65
|
+
* Open CDN substitutes (Arimo / Tinos / Cousine / Carlito / Caladea — the
|
|
66
|
+
* LibreOffice metric-compatible mapping).
|
|
66
67
|
* Always answers; the chain reports it as a substitution.
|
|
67
68
|
*/
|
|
68
69
|
function remoteFontProvider(options = {}) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FontBytesByVariant } from '../font/index.js';
|
|
2
|
-
export type FamilyKey = '
|
|
2
|
+
export type FamilyKey = 'arimo' | 'tinos' | 'cousine' | 'carlito' | 'caladea';
|
|
3
3
|
export declare function resolveFamilyKey(name: string | undefined): FamilyKey;
|
|
4
4
|
export type FetchLike = (url: string) => Promise<{
|
|
5
5
|
ok: boolean;
|
|
@@ -7,9 +7,9 @@ var VARIANT_SUFFIX = {
|
|
|
7
7
|
boldItalic: "700Bold_Italic"
|
|
8
8
|
};
|
|
9
9
|
var FAMILIES = {
|
|
10
|
-
|
|
11
|
-
pkg: "
|
|
12
|
-
file: "
|
|
10
|
+
arimo: {
|
|
11
|
+
pkg: "arimo",
|
|
12
|
+
file: "Arimo"
|
|
13
13
|
},
|
|
14
14
|
tinos: {
|
|
15
15
|
pkg: "tinos",
|
|
@@ -18,13 +18,32 @@ var FAMILIES = {
|
|
|
18
18
|
cousine: {
|
|
19
19
|
pkg: "cousine",
|
|
20
20
|
file: "Cousine"
|
|
21
|
+
},
|
|
22
|
+
carlito: {
|
|
23
|
+
pkg: "carlito",
|
|
24
|
+
file: "Carlito",
|
|
25
|
+
nested: true
|
|
26
|
+
},
|
|
27
|
+
caladea: {
|
|
28
|
+
pkg: "caladea",
|
|
29
|
+
file: "Caladea"
|
|
21
30
|
}
|
|
22
31
|
};
|
|
32
|
+
var EXACT = {
|
|
33
|
+
calibri: "carlito",
|
|
34
|
+
cambria: "caladea",
|
|
35
|
+
arial: "arimo",
|
|
36
|
+
helvetica: "arimo",
|
|
37
|
+
"liberation sans": "arimo",
|
|
38
|
+
"times new roman": "tinos",
|
|
39
|
+
times: "tinos",
|
|
40
|
+
"liberation serif": "tinos",
|
|
41
|
+
"courier new": "cousine",
|
|
42
|
+
courier: "cousine",
|
|
43
|
+
"liberation mono": "cousine"
|
|
44
|
+
};
|
|
23
45
|
var SERIF = new Set([
|
|
24
|
-
"times",
|
|
25
|
-
"times new roman",
|
|
26
46
|
"georgia",
|
|
27
|
-
"cambria",
|
|
28
47
|
"garamond",
|
|
29
48
|
"book antiqua",
|
|
30
49
|
"palatino",
|
|
@@ -33,8 +52,6 @@ var SERIF = new Set([
|
|
|
33
52
|
"serif"
|
|
34
53
|
]);
|
|
35
54
|
var MONO = new Set([
|
|
36
|
-
"courier",
|
|
37
|
-
"courier new",
|
|
38
55
|
"consolas",
|
|
39
56
|
"monaco",
|
|
40
57
|
"menlo",
|
|
@@ -43,14 +60,18 @@ var MONO = new Set([
|
|
|
43
60
|
"monospace"
|
|
44
61
|
]);
|
|
45
62
|
function resolveFamilyKey(name) {
|
|
46
|
-
if (!name) return "
|
|
63
|
+
if (!name) return "arimo";
|
|
47
64
|
const n = name.trim().toLowerCase();
|
|
65
|
+
const exact = EXACT[n];
|
|
66
|
+
if (exact) return exact;
|
|
48
67
|
if (MONO.has(n)) return "cousine";
|
|
49
68
|
if (SERIF.has(n)) return "tinos";
|
|
50
|
-
return "
|
|
69
|
+
return "arimo";
|
|
51
70
|
}
|
|
52
71
|
function fontUrl(family, variant) {
|
|
53
|
-
|
|
72
|
+
const suffix = VARIANT_SUFFIX[variant];
|
|
73
|
+
const leaf = `${family.file}_${suffix}.ttf`;
|
|
74
|
+
return family.nested ? `${CDN_BASE}/${family.pkg}/${suffix}/${leaf}` : `${CDN_BASE}/${family.pkg}/${leaf}`;
|
|
54
75
|
}
|
|
55
76
|
var cache = /* @__PURE__ */ new Map();
|
|
56
77
|
async function fetchTtf(url, fetchImpl, required) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BodyElement, Chart, DocumentInfo, Numbering, Section, SectionProperties, StyleSheet } from '../document-model/index.js';
|
|
1
|
+
import { BodyElement, Chart, Comment, DocumentInfo, Numbering, Section, SectionProperties, StyleSheet } from '../document-model/index.js';
|
|
2
2
|
import { FontRegistry } from '../font/index.js';
|
|
3
3
|
import { ResourceStore } from './resources.js';
|
|
4
4
|
export interface FlowDoc {
|
|
@@ -14,6 +14,8 @@ export interface FlowDoc {
|
|
|
14
14
|
/** §17.11 footnotes/endnotes content by id (separator stubs excluded). */
|
|
15
15
|
readonly footnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|
|
16
16
|
readonly endnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|
|
17
|
+
/** §17.13.4 review comments by id, anchored from a run's `commentRef`. */
|
|
18
|
+
readonly comments?: ReadonlyMap<string, Comment>;
|
|
17
19
|
/** Parsed charts keyed by relationship id (ChartBlock.chartRelId). */
|
|
18
20
|
readonly charts?: ReadonlyMap<string, Chart>;
|
|
19
21
|
/** Content-addressed binary resources (images). */
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { FORBIDDEN_BREAK } from "./knuth-plass.js";
|
|
2
|
+
//#region src/core/line-breaker/greedy.ts
|
|
3
|
+
function greedyBreakLines(items, lineWidths) {
|
|
4
|
+
if (items.length === 0) return [];
|
|
5
|
+
const widthFor = (line) => typeof lineWidths === "number" ? lineWidths : lineWidths[Math.min(line, lineWidths.length - 1)];
|
|
6
|
+
const widthBefore = new Array(items.length + 1);
|
|
7
|
+
widthBefore[0] = 0;
|
|
8
|
+
for (let i = 0; i < items.length; i++) {
|
|
9
|
+
const it = items[i];
|
|
10
|
+
widthBefore[i + 1] = widthBefore[i] + (it.type === "penalty" ? 0 : it.width);
|
|
11
|
+
}
|
|
12
|
+
const lineWidth = (start, b) => {
|
|
13
|
+
let w = widthBefore[b] - widthBefore[start];
|
|
14
|
+
const item = items[b];
|
|
15
|
+
if (item.type === "penalty") w += item.width;
|
|
16
|
+
return w;
|
|
17
|
+
};
|
|
18
|
+
const isFeasible = (i) => {
|
|
19
|
+
const item = items[i];
|
|
20
|
+
if (item.type === "penalty") return item.penalty < FORBIDDEN_BREAK;
|
|
21
|
+
if (item.type === "glue") {
|
|
22
|
+
const prev = items[i - 1];
|
|
23
|
+
return !!prev && prev.type === "box";
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
};
|
|
27
|
+
const breaks = [];
|
|
28
|
+
let lineStart = 0;
|
|
29
|
+
let lineNo = 0;
|
|
30
|
+
let lastGood = -1;
|
|
31
|
+
let i = 0;
|
|
32
|
+
while (i < items.length) {
|
|
33
|
+
if (!isFeasible(i)) {
|
|
34
|
+
i++;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const item = items[i];
|
|
38
|
+
const forced = item.type === "penalty" && item.penalty <= -1e4;
|
|
39
|
+
const fits = lineWidth(lineStart, i) <= widthFor(lineNo);
|
|
40
|
+
if (!fits && lastGood >= 0) {
|
|
41
|
+
breaks.push(lastGood);
|
|
42
|
+
lineStart = lastGood + 1;
|
|
43
|
+
lineNo++;
|
|
44
|
+
lastGood = -1;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (forced) {
|
|
48
|
+
breaks.push(i);
|
|
49
|
+
lineStart = i + 1;
|
|
50
|
+
lineNo++;
|
|
51
|
+
lastGood = -1;
|
|
52
|
+
i++;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (fits) lastGood = i;
|
|
56
|
+
else {
|
|
57
|
+
breaks.push(i);
|
|
58
|
+
lineStart = i + 1;
|
|
59
|
+
lineNo++;
|
|
60
|
+
lastGood = -1;
|
|
61
|
+
}
|
|
62
|
+
i++;
|
|
63
|
+
}
|
|
64
|
+
return breaks;
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
export { greedyBreakLines };
|
|
@@ -20,6 +20,7 @@ export interface BreakResult {
|
|
|
20
20
|
readonly ratios: ReadonlyArray<number>;
|
|
21
21
|
}
|
|
22
22
|
export declare const FORCED_BREAK = -10000;
|
|
23
|
+
export declare const FORBIDDEN_BREAK = 10000;
|
|
23
24
|
/**
|
|
24
25
|
* Break a paragraph into lines using the Knuth-Plass total-fit algorithm.
|
|
25
26
|
*
|
|
@@ -47,7 +47,7 @@ function breakLines(items, lineWidths) {
|
|
|
47
47
|
let active = [startNode];
|
|
48
48
|
const isFeasibleBreakpoint = (i) => {
|
|
49
49
|
const item = items[i];
|
|
50
|
-
if (item.type === "penalty" && item.penalty <
|
|
50
|
+
if (item.type === "penalty" && item.penalty < 1e4) return true;
|
|
51
51
|
if (item.type === "glue") {
|
|
52
52
|
const prev = items[i - 1];
|
|
53
53
|
return !!prev && prev.type === "box";
|
|
@@ -90,7 +90,7 @@ function breakLines(items, lineWidths) {
|
|
|
90
90
|
const lineDemerits = (r, penaltyAtBreak, lastItemPenalty) => {
|
|
91
91
|
const badness = 100 * Math.pow(Math.abs(r), 3);
|
|
92
92
|
let d;
|
|
93
|
-
if (penaltyAtBreak >= 0 && penaltyAtBreak <
|
|
93
|
+
if (penaltyAtBreak >= 0 && penaltyAtBreak < 1e4) d = Math.pow(LINE_PENALTY + badness + penaltyAtBreak, 2);
|
|
94
94
|
else if (penaltyAtBreak > -1e4 && penaltyAtBreak < 0) d = Math.pow(LINE_PENALTY + badness, 2) - penaltyAtBreak * penaltyAtBreak;
|
|
95
95
|
else d = Math.pow(LINE_PENALTY + badness, 2);
|
|
96
96
|
return d;
|
|
@@ -169,4 +169,4 @@ function breakLines(items, lineWidths) {
|
|
|
169
169
|
};
|
|
170
170
|
}
|
|
171
171
|
//#endregion
|
|
172
|
-
export { FORCED_BREAK, breakLines };
|
|
172
|
+
export { FORBIDDEN_BREAK, FORCED_BREAK, breakLines };
|
|
@@ -6,6 +6,8 @@ export declare function poChildren(node: PoNode | undefined): ReadonlyArray<PoNo
|
|
|
6
6
|
export declare function poChildrenWith(node: PoNode | undefined, tag: string): Array<PoNode>;
|
|
7
7
|
export declare function poFirstChild(node: PoNode | undefined, tag: string): PoNode | undefined;
|
|
8
8
|
export declare function poAttr(node: PoNode | undefined, name: string): string | undefined;
|
|
9
|
+
export declare function poAttrLocal(node: PoNode | undefined, name: string): string | undefined;
|
|
10
|
+
export declare function poIsLocal(node: PoNode | undefined, name: string): boolean;
|
|
9
11
|
export declare function poIntAttr(node: PoNode | undefined, name: string): number | undefined;
|
|
10
12
|
export declare function poVal(node: PoNode | undefined): string | undefined;
|
|
11
13
|
export declare function poToggle(node: PoNode | undefined): boolean | undefined;
|
|
@@ -28,6 +28,19 @@ function poAttr(node, name) {
|
|
|
28
28
|
const v = a[`@_w:${name}`] ?? a[`@_r:${name}`] ?? a[`@_m:${name}`] ?? a[`@_xml:${name}`] ?? a[`@_${name}`];
|
|
29
29
|
return typeof v === "string" ? v : void 0;
|
|
30
30
|
}
|
|
31
|
+
function poAttrLocal(node, name) {
|
|
32
|
+
if (!node || typeof node !== "object") return void 0;
|
|
33
|
+
const attrs = node[ATTRS_KEY];
|
|
34
|
+
if (!attrs || typeof attrs !== "object") return void 0;
|
|
35
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
36
|
+
if (!k.startsWith("@_")) continue;
|
|
37
|
+
if (k.slice(2).split(":").pop() === name && typeof v === "string") return v;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function poIsLocal(node, name) {
|
|
41
|
+
const tag = poTag(node);
|
|
42
|
+
return tag !== void 0 && (tag === name || tag.split(":").pop() === name);
|
|
43
|
+
}
|
|
31
44
|
function poIntAttr(node, name) {
|
|
32
45
|
const v = poAttr(node, name);
|
|
33
46
|
if (v === void 0) return void 0;
|
|
@@ -73,4 +86,4 @@ function poFindDescendant(node, tag) {
|
|
|
73
86
|
}
|
|
74
87
|
}
|
|
75
88
|
//#endregion
|
|
76
|
-
export { poAttr, poChildren, poChildrenWith, poFindByPath, poFindDescendant, poFirstChild, poIntAttr, poIs, poTag, poText, poToggle, poVal };
|
|
89
|
+
export { poAttr, poAttrLocal, poChildren, poChildrenWith, poFindByPath, poFindDescendant, poFirstChild, poIntAttr, poIs, poIsLocal, poTag, poText, poToggle, poVal };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { CellType, WorksheetCell, ColumnWidth, MergedRange, RowHeight, XlsxPageMargins, XlsxPageSetup, XlsxPrintOptions, ParsedWorksheet, XlsxFont, XlsxFill, XlsxBorderStyleName, XlsxBorderEdge, XlsxBorder, XlsxHorizontalAlign, XlsxVerticalAlign, XlsxCellAlignment, XlsxCellXf, XlsxStyles, DefinedName, Dxf, CfOperator, CfRuleCellIs, CfvoType, Cfvo, CfRuleColorScale, CfRuleDataBar, CfRuleIconSet, CfRule, ConditionalFormat, SparklineKind, ParsedSparkline, ExcelTable, SheetPane, } from './types.js';
|
|
1
|
+
export type { CellType, WorksheetCell, ColumnWidth, MergedRange, RowHeight, XlsxPageMargins, XlsxPageSetup, XlsxPrintOptions, ParsedWorksheet, XlsxFont, XlsxFill, XlsxBorderStyleName, XlsxBorderEdge, XlsxBorder, XlsxHorizontalAlign, XlsxVerticalAlign, XlsxCellAlignment, XlsxCellXf, XlsxStyles, DefinedName, Dxf, CfOperator, CfRuleCellIs, CfvoType, Cfvo, CfRuleColorScale, CfRuleDataBar, CfRuleIconSet, CfRule, ConditionalFormat, SparklineKind, ParsedSparkline, ExcelTable, PivotTable, SheetPane, } from './types.js';
|
|
@@ -61,6 +61,7 @@ export interface ParsedWorksheet {
|
|
|
61
61
|
readonly sparklines?: ReadonlyArray<ParsedSparkline>;
|
|
62
62
|
readonly tablePartRelIds?: ReadonlyArray<string>;
|
|
63
63
|
readonly tables?: ReadonlyArray<ExcelTable>;
|
|
64
|
+
readonly pivotTables?: ReadonlyArray<PivotTable>;
|
|
64
65
|
readonly pane?: SheetPane;
|
|
65
66
|
}
|
|
66
67
|
export interface SheetPane {
|
|
@@ -81,6 +82,21 @@ export interface ExcelTable {
|
|
|
81
82
|
readonly bandHex?: string;
|
|
82
83
|
readonly headerTextHex?: string;
|
|
83
84
|
}
|
|
85
|
+
export interface PivotTable {
|
|
86
|
+
readonly ref: MergedRange;
|
|
87
|
+
readonly name?: string;
|
|
88
|
+
readonly styleName?: string;
|
|
89
|
+
readonly firstHeaderRow: number;
|
|
90
|
+
readonly firstDataRow: number;
|
|
91
|
+
readonly firstDataCol: number;
|
|
92
|
+
readonly showRowStripes: boolean;
|
|
93
|
+
readonly showColStripes: boolean;
|
|
94
|
+
readonly rowItemTypes?: ReadonlyArray<string | undefined>;
|
|
95
|
+
readonly colItemTypes?: ReadonlyArray<string | undefined>;
|
|
96
|
+
readonly headerHex?: string;
|
|
97
|
+
readonly bandHex?: string;
|
|
98
|
+
readonly headerTextHex?: string;
|
|
99
|
+
}
|
|
84
100
|
export type SparklineKind = 'line' | 'column' | 'winLoss';
|
|
85
101
|
export interface ParsedSparkline {
|
|
86
102
|
readonly kind: SparklineKind;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { parseAreaRef } from "./defined-name-ref.js";
|
|
2
|
+
import { XMLParser } from "fast-xml-parser";
|
|
3
|
+
//#region src/excel/pivot-table-parser.ts
|
|
4
|
+
var decoder = new TextDecoder("utf-8");
|
|
5
|
+
var parser = new XMLParser({
|
|
6
|
+
ignoreAttributes: false,
|
|
7
|
+
attributeNamePrefix: "@_",
|
|
8
|
+
parseAttributeValue: false,
|
|
9
|
+
parseTagValue: false,
|
|
10
|
+
removeNSPrefix: true
|
|
11
|
+
});
|
|
12
|
+
function parsePivotTablePart(data) {
|
|
13
|
+
const def = asObj(parser.parse(decoder.decode(data))["pivotTableDefinition"]);
|
|
14
|
+
if (!def) return void 0;
|
|
15
|
+
const loc = asObj(def["location"]);
|
|
16
|
+
const refStr = loc ? strAttr(loc, "ref") : void 0;
|
|
17
|
+
const ref = refStr ? parseAreaRef(refStr) : void 0;
|
|
18
|
+
if (!ref) return void 0;
|
|
19
|
+
const si = asObj(def["pivotTableStyleInfo"]);
|
|
20
|
+
const name = strAttr(def, "name");
|
|
21
|
+
const styleName = si ? strAttr(si, "name") : void 0;
|
|
22
|
+
const rowItemTypes = itemTypes(asObj(def["rowItems"]));
|
|
23
|
+
const colItemTypes = itemTypes(asObj(def["colItems"]));
|
|
24
|
+
const hasRowTotals = rowItemTypes?.some((t) => t !== void 0) ?? false;
|
|
25
|
+
const hasColTotals = colItemTypes?.some((t) => t !== void 0) ?? false;
|
|
26
|
+
return {
|
|
27
|
+
ref,
|
|
28
|
+
...name ? { name } : {},
|
|
29
|
+
...styleName ? { styleName } : {},
|
|
30
|
+
firstHeaderRow: (loc ? numAttr(loc, "firstHeaderRow") : void 0) ?? 1,
|
|
31
|
+
firstDataRow: (loc ? numAttr(loc, "firstDataRow") : void 0) ?? 1,
|
|
32
|
+
firstDataCol: (loc ? numAttr(loc, "firstDataCol") : void 0) ?? 0,
|
|
33
|
+
showRowStripes: si ? boolAttr(si, "showRowStripes") : false,
|
|
34
|
+
showColStripes: si ? boolAttr(si, "showColStripes") : false,
|
|
35
|
+
...hasRowTotals && rowItemTypes ? { rowItemTypes } : {},
|
|
36
|
+
...hasColTotals && colItemTypes ? { colItemTypes } : {}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function itemTypes(node) {
|
|
40
|
+
if (!node) return void 0;
|
|
41
|
+
return toArray(node["i"]).map((i) => {
|
|
42
|
+
const o = asObj(i);
|
|
43
|
+
return o ? strAttr(o, "t") : void 0;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function toArray(v) {
|
|
47
|
+
if (v === void 0 || v === null) return [];
|
|
48
|
+
return Array.isArray(v) ? v : [v];
|
|
49
|
+
}
|
|
50
|
+
function asObj(v) {
|
|
51
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
|
|
52
|
+
}
|
|
53
|
+
function strAttr(obj, name) {
|
|
54
|
+
const v = obj[`@_${name}`];
|
|
55
|
+
return typeof v === "string" ? v : void 0;
|
|
56
|
+
}
|
|
57
|
+
function numAttr(obj, name) {
|
|
58
|
+
const v = strAttr(obj, name);
|
|
59
|
+
if (v === void 0) return void 0;
|
|
60
|
+
const n = Number(v);
|
|
61
|
+
return Number.isFinite(n) ? n : void 0;
|
|
62
|
+
}
|
|
63
|
+
function boolAttr(obj, name) {
|
|
64
|
+
const v = strAttr(obj, name);
|
|
65
|
+
return v === "1" || v === "true";
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
export { parsePivotTablePart };
|
|
@@ -525,18 +525,23 @@ function collectSeriesValues(cells, area) {
|
|
|
525
525
|
for (let r = area.startRow; r <= area.endRow; r++) for (let col = area.startColumn; col <= area.endColumn; col++) out.push(byKey.get(key(r, col)) ?? null);
|
|
526
526
|
return out;
|
|
527
527
|
}
|
|
528
|
+
function isPivotTotal(t) {
|
|
529
|
+
return t !== void 0 && t !== "data" && t !== "blank";
|
|
530
|
+
}
|
|
528
531
|
function buildTableFormatLookup(worksheet) {
|
|
529
532
|
const out = /* @__PURE__ */ new Map();
|
|
530
|
-
|
|
531
|
-
const
|
|
532
|
-
const firstDataRow = ref.startRow + t.headerRowCount;
|
|
533
|
+
const band = (ref, headerRows, style, isTotalRow) => {
|
|
534
|
+
const firstDataRow = ref.startRow + headerRows;
|
|
533
535
|
for (let r = ref.startRow; r <= ref.endRow; r++) {
|
|
534
536
|
let colorHex;
|
|
535
537
|
let fontColorHex;
|
|
536
538
|
if (r < firstDataRow) {
|
|
537
|
-
colorHex =
|
|
538
|
-
fontColorHex =
|
|
539
|
-
} else if (
|
|
539
|
+
colorHex = style.headerHex;
|
|
540
|
+
fontColorHex = style.headerTextHex;
|
|
541
|
+
} else if (isTotalRow?.(r - firstDataRow)) {
|
|
542
|
+
colorHex = style.headerHex;
|
|
543
|
+
fontColorHex = style.headerTextHex;
|
|
544
|
+
} else if (style.showRowStripes && style.bandHex) colorHex = (r - firstDataRow) % 2 === 1 ? style.bandHex : void 0;
|
|
540
545
|
if (!colorHex && !fontColorHex) continue;
|
|
541
546
|
const fmt = {
|
|
542
547
|
...colorHex ? { shading: { colorHex } } : {},
|
|
@@ -544,6 +549,23 @@ function buildTableFormatLookup(worksheet) {
|
|
|
544
549
|
};
|
|
545
550
|
for (let c = ref.startColumn; c <= ref.endColumn; c++) out.set(key(r, c), fmt);
|
|
546
551
|
}
|
|
552
|
+
};
|
|
553
|
+
for (const t of worksheet.tables ?? []) band(t.ref, t.headerRowCount, t);
|
|
554
|
+
for (const p of worksheet.pivotTables ?? []) band(p.ref, p.firstDataRow, p, (off) => isPivotTotal(p.rowItemTypes?.[off]));
|
|
555
|
+
for (const p of worksheet.pivotTables ?? []) {
|
|
556
|
+
if (!p.colItemTypes || p.headerHex === void 0) continue;
|
|
557
|
+
const firstDataRow = p.ref.startRow + p.firstDataRow;
|
|
558
|
+
const firstDataCol = p.ref.startColumn + p.firstDataCol;
|
|
559
|
+
const totalFmt = {
|
|
560
|
+
shading: { colorHex: p.headerHex },
|
|
561
|
+
...p.headerTextHex ? { fontColorHex: p.headerTextHex } : {}
|
|
562
|
+
};
|
|
563
|
+
for (let i = 0; i < p.colItemTypes.length; i++) {
|
|
564
|
+
if (!isPivotTotal(p.colItemTypes[i])) continue;
|
|
565
|
+
const c = firstDataCol + i;
|
|
566
|
+
if (c > p.ref.endColumn) continue;
|
|
567
|
+
for (let r = firstDataRow; r <= p.ref.endRow; r++) out.set(key(r, c), totalFmt);
|
|
568
|
+
}
|
|
547
569
|
}
|
|
548
570
|
return out;
|
|
549
571
|
}
|