reamkit 1.21.0 → 1.23.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/dist/esm/core/bytes.d.ts +22 -4
- package/dist/esm/core/bytes.js +73 -5
- package/dist/esm/core/converter/ream.d.ts +16 -0
- package/dist/esm/core/converter/ream.js +59 -9
- package/dist/esm/core/document-model/types.d.ts +78 -0
- package/dist/esm/core/drawingml/chart-geometry.js +11 -5
- package/dist/esm/core/drawingml/chart-parser.d.ts +2 -1
- package/dist/esm/core/drawingml/chart-parser.js +23 -1
- package/dist/esm/core/drawingml/colors.d.ts +30 -4
- package/dist/esm/core/drawingml/colors.js +45 -6
- package/dist/esm/core/drawingml/preset-geometry.js +236 -0
- package/dist/esm/core/drawingml/theme-parser.d.ts +38 -0
- package/dist/esm/core/drawingml/theme-parser.js +64 -1
- package/dist/esm/core/fonts/families.d.ts +10 -0
- package/dist/esm/core/fonts/families.js +44 -0
- package/dist/esm/core/fonts/index.d.ts +3 -2
- package/dist/esm/core/fonts/remote-fonts.d.ts +50 -0
- package/dist/esm/core/fonts/remote-fonts.js +114 -8
- package/dist/esm/core/fonts/scripts.d.ts +26 -0
- package/dist/esm/core/fonts/scripts.js +177 -0
- package/dist/esm/core/metafile/blit.d.ts +33 -0
- package/dist/esm/core/metafile/blit.js +67 -0
- package/dist/esm/core/metafile/dib.d.ts +61 -0
- package/dist/esm/core/metafile/dib.js +205 -0
- package/dist/esm/core/metafile/emf.js +139 -24
- package/dist/esm/core/metafile/picture.d.ts +12 -1
- package/dist/esm/core/metafile/symbol-fonts.d.ts +42 -0
- package/dist/esm/core/metafile/symbol-fonts.js +154 -0
- package/dist/esm/core/metafile/wmf.js +183 -10
- package/dist/esm/core/ole/escher-blip.d.ts +12 -0
- package/dist/esm/core/ole/escher-blip.js +88 -0
- package/dist/esm/{pdf-reader → core}/png-encode.js +1 -1
- package/dist/esm/core/vector.d.ts +5 -0
- package/dist/esm/excel/print-model.js +3 -1
- package/dist/esm/excel/sheet-shape-parser.js +1 -12
- package/dist/esm/excel/styles-parser.js +1 -1
- package/dist/esm/excel/xls/biff-reader.js +53 -18
- package/dist/esm/excel/xls/biff-styles.d.ts +5 -2
- package/dist/esm/excel/xls/biff-styles.js +9 -7
- package/dist/esm/excel/xls/escher.js +90 -24
- package/dist/esm/excel/xlsx-reader.d.ts +1 -1
- package/dist/esm/excel/xlsx-reader.js +3 -3
- package/dist/esm/layout/page-doc.d.ts +77 -0
- package/dist/esm/layout/page-doc.js +34 -18
- package/dist/esm/layout/styled-layout.d.ts +2 -2
- package/dist/esm/layout/styled-layout.js +467 -76
- package/dist/esm/pdf/shading.d.ts +19 -0
- package/dist/esm/pdf/shading.js +84 -11
- package/dist/esm/pdf/styled-page-emitter.js +647 -444
- package/dist/esm/pdf-reader/image-decode.js +1 -1
- package/dist/esm/pptx/embedded-fonts.d.ts +31 -0
- package/dist/esm/pptx/embedded-fonts.js +104 -0
- package/dist/esm/pptx/ole-preview.d.ts +10 -0
- package/dist/esm/pptx/ole-preview.js +52 -0
- package/dist/esm/pptx/placeholder-cascade.d.ts +136 -5
- package/dist/esm/pptx/placeholder-cascade.js +260 -27
- package/dist/esm/pptx/ppt/ppt-reader.js +120 -10
- package/dist/esm/pptx/ppt/ppt-text.d.ts +66 -1
- package/dist/esm/pptx/ppt/ppt-text.js +576 -137
- package/dist/esm/pptx/pptx-reader.d.ts +8 -0
- package/dist/esm/pptx/pptx-reader.js +271 -41
- package/dist/esm/pptx/preset-table-styles.d.ts +9 -0
- package/dist/esm/pptx/preset-table-styles.js +27 -0
- package/dist/esm/pptx/slide-parser.d.ts +78 -10
- package/dist/esm/pptx/slide-parser.js +396 -95
- package/dist/esm/pptx/sp-helpers.d.ts +2 -1
- package/dist/esm/pptx/sp-helpers.js +22 -6
- package/dist/esm/pptx/table-style.d.ts +64 -0
- package/dist/esm/pptx/table-style.js +203 -0
- package/dist/esm/svg/svg-writer.js +53 -5
- package/dist/esm/word/document-parser.d.ts +7 -1
- package/dist/esm/word/document-parser.js +6 -6
- package/dist/esm/word/docx-reader.js +8 -5
- package/dist/esm/word/docx-to-pdf.d.ts +8 -7
- package/dist/esm/word/docx-to-pdf.js +25 -7
- package/dist/esm/word/drawing-parser.d.ts +34 -0
- package/dist/esm/word/drawing-parser.js +173 -15
- package/dist/esm/word/numbering-parser.d.ts +2 -1
- package/dist/esm/word/numbering-parser.js +6 -6
- package/dist/esm/word/paragraph-properties.d.ts +2 -1
- package/dist/esm/word/paragraph-properties.js +2 -2
- package/dist/esm/word/run-properties.d.ts +5 -2
- package/dist/esm/word/run-properties.js +13 -8
- package/dist/esm/word/styles-parser.d.ts +2 -1
- package/dist/esm/word/styles-parser.js +12 -12
- package/dist/esm/word/table-parser.js +3 -3
- package/dist/esm/word/theme-fonts.d.ts +10 -0
- package/dist/esm/word/theme-fonts.js +28 -0
- package/package.json +1 -1
- /package/dist/esm/{pdf-reader → core}/png-encode.d.ts +0 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { unzlibSync } from "fflate";
|
|
2
|
+
//#region src/core/ole/escher-blip.ts
|
|
3
|
+
var BLIP_EMF = 61466;
|
|
4
|
+
var BLIP_WMF = 61467;
|
|
5
|
+
var BLIP_PICT = 61468;
|
|
6
|
+
var BLIP_JPEG = 61469;
|
|
7
|
+
var BLIP_PNG = 61470;
|
|
8
|
+
var BLIP_DIB = 61471;
|
|
9
|
+
var BLIP_TIFF = 61481;
|
|
10
|
+
var BLIP_JPEG_CMYK = 61482;
|
|
11
|
+
var METAFILE_HEADER_BYTES = 34;
|
|
12
|
+
var COMPRESSION_DEFLATE = 0;
|
|
13
|
+
var TWO_UID_INSTANCES = new Set([
|
|
14
|
+
981,
|
|
15
|
+
535,
|
|
16
|
+
1347,
|
|
17
|
+
1761,
|
|
18
|
+
1763,
|
|
19
|
+
1961,
|
|
20
|
+
1765,
|
|
21
|
+
481
|
|
22
|
+
]);
|
|
23
|
+
/** The signatures of the rasters a blip may hold, for the scan fallback. */
|
|
24
|
+
var RASTER_MAGICS = [
|
|
25
|
+
[
|
|
26
|
+
137,
|
|
27
|
+
80,
|
|
28
|
+
78,
|
|
29
|
+
71
|
|
30
|
+
],
|
|
31
|
+
[
|
|
32
|
+
255,
|
|
33
|
+
216,
|
|
34
|
+
255
|
|
35
|
+
],
|
|
36
|
+
[
|
|
37
|
+
71,
|
|
38
|
+
73,
|
|
39
|
+
70,
|
|
40
|
+
56
|
|
41
|
+
],
|
|
42
|
+
[66, 77]
|
|
43
|
+
];
|
|
44
|
+
/**
|
|
45
|
+
* The picture bytes inside one Escher BLIP record — a raster as it stands, or a
|
|
46
|
+
* metafile unpacked out of its header.
|
|
47
|
+
*
|
|
48
|
+
* @param type The blip record type (`0xF01A`…`0xF02A`).
|
|
49
|
+
* @param instance The record's `recInstance`, which says how many UIDs precede
|
|
50
|
+
* the payload.
|
|
51
|
+
* @param data The record's body, without the 8-byte header.
|
|
52
|
+
* @returns The bytes to hand the renderer, or `undefined` when the blip holds
|
|
53
|
+
* nothing this can read.
|
|
54
|
+
*/
|
|
55
|
+
function readEscherBlip(type, instance, data) {
|
|
56
|
+
if (type === BLIP_EMF || type === BLIP_WMF || type === BLIP_PICT) return readMetafileBlip(instance, data);
|
|
57
|
+
if (type === BLIP_JPEG || type === BLIP_PNG || type === BLIP_DIB || type === BLIP_TIFF || type === BLIP_JPEG_CMYK) return scanRaster(data);
|
|
58
|
+
return scanRaster(data);
|
|
59
|
+
}
|
|
60
|
+
function readMetafileBlip(instance, data) {
|
|
61
|
+
const uidBytes = TWO_UID_INSTANCES.has(instance) ? 32 : 16;
|
|
62
|
+
const head = uidBytes + METAFILE_HEADER_BYTES;
|
|
63
|
+
if (data.length < head) return void 0;
|
|
64
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
65
|
+
const uncompressed = view.getUint32(uidBytes, true);
|
|
66
|
+
const compressed = view.getUint32(uidBytes + 24, true);
|
|
67
|
+
const compression = data[uidBytes + 32];
|
|
68
|
+
const payload = data.subarray(head, Math.min(data.length, head + (compressed || Infinity)));
|
|
69
|
+
if (payload.length === 0) return void 0;
|
|
70
|
+
if (compression !== COMPRESSION_DEFLATE) return payload;
|
|
71
|
+
try {
|
|
72
|
+
const out = unzlibSync(payload, uncompressed > 0 ? { out: new Uint8Array(uncompressed) } : {});
|
|
73
|
+
return out.length > 0 ? out : void 0;
|
|
74
|
+
} catch {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function scanRaster(data) {
|
|
79
|
+
const limit = Math.min(data.length, 80);
|
|
80
|
+
for (let off = 0; off < limit; off++) for (const magic of RASTER_MAGICS) if (matches(data, off, magic)) return data.subarray(off);
|
|
81
|
+
}
|
|
82
|
+
function matches(d, off, magic) {
|
|
83
|
+
if (off + magic.length > d.length) return false;
|
|
84
|
+
for (let i = 0; i < magic.length; i++) if (d[off + i] !== magic[i]) return false;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
export { readEscherBlip };
|
|
@@ -49,6 +49,11 @@ export interface GradientStop {
|
|
|
49
49
|
readonly offset: number;
|
|
50
50
|
/** Stop colour as 6-hex, no leading `#`. */
|
|
51
51
|
readonly colorHex: string;
|
|
52
|
+
/**
|
|
53
|
+
* §20.1.2.3.1 `a:alpha` — how opaque THIS stop is, `0..1`; absent is opaque.
|
|
54
|
+
* The colour beside it is the stop's own, not composited over the paper.
|
|
55
|
+
*/
|
|
56
|
+
readonly alpha?: number;
|
|
52
57
|
}
|
|
53
58
|
/**
|
|
54
59
|
* A gradient fill — the format-agnostic vocabulary shared by the shape model
|
|
@@ -101,7 +101,8 @@ var DIGIT_EM = [
|
|
|
101
101
|
[/^(arial|helvetica|liberation sans|arimo|arial unicode ms)$/i, 1139 / 2048],
|
|
102
102
|
[/^(times new roman|liberation serif|tinos)$/i, 1024 / 2048],
|
|
103
103
|
[/^(tahoma)$/i, 1118 / 2048],
|
|
104
|
-
[/^(verdana|dejavu sans)$/i, 1303 / 2048]
|
|
104
|
+
[/^(verdana|dejavu sans)$/i, 1303 / 2048],
|
|
105
|
+
[/* @__PURE__ */ new RegExp("^(新細明體|細明體|pmingliu|mingliu|標楷體|dfkai-sb|宋体|新宋体|simsun|nsimsun|黑体|simhei|微软雅黑|microsoft yahei|微軟正黑體|microsoft jhenghei|MS [P]?明朝|MS [P]?ゴシック|ms [p]?mincho|ms [p]?gothic|meiryo|メイリオ|游ゴシック|yu gothic|굴림체?|gulim(che)?|돋움체?|dotum(che)?|바탕체?|batang(che)?|맑은 고딕|malgun gothic)$", "iu"), 1024 / 2048]
|
|
105
106
|
];
|
|
106
107
|
/**
|
|
107
108
|
* §18.3.1.13 — the Maximum Digit Width, in twips, of the normal style's font.
|
|
@@ -1021,6 +1022,7 @@ function runPropsFromXf(xf, styles) {
|
|
|
1021
1022
|
if (font.strike) props.strike = true;
|
|
1022
1023
|
if (font.sizePt !== void 0) props.fontSizePt = halfPtToPt(Math.round(font.sizePt * 2));
|
|
1023
1024
|
if (font.colorHex) props.colorHex = font.colorHex;
|
|
1025
|
+
if (font.name) props.fontFamily = { ascii: font.name };
|
|
1024
1026
|
return props;
|
|
1025
1027
|
}
|
|
1026
1028
|
function stackedVerticalContent(text, runProps, href) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { emuToPt, pt } from "../core/ir/units.js";
|
|
2
2
|
import { poAttr, poChildren, poFirstChild, poIntAttr, poIs, poTag, poText } from "../core/po-helpers.js";
|
|
3
|
-
import {
|
|
3
|
+
import { placeholderColors, resolveColorNode } from "../core/drawingml/colors.js";
|
|
4
4
|
import { parseFill, parseLine, parseShadow, parseXfrm, shadowFromOuterShdw } from "../word/drawing-parser.js";
|
|
5
5
|
import { parseGeometry, parseTxBody } from "../pptx/slide-parser.js";
|
|
6
6
|
import { parseXml } from "../pptx/pptx-reader.js";
|
|
@@ -387,17 +387,6 @@ function styleFill(sp, ns, colors, themeFillStyles) {
|
|
|
387
387
|
} : themed;
|
|
388
388
|
}
|
|
389
389
|
/**
|
|
390
|
-
* §20.1.4.2.10 — inside a theme's style list, `phClr` stands for "the colour the
|
|
391
|
-
* reference names". Every other colour resolves as it always did.
|
|
392
|
-
*
|
|
393
|
-
* @param colors The workbook's own resolver.
|
|
394
|
-
* @param phHex The colour the `a:fillRef` / `a:lnRef` carries.
|
|
395
|
-
* @returns A resolver that answers `phClr` with that colour, transforms and all.
|
|
396
|
-
*/
|
|
397
|
-
function placeholderColors(colors, phHex) {
|
|
398
|
-
return (raw) => "scheme" in raw && raw.scheme === "phClr" ? applyColorMods(phHex, raw.mods ?? []) : colors(raw);
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
390
|
* The shadow `<xdr:style><a:effectRef>` names, if it names one.
|
|
402
391
|
*
|
|
403
392
|
* §20.1.4.2.8, the same mechanism as the fill and the outline beside it: a
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { INDEXED_COLORS } from "../core/indexed-colors.js";
|
|
2
2
|
import { applyColorMods } from "../core/drawingml/colors.js";
|
|
3
|
-
import { resolveInternalEntities } from "../core/opc/xml-entities.js";
|
|
4
3
|
import { resolveAlternateContent } from "../core/opc/alternate-content.js";
|
|
4
|
+
import { resolveInternalEntities } from "../core/opc/xml-entities.js";
|
|
5
5
|
import { XMLParser } from "fast-xml-parser";
|
|
6
6
|
//#region src/excel/styles-parser.ts
|
|
7
7
|
var decoder = new TextDecoder("utf-8");
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { pt } from "../../core/ir/units.js";
|
|
2
2
|
import { ResourceStore } from "../../core/ir/resources.js";
|
|
3
|
+
import { makeColWidthPt, makeRowHeightPt } from "../sheet-drawing.js";
|
|
3
4
|
import { openCfb } from "../../core/ole/cfb.js";
|
|
4
5
|
import { parseBiffChart } from "./biff-chart.js";
|
|
5
6
|
import { buildBiffPalette, parseBiffStyles } from "./biff-styles.js";
|
|
@@ -333,7 +334,8 @@ function readSheet(wb, view, start, blips, resources, sharedStrings, chartData,
|
|
|
333
334
|
...pane ? { pane } : {}
|
|
334
335
|
};
|
|
335
336
|
const drawing = gatherDrawing(records, REC.MSODRAWING);
|
|
336
|
-
const
|
|
337
|
+
const place = anchorPlacer(grid);
|
|
338
|
+
const images = drawing.length > 0 && blips.length > 0 ? buildImages(drawing, blips, resources, place) : [];
|
|
337
339
|
const charts = [];
|
|
338
340
|
for (let i = 0; i < records.length; i++) {
|
|
339
341
|
if (records[i].type !== REC.BOF || readU16(records[i].data, 2) !== 32) continue;
|
|
@@ -361,7 +363,7 @@ function readSheet(wb, view, start, blips, resources, sharedStrings, chartData,
|
|
|
361
363
|
grid,
|
|
362
364
|
images,
|
|
363
365
|
charts,
|
|
364
|
-
shapes: drawing.length > 0 ? buildShapes(parseSheetShapes(drawing), gatherTxoTexts(records)) : [],
|
|
366
|
+
shapes: drawing.length > 0 ? buildShapes(parseSheetShapes(drawing), gatherTxoTexts(records), place) : [],
|
|
365
367
|
hyperlinks,
|
|
366
368
|
comments: parseComments(records)
|
|
367
369
|
};
|
|
@@ -1174,13 +1176,24 @@ function parseComments(records) {
|
|
|
1174
1176
|
}
|
|
1175
1177
|
return out;
|
|
1176
1178
|
}
|
|
1177
|
-
function buildShapes(escherShapes, txoTexts) {
|
|
1179
|
+
function buildShapes(escherShapes, txoTexts, place) {
|
|
1178
1180
|
const out = [];
|
|
1179
1181
|
let textIdx = 0;
|
|
1180
1182
|
for (const s of escherShapes) {
|
|
1181
1183
|
const text = s.hasText ? txoTexts[textIdx++] ?? "" : "";
|
|
1182
|
-
const { widthPt, heightPt } =
|
|
1184
|
+
const { xPt, yPt, widthPt, heightPt } = place(s.anchor);
|
|
1183
1185
|
out.push({
|
|
1186
|
+
float: {
|
|
1187
|
+
wrap: "none",
|
|
1188
|
+
posH: {
|
|
1189
|
+
relativeFrom: "margin",
|
|
1190
|
+
offsetPt: pt(xPt)
|
|
1191
|
+
},
|
|
1192
|
+
posV: {
|
|
1193
|
+
relativeFrom: "margin",
|
|
1194
|
+
offsetPt: pt(yPt)
|
|
1195
|
+
}
|
|
1196
|
+
},
|
|
1184
1197
|
width: pt(widthPt),
|
|
1185
1198
|
height: pt(heightPt),
|
|
1186
1199
|
geometry: {
|
|
@@ -1222,34 +1235,56 @@ function shapePreset(type) {
|
|
|
1222
1235
|
default: return "rect";
|
|
1223
1236
|
}
|
|
1224
1237
|
}
|
|
1225
|
-
function buildImages(drawing, blips, resources) {
|
|
1238
|
+
function buildImages(drawing, blips, resources, place) {
|
|
1226
1239
|
const out = [];
|
|
1227
1240
|
for (const pic of parseSheetPictures(drawing)) {
|
|
1228
1241
|
const bytes = blips[pic.blipIndex - 1];
|
|
1229
1242
|
if (!bytes) continue;
|
|
1230
|
-
const { widthPt, heightPt } =
|
|
1243
|
+
const { xPt, yPt, widthPt, heightPt } = place(pic.anchor);
|
|
1231
1244
|
out.push({
|
|
1232
1245
|
resourceId: resources.put(bytes),
|
|
1233
1246
|
widthPt,
|
|
1234
|
-
heightPt
|
|
1247
|
+
heightPt,
|
|
1248
|
+
xPt,
|
|
1249
|
+
yPt
|
|
1235
1250
|
});
|
|
1236
1251
|
}
|
|
1237
1252
|
return out;
|
|
1238
1253
|
}
|
|
1239
|
-
function
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1254
|
+
function anchorPlacer(grid) {
|
|
1255
|
+
const colPt = makeColWidthPt(grid);
|
|
1256
|
+
const rowPt = makeRowHeightPt(grid);
|
|
1257
|
+
const before = (track) => {
|
|
1258
|
+
const sums = [0];
|
|
1259
|
+
return (index) => {
|
|
1260
|
+
const n = Math.min(Math.max(Math.trunc(index), 0), MAX_ANCHOR_TRACKS);
|
|
1261
|
+
while (sums.length <= n) sums.push(sums[sums.length - 1] + track(sums.length - 1));
|
|
1262
|
+
return sums[n];
|
|
1263
|
+
};
|
|
1243
1264
|
};
|
|
1244
|
-
const
|
|
1245
|
-
const
|
|
1246
|
-
const
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1265
|
+
const beforeCol = before(colPt);
|
|
1266
|
+
const beforeRow = before(rowPt);
|
|
1267
|
+
const at = (index, off, start, track) => start(index) + off / 1024 * track(index);
|
|
1268
|
+
return (anchor) => {
|
|
1269
|
+
if (!anchor) return {
|
|
1270
|
+
xPt: 0,
|
|
1271
|
+
yPt: 0,
|
|
1272
|
+
widthPt: 96,
|
|
1273
|
+
heightPt: 72
|
|
1274
|
+
};
|
|
1275
|
+
const x1 = at(anchor.col1, anchor.dx1, beforeCol, colPt);
|
|
1276
|
+
const x2 = at(anchor.col2, anchor.dx2, beforeCol, colPt);
|
|
1277
|
+
const y1 = at(anchor.row1, anchor.dy1, beforeRow, rowPt);
|
|
1278
|
+
const y2 = at(anchor.row2, anchor.dy2, beforeRow, rowPt);
|
|
1279
|
+
return {
|
|
1280
|
+
xPt: x1,
|
|
1281
|
+
yPt: y1,
|
|
1282
|
+
widthPt: Math.max(1, x2 - x1),
|
|
1283
|
+
heightPt: Math.max(1, y2 - y1)
|
|
1284
|
+
};
|
|
1251
1285
|
};
|
|
1252
1286
|
}
|
|
1287
|
+
var MAX_ANCHOR_TRACKS = 65536;
|
|
1253
1288
|
function numCell(row, column, style, rawValue) {
|
|
1254
1289
|
return {
|
|
1255
1290
|
column,
|
|
@@ -4,8 +4,11 @@ interface BiffRec {
|
|
|
4
4
|
readonly data: Uint8Array;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
* Build a palette-index → `"
|
|
8
|
-
*
|
|
7
|
+
* Build a palette-index → `"RRGGBB"` resolver from the default 56-colour palette
|
|
8
|
+
* plus any `PALETTE` override. Six hex digits, not the eight an xlsx `rgb`
|
|
9
|
+
* attribute carries: by the time styles are parsed the alpha is gone, and a
|
|
10
|
+
* colour that kept it was read from its first six characters — silver `C0C0C0`
|
|
11
|
+
* reaching the page as the pink `FFC0C0`.
|
|
9
12
|
* `0x7FFF` / 64 / 65 are automatic or system colours → `undefined` (the renderer
|
|
10
13
|
* default). Exported so the conditional-format reader (XLS-13) resolves its dxf
|
|
11
14
|
* colours the same way.
|
|
@@ -116,8 +116,11 @@ var PALETTE56 = [
|
|
|
116
116
|
"333333"
|
|
117
117
|
];
|
|
118
118
|
/**
|
|
119
|
-
* Build a palette-index → `"
|
|
120
|
-
*
|
|
119
|
+
* Build a palette-index → `"RRGGBB"` resolver from the default 56-colour palette
|
|
120
|
+
* plus any `PALETTE` override. Six hex digits, not the eight an xlsx `rgb`
|
|
121
|
+
* attribute carries: by the time styles are parsed the alpha is gone, and a
|
|
122
|
+
* colour that kept it was read from its first six characters — silver `C0C0C0`
|
|
123
|
+
* reaching the page as the pink `FFC0C0`.
|
|
121
124
|
* `0x7FFF` / 64 / 65 are automatic or system colours → `undefined` (the renderer
|
|
122
125
|
* default). Exported so the conditional-format reader (XLS-13) resolves its dxf
|
|
123
126
|
* colours the same way.
|
|
@@ -130,8 +133,7 @@ function buildBiffPalette(records) {
|
|
|
130
133
|
for (const rec of records) if (rec.type === REC_PALETTE) applyPalette(rec.data, palette);
|
|
131
134
|
return (icv) => {
|
|
132
135
|
if (icv === 32767 || icv === 64 || icv === 65) return void 0;
|
|
133
|
-
|
|
134
|
-
return rgb ? `FF${rgb}` : void 0;
|
|
136
|
+
return palette[icv];
|
|
135
137
|
};
|
|
136
138
|
}
|
|
137
139
|
/**
|
|
@@ -252,11 +254,11 @@ function parseXf(d, color, internFill, internBorder) {
|
|
|
252
254
|
if (right) border.right = right;
|
|
253
255
|
if (top) border.top = top;
|
|
254
256
|
if (bottom) border.bottom = bottom;
|
|
255
|
-
const patternType = FILL_PATTERNS[brd3 >>
|
|
257
|
+
const patternType = FILL_PATTERNS[brd3 >> 10 & 63];
|
|
256
258
|
let fill;
|
|
257
259
|
if (patternType && patternType !== "none") {
|
|
258
|
-
const fg = color(brd3 & 127);
|
|
259
|
-
const bg = color(brd3 >>
|
|
260
|
+
const fg = color(brd3 >> 16 & 127);
|
|
261
|
+
const bg = color(brd3 >> 23 & 127);
|
|
260
262
|
fill = {
|
|
261
263
|
patternType,
|
|
262
264
|
...fg ? { fgColorHex: fg } : {},
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readEscherBlip } from "../../core/ole/escher-blip.js";
|
|
1
2
|
//#region src/excel/xls/escher.ts
|
|
2
3
|
var FBT_BSTORE_CONTAINER = 61441;
|
|
3
4
|
var FBT_BSE = 61447;
|
|
@@ -6,6 +7,7 @@ var FBT_OPT = 61451;
|
|
|
6
7
|
var FBT_CLIENT_ANCHOR = 61456;
|
|
7
8
|
var PROP_PIB = 260;
|
|
8
9
|
var MAX_DEPTH = 24;
|
|
10
|
+
var BSE_HEADER_BYTES = 36;
|
|
9
11
|
function* records(d) {
|
|
10
12
|
let off = 0;
|
|
11
13
|
while (off + 8 <= d.length) {
|
|
@@ -37,7 +39,11 @@ function parseBlipStore(msoDrawingGroup) {
|
|
|
37
39
|
const store = findContainer(msoDrawingGroup, FBT_BSTORE_CONTAINER, 0);
|
|
38
40
|
if (!store) return [];
|
|
39
41
|
const out = [];
|
|
40
|
-
for (const r of records(store))
|
|
42
|
+
for (const r of records(store)) {
|
|
43
|
+
if (r.type !== FBT_BSE) continue;
|
|
44
|
+
const nested = [...records(r.data.subarray(BSE_HEADER_BYTES))][0];
|
|
45
|
+
out.push(nested ? readEscherBlip(nested.type, nested.instance, nested.data) ?? findImageBytes(r.data) : findImageBytes(r.data));
|
|
46
|
+
}
|
|
41
47
|
return out;
|
|
42
48
|
}
|
|
43
49
|
function findContainer(d, type, depth) {
|
|
@@ -74,6 +80,9 @@ function collectPictures(d, out, depth) {
|
|
|
74
80
|
}
|
|
75
81
|
var FBT_SP = 61450;
|
|
76
82
|
var FBT_CLIENT_TEXTBOX = 61453;
|
|
83
|
+
var FBT_SPGR_CONTAINER = 61443;
|
|
84
|
+
var FBT_SPGR = 61449;
|
|
85
|
+
var FBT_CHILD_ANCHOR = 61455;
|
|
77
86
|
var PROP_FILL_COLOR = 385;
|
|
78
87
|
var PROP_LINE_COLOR = 448;
|
|
79
88
|
/**
|
|
@@ -86,30 +95,84 @@ function parseSheetShapes(msoDrawing) {
|
|
|
86
95
|
collectShapes(msoDrawing, out, 0);
|
|
87
96
|
return out;
|
|
88
97
|
}
|
|
89
|
-
function collectShapes(d, out, depth) {
|
|
98
|
+
function collectShapes(d, out, depth, group) {
|
|
90
99
|
if (depth > MAX_DEPTH) return;
|
|
91
|
-
for (const r of records(d))
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
fill
|
|
102
|
-
line
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
100
|
+
for (const r of records(d)) {
|
|
101
|
+
if (r.type === FBT_SPGR_CONTAINER) {
|
|
102
|
+
collectShapes(r.data, out, depth + 1, groupFrame(r.data) ?? group);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (r.type === FBT_SP_CONTAINER) {
|
|
106
|
+
let shapeType = 0;
|
|
107
|
+
let hasText = false;
|
|
108
|
+
let pib;
|
|
109
|
+
let anchor;
|
|
110
|
+
let fill;
|
|
111
|
+
let line;
|
|
112
|
+
for (const child of records(r.data)) if (child.type === FBT_SP) shapeType = child.instance;
|
|
113
|
+
else if (child.type === FBT_OPT) {
|
|
114
|
+
pib = optProperty(child.data, child.instance, PROP_PIB);
|
|
115
|
+
fill = optColor(child.data, child.instance, PROP_FILL_COLOR);
|
|
116
|
+
line = optColor(child.data, child.instance, PROP_LINE_COLOR);
|
|
117
|
+
} else if (child.type === FBT_CLIENT_ANCHOR) anchor = parseAnchor(child.data);
|
|
118
|
+
else if (child.type === FBT_CHILD_ANCHOR && group) anchor = childAnchor(child.data, group);
|
|
119
|
+
else if (child.type === FBT_CLIENT_TEXTBOX) hasText = true;
|
|
120
|
+
if (pib === void 0 && shapeType !== 0) out.push({
|
|
121
|
+
shapeType,
|
|
122
|
+
hasText,
|
|
123
|
+
...anchor ? { anchor } : {},
|
|
124
|
+
...fill ? { fillColorHex: fill } : {},
|
|
125
|
+
...line ? { lineColorHex: line } : {}
|
|
126
|
+
});
|
|
127
|
+
} else if (r.isContainer) collectShapes(r.data, out, depth + 1, group);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function groupFrame(spgrData) {
|
|
131
|
+
const sp = [...records(spgrData)].find((r) => r.type === FBT_SP_CONTAINER);
|
|
132
|
+
if (!sp) return void 0;
|
|
133
|
+
let box;
|
|
134
|
+
let anchor;
|
|
135
|
+
for (const child of records(sp.data)) if (child.type === FBT_SPGR && child.data.length >= 16) {
|
|
136
|
+
const x = i32(child.data, 0);
|
|
137
|
+
const y = i32(child.data, 4);
|
|
138
|
+
box = {
|
|
139
|
+
x,
|
|
140
|
+
y,
|
|
141
|
+
w: i32(child.data, 8) - x,
|
|
142
|
+
h: i32(child.data, 12) - y
|
|
143
|
+
};
|
|
144
|
+
} else if (child.type === FBT_CLIENT_ANCHOR) anchor = parseAnchor(child.data);
|
|
145
|
+
if (!box || !anchor || box.w <= 0 || box.h <= 0) return void 0;
|
|
146
|
+
return {
|
|
147
|
+
anchor,
|
|
148
|
+
...box
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function childAnchor(d, g) {
|
|
152
|
+
if (d.length < 16) return void 0;
|
|
153
|
+
const from = (v, lo, hi, min, size) => lo + (v - min) / size * (hi - lo);
|
|
154
|
+
const x1 = g.anchor.col1 + g.anchor.dx1 / 1024;
|
|
155
|
+
const x2 = g.anchor.col2 + g.anchor.dx2 / 1024;
|
|
156
|
+
const y1 = g.anchor.row1 + g.anchor.dy1 / 1024;
|
|
157
|
+
const y2 = g.anchor.row2 + g.anchor.dy2 / 1024;
|
|
158
|
+
const cell = (v) => {
|
|
159
|
+
const whole = Math.max(0, Math.min(65535, Math.floor(v)));
|
|
160
|
+
return [whole, Math.max(0, Math.min(1023, Math.round((v - whole) * 1024)))];
|
|
161
|
+
};
|
|
162
|
+
const [col1, dx1] = cell(from(i32(d, 0), x1, x2, g.x, g.w));
|
|
163
|
+
const [row1, dy1] = cell(from(i32(d, 4), y1, y2, g.y, g.h));
|
|
164
|
+
const [col2, dx2] = cell(from(i32(d, 8), x1, x2, g.x, g.w));
|
|
165
|
+
const [row2, dy2] = cell(from(i32(d, 12), y1, y2, g.y, g.h));
|
|
166
|
+
return {
|
|
167
|
+
col1,
|
|
168
|
+
dx1,
|
|
169
|
+
row1,
|
|
170
|
+
dy1,
|
|
171
|
+
col2,
|
|
172
|
+
dx2,
|
|
173
|
+
row2,
|
|
174
|
+
dy2
|
|
175
|
+
};
|
|
113
176
|
}
|
|
114
177
|
function optColor(d, count, wantId) {
|
|
115
178
|
const v = optProperty(d, count, wantId);
|
|
@@ -179,6 +242,9 @@ function matches(d, off, magic) {
|
|
|
179
242
|
for (let i = 0; i < magic.length; i++) if (d[off + i] !== magic[i]) return false;
|
|
180
243
|
return true;
|
|
181
244
|
}
|
|
245
|
+
function i32(d, off) {
|
|
246
|
+
return u32(d, off) | 0;
|
|
247
|
+
}
|
|
182
248
|
function u16(d, off) {
|
|
183
249
|
return (d[off] ?? 0) | (d[off + 1] ?? 0) << 8;
|
|
184
250
|
}
|
|
@@ -29,7 +29,7 @@ export declare function readXlsx(xlsx: Uint8Array, options?: ProjectSheetOptions
|
|
|
29
29
|
*/
|
|
30
30
|
export declare function readXlsxToSheetDoc(xlsx: Uint8Array): SheetDoc;
|
|
31
31
|
/**
|
|
32
|
-
* The OOXML `.xlsx` {@link DocumentReader}: sniffs the OPC ZIP for
|
|
32
|
+
* The OOXML `.xlsx` {@link DocumentReader}: sniffs the OPC ZIP's directory for
|
|
33
33
|
* `xl/workbook.xml` and reads it into a {@link SheetDoc}. The reader's native
|
|
34
34
|
* tree is the SheetDoc; the facade/Ream project it to a {@link FlowDoc} for
|
|
35
35
|
* rendering (E-SHEET SB1).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ResourceStore } from "../core/ir/resources.js";
|
|
2
2
|
import { FEATURES } from "../core/ir/features.js";
|
|
3
3
|
import { detectImageFormat } from "../core/images.js";
|
|
4
|
-
import { bytesInclude,
|
|
4
|
+
import { bytesInclude, packageHasPart } from "../core/bytes.js";
|
|
5
5
|
import { DEFAULT_THEME_PALETTE, makeColorResolver } from "../core/drawingml/colors.js";
|
|
6
6
|
import { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
|
|
7
7
|
import { parseTheme, parseThemeEffectStyles, parseThemeFillStyles, parseThemeLineWidths } from "../core/drawingml/theme-parser.js";
|
|
@@ -651,7 +651,7 @@ function lighten(hex, amount) {
|
|
|
651
651
|
return `${hx(ch(16))}${hx(ch(8))}${hx(ch(0))}`;
|
|
652
652
|
}
|
|
653
653
|
/**
|
|
654
|
-
* The OOXML `.xlsx` {@link DocumentReader}: sniffs the OPC ZIP for
|
|
654
|
+
* The OOXML `.xlsx` {@link DocumentReader}: sniffs the OPC ZIP's directory for
|
|
655
655
|
* `xl/workbook.xml` and reads it into a {@link SheetDoc}. The reader's native
|
|
656
656
|
* tree is the SheetDoc; the facade/Ream project it to a {@link FlowDoc} for
|
|
657
657
|
* rendering (E-SHEET SB1).
|
|
@@ -660,7 +660,7 @@ var xlsxReader = {
|
|
|
660
660
|
id: "xlsx",
|
|
661
661
|
produces: "sheet",
|
|
662
662
|
supports: new Set([FEATURES.text, FEATURES.tables]),
|
|
663
|
-
sniff: (bytes) => bytes[0] === 80 && bytes[1] === 75 &&
|
|
663
|
+
sniff: (bytes) => bytes[0] === 80 && bytes[1] === 75 && packageHasPart(bytes, "xl/workbook.xml"),
|
|
664
664
|
read: (bytes) => ({
|
|
665
665
|
doc: readXlsxToSheetDoc(bytes),
|
|
666
666
|
losses: []
|
|
@@ -17,6 +17,23 @@ export interface FontResource {
|
|
|
17
17
|
/** Glyphs collected by the usage walk — the emit phase subsets to these. */
|
|
18
18
|
readonly gids: ReadonlySet<number>;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* The face a run asked for and the registry could not give — what the page has
|
|
22
|
+
* to draw itself. A font set carries only `regular` by contract, so a caller
|
|
23
|
+
* who supplies one file still asks for bold headings and italic quotes.
|
|
24
|
+
*/
|
|
25
|
+
export interface SyntheticFace {
|
|
26
|
+
/** No bold cut: the glyphs are stroked as well as filled. */
|
|
27
|
+
readonly bold?: true;
|
|
28
|
+
/** No italic cut: the glyphs are sheared. */
|
|
29
|
+
readonly italic?: true;
|
|
30
|
+
/**
|
|
31
|
+
* No condensed cut: the glyphs are set at this fraction of their advance
|
|
32
|
+
* (`Arial Narrow` is 82 % of Arial). Layout measures through it too — the
|
|
33
|
+
* squeeze decides where the line breaks, not just how it looks.
|
|
34
|
+
*/
|
|
35
|
+
readonly widthScale?: number;
|
|
36
|
+
}
|
|
20
37
|
/** A run of text on a {@link Line}: the string plus its resolved font, size, width and link/tagging state. */
|
|
21
38
|
export interface TextToken {
|
|
22
39
|
readonly kind: 'text';
|
|
@@ -61,6 +78,8 @@ export interface TextToken {
|
|
|
61
78
|
readonly highlight?: true;
|
|
62
79
|
readonly resolvedRun: ResolvedRunProperties;
|
|
63
80
|
readonly font: FontResource;
|
|
81
|
+
/** What the chosen face lacks and the emitter must fake (see {@link SyntheticFace}). */
|
|
82
|
+
readonly synthetic?: SyntheticFace;
|
|
64
83
|
readonly fontSizePt: number;
|
|
65
84
|
/**
|
|
66
85
|
* §17.3.2.42 / §18.4.2 `vertAlign` — how far off the baseline this token
|
|
@@ -205,6 +224,19 @@ export interface MetafileDrawing {
|
|
|
205
224
|
readonly y: number;
|
|
206
225
|
readonly rotationDeg?: number;
|
|
207
226
|
}>;
|
|
227
|
+
/**
|
|
228
|
+
* MS-EMF §2.3.1 — the bitmaps the picture blits into itself, each already a
|
|
229
|
+
* page resource of its own. `x`/`y` are the box's BOTTOM-left corner, in the
|
|
230
|
+
* same local frame as the shapes.
|
|
231
|
+
*/
|
|
232
|
+
readonly images?: ReadonlyArray<{
|
|
233
|
+
readonly resourceName: string;
|
|
234
|
+
readonly x: number;
|
|
235
|
+
readonly y: number;
|
|
236
|
+
readonly width: number;
|
|
237
|
+
readonly height: number;
|
|
238
|
+
readonly rotationDeg?: number;
|
|
239
|
+
}>;
|
|
208
240
|
}
|
|
209
241
|
export interface ImageResource {
|
|
210
242
|
readonly resourceName: string;
|
|
@@ -220,6 +252,13 @@ export interface ImageResource {
|
|
|
220
252
|
* turns these into the same primitives a chart is made of.
|
|
221
253
|
*/
|
|
222
254
|
readonly metafile?: MetaPicture;
|
|
255
|
+
/**
|
|
256
|
+
* The resource name of each bitmap `metafile` blits, in the order its
|
|
257
|
+
* primitives hold them. A metafile draws its own pictures, and each is an
|
|
258
|
+
* ordinary image resource — entered under a key of its own, so the emitters
|
|
259
|
+
* embed and name it without knowing where it came from.
|
|
260
|
+
*/
|
|
261
|
+
readonly metafileImages?: ReadonlyArray<string>;
|
|
223
262
|
}
|
|
224
263
|
/**
|
|
225
264
|
* The fields every {@link PageItem} shares (ir-design §6, frozen at stage 6.4):
|
|
@@ -242,6 +281,24 @@ export interface PageItemBase {
|
|
|
242
281
|
* is treated as an artifact. Ignored when not tagging.
|
|
243
282
|
*/
|
|
244
283
|
readonly structId?: number;
|
|
284
|
+
/**
|
|
285
|
+
* §20.4.2.3 `@behindDoc` — the item is BEHIND the page's content: a
|
|
286
|
+
* watermark, a slide's backdrop. Such items paint before everything else, in
|
|
287
|
+
* their own order. Without that they rode the ordinary passes, where every
|
|
288
|
+
* shape paints after every image — so a slide's white backdrop covered the
|
|
289
|
+
* photograph the slide is made of (corpus: tdf156808, tdf157635, tdf156856).
|
|
290
|
+
*/
|
|
291
|
+
readonly behind?: boolean;
|
|
292
|
+
/**
|
|
293
|
+
* The picture this item belongs to, when it is part of one. A metafile is a
|
|
294
|
+
* list of drawing orders, and text among them is BOTH over what came before
|
|
295
|
+
* and under what comes after: an embedded diagram writes a label, lays a
|
|
296
|
+
* panel over it and writes the label again, offset, as its own drop shadow.
|
|
297
|
+
* Painted in the ordinary passes — every shape, then every line — the buried
|
|
298
|
+
* copy would show through the panel. Items sharing this id are held out of
|
|
299
|
+
* those passes and painted together, in list order.
|
|
300
|
+
*/
|
|
301
|
+
readonly pictureId?: number;
|
|
245
302
|
/**
|
|
246
303
|
* Tagged PDF: explicitly mark this item as a pagination artifact (running
|
|
247
304
|
* header/footer, §14.8.2.2.2). Distinguishes header/footer text from
|
|
@@ -314,6 +371,19 @@ export interface ImageItem extends PageItemBase {
|
|
|
314
371
|
readonly gain: number;
|
|
315
372
|
readonly black: number;
|
|
316
373
|
};
|
|
374
|
+
/**
|
|
375
|
+
* §20.1.8.4 `a:alphaModFix` — how opaque the picture is drawn, 0…1. A slide
|
|
376
|
+
* backed by a photograph at 70 % shows a pale wash of it, not the photograph.
|
|
377
|
+
*/
|
|
378
|
+
readonly alpha?: number;
|
|
379
|
+
/**
|
|
380
|
+
* §20.1.8.23 `a:duotone` — the picture painted between two colours instead of
|
|
381
|
+
* its own: its dark end is the first, its light end the second.
|
|
382
|
+
*/
|
|
383
|
+
readonly duotone?: {
|
|
384
|
+
readonly shadowHex: string;
|
|
385
|
+
readonly highlightHex: string;
|
|
386
|
+
};
|
|
317
387
|
/** §20.1.7.6 — degrees clockwise about the box's centre. */
|
|
318
388
|
readonly rotationDeg?: number;
|
|
319
389
|
/** §20.1.7.6 — the picture drawn mirrored in its box. */
|
|
@@ -340,11 +410,18 @@ export interface ShapeItem extends PageItemBase {
|
|
|
340
410
|
/** A positioned item on a laid-out page: a text line, border, fill, image, or vector shape. */
|
|
341
411
|
export type PageItem = TextLineItem | BorderItem | FillItem | ImageItem | ShapeItem;
|
|
342
412
|
export interface PagePaintPlan {
|
|
413
|
+
/** What sits behind the page's content, in its own order — painted first. */
|
|
414
|
+
readonly behind: ReadonlyArray<PageItem>;
|
|
343
415
|
readonly fills: ReadonlyArray<FillItem>;
|
|
344
416
|
readonly images: ReadonlyArray<ImageItem>;
|
|
345
417
|
readonly borders: ReadonlyArray<BorderItem>;
|
|
346
418
|
readonly shapes: ReadonlyArray<ShapeItem>;
|
|
347
419
|
readonly lines: ReadonlyArray<TextLineItem>;
|
|
420
|
+
/**
|
|
421
|
+
* Pictures, each as its own list in the order the picture draws — one entry
|
|
422
|
+
* per {@link PageItemBase.pictureId}. They paint where the shape pass does.
|
|
423
|
+
*/
|
|
424
|
+
readonly pictures: ReadonlyArray<ReadonlyArray<PageItem>>;
|
|
348
425
|
}
|
|
349
426
|
export declare function paintPlan(commands: ReadonlyArray<PageItem>): PagePaintPlan;
|
|
350
427
|
export interface LaidOutDocument {
|