reamkit 1.30.0 → 1.32.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 (147) hide show
  1. package/README.md +70 -24
  2. package/dist/esm/core/bidi/index.d.ts +10 -0
  3. package/dist/esm/core/bidi/index.js +18 -2
  4. package/dist/esm/core/converter/facade.js +10 -5
  5. package/dist/esm/core/converter/project.js +4 -1
  6. package/dist/esm/core/converter/ream.js +17 -6
  7. package/dist/esm/core/converter/sheet-images.d.ts +17 -0
  8. package/dist/esm/core/converter/sheet-images.js +33 -0
  9. package/dist/esm/core/crypto/offcrypto.js +1 -1
  10. package/dist/esm/core/document-model/index.d.ts +1 -1
  11. package/dist/esm/core/document-model/types.d.ts +175 -0
  12. package/dist/esm/core/drawingml/chart-geometry.d.ts +21 -8
  13. package/dist/esm/core/drawingml/chart-geometry.js +198 -108
  14. package/dist/esm/core/drawingml/chart-parser.js +73 -2
  15. package/dist/esm/core/drawingml/colors.d.ts +6 -0
  16. package/dist/esm/core/drawingml/colors.js +23 -3
  17. package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
  18. package/dist/esm/core/drawingml/shape-render.js +4 -2
  19. package/dist/esm/core/font/index.d.ts +2 -0
  20. package/dist/esm/core/font/measure.d.ts +9 -0
  21. package/dist/esm/core/font/measure.js +9 -1
  22. package/dist/esm/core/font/ttf-build.d.ts +113 -0
  23. package/dist/esm/core/font/ttf-build.js +1224 -0
  24. package/dist/esm/core/font/ttf-subset.d.ts +19 -0
  25. package/dist/esm/core/font/ttf-subset.js +14 -1
  26. package/dist/esm/core/fonts/families.js +4 -2
  27. package/dist/esm/core/fonts/provider.d.ts +17 -0
  28. package/dist/esm/core/fonts/provider.js +27 -2
  29. package/dist/esm/core/fonts/remote-fonts.js +8 -1
  30. package/dist/esm/core/fonts/scripts.js +4 -2
  31. package/dist/esm/core/ir/flow.d.ts +77 -1
  32. package/dist/esm/core/ir/sheet.d.ts +43 -0
  33. package/dist/esm/core/number-format.js +16 -11
  34. package/dist/esm/core/numbering/index.d.ts +1 -1
  35. package/dist/esm/core/numbering/state.d.ts +11 -1
  36. package/dist/esm/core/numbering/state.js +10 -1
  37. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  38. package/dist/esm/core/spreadsheet-model/types.d.ts +96 -0
  39. package/dist/esm/core/style-cascade/resolver.js +39 -6
  40. package/dist/esm/core/style-cascade/types.d.ts +19 -1
  41. package/dist/esm/core/style-cascade/types.js +3 -0
  42. package/dist/esm/excel/column-bands.d.ts +39 -6
  43. package/dist/esm/excel/column-bands.js +41 -17
  44. package/dist/esm/excel/comments-parser.js +19 -0
  45. package/dist/esm/excel/conditional-format.d.ts +7 -3
  46. package/dist/esm/excel/conditional-format.js +77 -39
  47. package/dist/esm/excel/formula/context.d.ts +18 -0
  48. package/dist/esm/excel/formula/eval.js +51 -0
  49. package/dist/esm/excel/formula/index.d.ts +2 -2
  50. package/dist/esm/excel/formula/lexer.d.ts +1 -1
  51. package/dist/esm/excel/formula/lexer.js +41 -0
  52. package/dist/esm/excel/formula/parser.d.ts +25 -0
  53. package/dist/esm/excel/formula/parser.js +95 -0
  54. package/dist/esm/excel/print-model.d.ts +38 -1
  55. package/dist/esm/excel/print-model.js +522 -80
  56. package/dist/esm/excel/shared-strings-parser.d.ts +9 -0
  57. package/dist/esm/excel/shared-strings-parser.js +11 -1
  58. package/dist/esm/excel/sheet-drawing.d.ts +38 -2
  59. package/dist/esm/excel/sheet-drawing.js +47 -13
  60. package/dist/esm/excel/sheet-shape-parser.d.ts +6 -2
  61. package/dist/esm/excel/sheet-shape-parser.js +18 -6
  62. package/dist/esm/excel/sheet-to-flow.d.ts +17 -0
  63. package/dist/esm/excel/sheet-to-flow.js +434 -47
  64. package/dist/esm/excel/styles-parser.d.ts +9 -0
  65. package/dist/esm/excel/styles-parser.js +80 -26
  66. package/dist/esm/excel/table-parser.js +5 -0
  67. package/dist/esm/excel/table-style-presets.d.ts +26 -0
  68. package/dist/esm/excel/table-style-presets.js +1555 -0
  69. package/dist/esm/excel/table-style.d.ts +13 -0
  70. package/dist/esm/excel/table-style.js +75 -0
  71. package/dist/esm/excel/tint.d.ts +10 -0
  72. package/dist/esm/excel/tint.js +75 -0
  73. package/dist/esm/excel/vml-drawing.d.ts +41 -3
  74. package/dist/esm/excel/vml-drawing.js +143 -10
  75. package/dist/esm/excel/worksheet-parser.js +89 -15
  76. package/dist/esm/excel/xls/biff-reader.js +10 -2
  77. package/dist/esm/excel/xlsx-reader.js +195 -14
  78. package/dist/esm/excel/xlsx-writer.js +8 -7
  79. package/dist/esm/html/html-writer.js +202 -20
  80. package/dist/esm/index.d.ts +2 -2
  81. package/dist/esm/layout/fit-pages.d.ts +11 -0
  82. package/dist/esm/layout/fit-pages.js +86 -0
  83. package/dist/esm/layout/line-paint.d.ts +58 -0
  84. package/dist/esm/layout/line-paint.js +107 -0
  85. package/dist/esm/layout/page-doc.d.ts +61 -3
  86. package/dist/esm/layout/page-doc.js +141 -1
  87. package/dist/esm/layout/styled-layout.d.ts +55 -7
  88. package/dist/esm/layout/styled-layout.js +1154 -169
  89. package/dist/esm/layout/turned-section.d.ts +38 -0
  90. package/dist/esm/layout/turned-section.js +205 -0
  91. package/dist/esm/markdown/markdown-writer.js +17 -6
  92. package/dist/esm/pdf/styled-page-emitter.js +114 -60
  93. package/dist/esm/pdf/styled-page-renderer.js +1 -1
  94. package/dist/esm/pdf-reader/cff-outline.d.ts +27 -0
  95. package/dist/esm/pdf-reader/cff-outline.js +169 -21
  96. package/dist/esm/pdf-reader/content.d.ts +53 -0
  97. package/dist/esm/pdf-reader/content.js +9 -1
  98. package/dist/esm/pdf-reader/display.d.ts +36 -0
  99. package/dist/esm/pdf-reader/display.js +66 -1
  100. package/dist/esm/pdf-reader/document.js +5 -1
  101. package/dist/esm/pdf-reader/embedded-fonts.d.ts +2 -1
  102. package/dist/esm/pdf-reader/embedded-fonts.js +32 -2
  103. package/dist/esm/pdf-reader/encodings.d.ts +8 -0
  104. package/dist/esm/pdf-reader/encodings.js +25 -3
  105. package/dist/esm/pdf-reader/face-outlines.d.ts +78 -0
  106. package/dist/esm/pdf-reader/face-outlines.js +362 -0
  107. package/dist/esm/pdf-reader/figures.d.ts +52 -0
  108. package/dist/esm/pdf-reader/figures.js +433 -0
  109. package/dist/esm/pdf-reader/flow-build.d.ts +131 -5
  110. package/dist/esm/pdf-reader/flow-build.js +345 -26
  111. package/dist/esm/pdf-reader/font.js +321 -36
  112. package/dist/esm/pdf-reader/glyf-outline.d.ts +33 -0
  113. package/dist/esm/pdf-reader/glyf-outline.js +135 -1
  114. package/dist/esm/pdf-reader/glyph-names.js +154 -1
  115. package/dist/esm/pdf-reader/layout.d.ts +93 -0
  116. package/dist/esm/pdf-reader/layout.js +1703 -214
  117. package/dist/esm/pdf-reader/page-numbers.d.ts +53 -0
  118. package/dist/esm/pdf-reader/page-numbers.js +167 -0
  119. package/dist/esm/pdf-reader/tagged.js +182 -23
  120. package/dist/esm/pdf-reader/text.d.ts +6 -3
  121. package/dist/esm/pdf-reader/text.js +98 -9
  122. package/dist/esm/pdf-reader/type1-outline.d.ts +11 -0
  123. package/dist/esm/pdf-reader/type1-outline.js +63 -8
  124. package/dist/esm/pdf-reader/vector.js +71 -1
  125. package/dist/esm/svg/svg-text.d.ts +58 -0
  126. package/dist/esm/svg/svg-text.js +322 -0
  127. package/dist/esm/svg/svg-writer.js +135 -71
  128. package/dist/esm/word/doc/doc-reader.js +6 -2
  129. package/dist/esm/word/doc/doc-text.d.ts +6 -0
  130. package/dist/esm/word/doc/doc-text.js +19 -1
  131. package/dist/esm/word/document-parser.d.ts +2 -2
  132. package/dist/esm/word/document-parser.js +11 -1
  133. package/dist/esm/word/docx-reader.js +5 -3
  134. package/dist/esm/word/docx-writer.js +218 -23
  135. package/dist/esm/word/drawing-parser.d.ts +5 -3
  136. package/dist/esm/word/drawing-parser.js +49 -8
  137. package/dist/esm/word/font-embed.d.ts +30 -0
  138. package/dist/esm/word/font-embed.js +173 -0
  139. package/dist/esm/word/font-table.d.ts +10 -0
  140. package/dist/esm/word/font-table.js +13 -1
  141. package/dist/esm/word/index.js +1 -1
  142. package/dist/esm/word/numbering-parser.d.ts +3 -1
  143. package/dist/esm/word/numbering-parser.js +2 -1
  144. package/dist/esm/word/paragraph-properties.d.ts +7 -6
  145. package/dist/esm/word/paragraph-properties.js +14 -2
  146. package/dist/esm/word/run-properties.js +26 -0
  147. package/package.json +12 -3
@@ -0,0 +1,13 @@
1
+ import { TableStyleFormat, XlsxStyles } from '../core/spreadsheet-model/index.js';
2
+ import { WorkbookColors } from './styles-parser.js';
3
+ /**
4
+ * The style a table names, region by region, its colours resolved: the
5
+ * workbook's own style of that name when it defines one, else the style Excel
6
+ * builds in (TableStyleLight1…21, Medium1…28, Dark1…11).
7
+ *
8
+ * @param name The style's name (`<tableStyleInfo name>`).
9
+ * @param styles The workbook's styles: its own table styles and their dxfs.
10
+ * @param colors The workbook's theme and palette, for a built-in style's colours.
11
+ * @returns The style, or undefined for a name that is neither.
12
+ */
13
+ export declare function resolveTableStyleFormat(name: string, styles: XlsxStyles, colors: WorkbookColors): TableStyleFormat | undefined;
@@ -0,0 +1,75 @@
1
+ import { workbookColorHex } from "./styles-parser.js";
2
+ import { presetTableStyle } from "./table-style-presets.js";
3
+ //#region src/excel/table-style.ts
4
+ /**
5
+ * The style a table names, region by region, its colours resolved: the
6
+ * workbook's own style of that name when it defines one, else the style Excel
7
+ * builds in (TableStyleLight1…21, Medium1…28, Dark1…11).
8
+ *
9
+ * @param name The style's name (`<tableStyleInfo name>`).
10
+ * @param styles The workbook's styles: its own table styles and their dxfs.
11
+ * @param colors The workbook's theme and palette, for a built-in style's colours.
12
+ * @returns The style, or undefined for a name that is neither.
13
+ */
14
+ function resolveTableStyleFormat(name, styles, colors) {
15
+ const own = styles.tableStyles?.get(name);
16
+ if (own) {
17
+ const out = {};
18
+ for (const el of own.elements) {
19
+ const dxf = styles.dxfs?.[el.dxfId];
20
+ if (dxf) out[el.type] = {
21
+ dxf,
22
+ ...el.size !== void 0 ? { size: el.size } : {}
23
+ };
24
+ }
25
+ return out;
26
+ }
27
+ const preset = presetTableStyle(name);
28
+ if (!preset) return void 0;
29
+ const out = {};
30
+ for (const [type, dxf] of Object.entries(preset)) out[type] = { dxf: presetDxf(dxf, colors) };
31
+ return out;
32
+ }
33
+ /** A preset's format with its theme colours resolved. */
34
+ function presetDxf(p, colors) {
35
+ const hex = (c) => workbookColorHex({
36
+ "@_theme": String(c[0]),
37
+ "@_tint": String(c[1])
38
+ }, colors);
39
+ const color = p.color ? hex(p.color) : void 0;
40
+ const fill = p.fill ? hex(p.fill) : void 0;
41
+ const edge = (e) => {
42
+ if (!e) return void 0;
43
+ const c = hex(e[1]);
44
+ return {
45
+ style: e[0],
46
+ ...c !== void 0 ? { colorHex: c } : {}
47
+ };
48
+ };
49
+ const border = {};
50
+ for (const side of [
51
+ "left",
52
+ "right",
53
+ "top",
54
+ "bottom",
55
+ "vertical",
56
+ "horizontal"
57
+ ]) {
58
+ const e = edge(p.border?.[side]);
59
+ if (e) border[side] = e;
60
+ }
61
+ return {
62
+ ...p.bold || color !== void 0 ? { font: {
63
+ ...p.bold ? { bold: true } : {},
64
+ ...color !== void 0 ? { colorHex: color } : {}
65
+ } } : {},
66
+ ...fill !== void 0 ? { fill: {
67
+ patternType: "solid",
68
+ fgColorHex: fill,
69
+ bgColorHex: fill
70
+ } } : {},
71
+ ...Object.keys(border).length > 0 ? { border } : {}
72
+ };
73
+ }
74
+ //#endregion
75
+ export { resolveTableStyleFormat };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Lighten a colour by a positive tint or darken it by a negative one, as Excel
3
+ * does (§18.8.19): a tint of 0.4 takes the luminance 40% of the way to white,
4
+ * one of -0.25 a quarter of the way to black.
5
+ *
6
+ * @param hex The colour, RRGGBB.
7
+ * @param tint The tint, -1..1.
8
+ * @returns The tinted colour, RRGGBB in upper case; `hex` itself at no tint.
9
+ */
10
+ export declare function applyTint(hex: string, tint: number): string;
@@ -0,0 +1,75 @@
1
+ //#region src/excel/tint.ts
2
+ /** The top of the HLS scale. */
3
+ var HLS_MAX = 240;
4
+ /** The top of an RGB channel. */
5
+ var RGB_MAX = 255;
6
+ /** Integer division of a non-negative numerator, as C divides unsigned values. */
7
+ var div = (a, b) => Math.floor(a / b);
8
+ /** RGB to hue, luminance and saturation, each 0..240, rounded as Windows rounds. */
9
+ function toHls(r, g, b) {
10
+ const max = Math.max(r, g, b);
11
+ const min = Math.min(r, g, b);
12
+ const sum = max + min;
13
+ const l = div(sum * HLS_MAX + RGB_MAX, 2 * RGB_MAX);
14
+ if (max === min) return [
15
+ HLS_MAX * 2 / 3,
16
+ l,
17
+ 0
18
+ ];
19
+ const range = max - min;
20
+ const s = l <= HLS_MAX / 2 ? div(range * HLS_MAX + div(sum, 2), sum) : div(range * HLS_MAX + div(2 * RGB_MAX - sum, 2), 2 * RGB_MAX - sum);
21
+ const away = (c) => div((max - c) * (HLS_MAX / 6) + div(range, 2), range);
22
+ let h = r === max ? away(b) - away(g) : g === max ? HLS_MAX / 3 + away(r) - away(b) : HLS_MAX * 2 / 3 + away(g) - away(r);
23
+ if (h < 0) h += HLS_MAX;
24
+ if (h > HLS_MAX) h -= HLS_MAX;
25
+ return [
26
+ h,
27
+ l,
28
+ s
29
+ ];
30
+ }
31
+ /** Hue, luminance and saturation, each 0..240, back to RGB as Windows rounds it. */
32
+ function toRgb(h, l, s) {
33
+ const channel = (v) => div(v * RGB_MAX + HLS_MAX / 2, HLS_MAX);
34
+ if (s === 0) {
35
+ const grey = channel(l);
36
+ return [
37
+ grey,
38
+ grey,
39
+ grey
40
+ ];
41
+ }
42
+ const hi = l <= HLS_MAX / 2 ? div(l * (HLS_MAX + s) + HLS_MAX / 2, HLS_MAX) : l + s - div(l * s + HLS_MAX / 2, HLS_MAX);
43
+ const lo = 2 * l - hi;
44
+ const at = (hue) => {
45
+ const x = hue < 0 ? hue + HLS_MAX : hue > HLS_MAX ? hue - HLS_MAX : hue;
46
+ if (x < HLS_MAX / 6) return lo + div((hi - lo) * x + HLS_MAX / 12, HLS_MAX / 6);
47
+ if (x < HLS_MAX / 2) return hi;
48
+ if (x < HLS_MAX * 2 / 3) return lo + div((hi - lo) * (HLS_MAX * 2 / 3 - x) + HLS_MAX / 12, HLS_MAX / 6);
49
+ return lo;
50
+ };
51
+ return [
52
+ channel(at(h + HLS_MAX / 3)),
53
+ channel(at(h)),
54
+ channel(at(h - HLS_MAX / 3))
55
+ ];
56
+ }
57
+ /**
58
+ * Lighten a colour by a positive tint or darken it by a negative one, as Excel
59
+ * does (§18.8.19): a tint of 0.4 takes the luminance 40% of the way to white,
60
+ * one of -0.25 a quarter of the way to black.
61
+ *
62
+ * @param hex The colour, RRGGBB.
63
+ * @param tint The tint, -1..1.
64
+ * @returns The tinted colour, RRGGBB in upper case; `hex` itself at no tint.
65
+ */
66
+ function applyTint(hex, tint) {
67
+ if (!Number.isFinite(tint)) return hex;
68
+ const thousandths = Math.max(-1e3, Math.min(1e3, Math.round(tint * 1e3)));
69
+ if (thousandths === 0) return hex;
70
+ const v = parseInt(hex, 16);
71
+ const [h, l, s] = toHls(v >> 16 & 255, v >> 8 & 255, v & 255);
72
+ return toRgb(h, thousandths > 0 ? l + div((HLS_MAX - l) * thousandths, 1e3) : div(l * (1e3 + thousandths), 1e3), s).map((c) => c.toString(16).padStart(2, "0")).join("").toUpperCase();
73
+ }
74
+ //#endregion
75
+ export { applyTint };
@@ -3,9 +3,10 @@
3
3
  * CSS `style` attribute (`margin-left`/`margin-top`/`width`/`height`).
4
4
  *
5
5
  * The same rectangle is also expressed by `<x:ClientData><x:Anchor>` as
6
- * (column, offset px, row, offset px) pairs, which needs the sheet's column
7
- * widths and row heights to resolve. The style is already absolute, and the two
8
- * agree exactly in every file checked, so this reads the simpler one.
6
+ * (column, offset px, row, offset px) pairs (VmlAnchor). The style is in the
7
+ * producer's own measure of the columns, which is not ours wherever the two
8
+ * measure a digit differently; the anchor counts cells, and lands on the grid
9
+ * we draw. A reader takes the anchor where there is one.
9
10
  */
10
11
  export interface VmlShapeBox {
11
12
  readonly xPt: number;
@@ -13,6 +14,11 @@ export interface VmlShapeBox {
13
14
  readonly widthPt: number;
14
15
  readonly heightPt: number;
15
16
  }
17
+ /**
18
+ * `<x:Anchor>` — LeftColumn, LeftOffset, TopRow, TopOffset, RightColumn,
19
+ * RightOffset, BottomRow, BottomOffset: cells, 0-based, and pixels into them.
20
+ */
21
+ export type VmlAnchor = readonly [number, number, number, number, number, number, number, number];
16
22
  /** A form control declared by a legacy VML shape's `<x:ClientData>`. */
17
23
  export interface VmlFormControl {
18
24
  /** `ST_ObjectType` — `Radio`, `CheckBox`, `GBox`, `Button`, `Drop`, … */
@@ -27,12 +33,42 @@ export interface VmlFormControl {
27
33
  readonly firstButton?: boolean;
28
34
  /** Where the control sits, from the shape's `style` (§{@link VmlShapeBox}). */
29
35
  readonly box?: VmlShapeBox;
36
+ /** …and by the cells it covers, `<x:Anchor>` ({@link VmlAnchor}). */
37
+ readonly anchor?: VmlAnchor;
30
38
  /** `<v:textbox><font size>` — twentieths of a point, so 160 is 8pt. */
31
39
  readonly fontSizePt?: number;
32
40
  }
41
+ /**
42
+ * A cell note's box — the `ObjectType="Note"` shape Excel keeps beside every
43
+ * legacy comment. The comment part holds what the note SAYS; this says whether
44
+ * it is shown and where, and what it is painted with.
45
+ */
46
+ export interface VmlNote {
47
+ /** `<x:Row>` / `<x:Column>` — the cell the note belongs to, 0-based. */
48
+ readonly row: number;
49
+ readonly column: number;
50
+ /** `<x:Visible/>` — the note is shown, not merely flagged in its cell. */
51
+ readonly visible: boolean;
52
+ /**
53
+ * `<x:Anchor>` — LeftColumn, LeftOffset, TopRow, TopOffset, RightColumn,
54
+ * RightOffset, BottomRow, BottomOffset: the box in cells, offsets in pixels.
55
+ */
56
+ readonly anchor?: VmlAnchor;
57
+ /** The same box from the shape's `style`, already in points. */
58
+ readonly box?: VmlShapeBox;
59
+ /** `fillcolor` / `strokecolor`, as 6-hex. */
60
+ readonly fillHex: string;
61
+ readonly lineHex: string;
62
+ /** `<v:shadow on="t">` — the hard shadow a note casts down and to the right. */
63
+ readonly shadow: boolean;
64
+ /** `<v:textbox><div style="text-align">` — the side its text is set to. */
65
+ readonly textAlign?: 'left' | 'center' | 'right';
66
+ }
33
67
  /** A parsed legacy VML drawing: its form controls plus every shape's box. */
34
68
  export interface VmlDrawing {
35
69
  readonly controls: ReadonlyArray<VmlFormControl>;
70
+ /** Every cell note's box, shown or not, in document order. */
71
+ readonly notes: ReadonlyArray<VmlNote>;
36
72
  /**
37
73
  * Shape id (`o:spid` without its `_x0000_s` prefix) → box, for EVERY shape in
38
74
  * the part, controls or not. An ActiveX control's box lives nowhere else: its
@@ -41,6 +77,8 @@ export interface VmlDrawing {
41
77
  * the one thing that says where it goes.
42
78
  */
43
79
  readonly boxes: ReadonlyMap<string, VmlShapeBox>;
80
+ /** Shape id → `<x:Anchor>`, for every shape that carries one. */
81
+ readonly anchors: ReadonlyMap<string, VmlAnchor>;
44
82
  /**
45
83
  * Shape ids whose `<x:ClientData>` clears `<x:PrintObject>` — Excel's "Print
46
84
  * object" checkbox, the legacy spelling of §18.3.1.20 `<controlPr print>`.
@@ -43,26 +43,33 @@ function parseVmlDrawing(data) {
43
43
  const tree = parser.parse(decoder.decode(data));
44
44
  const root = asObject(tree["xml"]) ?? tree;
45
45
  const out = [];
46
+ const notes = [];
46
47
  const boxes = /* @__PURE__ */ new Map();
48
+ const anchors = /* @__PURE__ */ new Map();
47
49
  const nonPrinting = /* @__PURE__ */ new Set();
48
- for (const raw of asArray(root["shape"])) {
49
- const shape = asObject(raw);
50
- if (!shape) continue;
50
+ for (const { shape, box } of shapesOf(root)) {
51
51
  const idAttr = strAttr(shape, "id");
52
52
  const shapeId = (strAttr(shape, "spid") ?? (idAttr !== void 0 && SPID_PREFIX.test(idAttr) ? idAttr : void 0))?.replace(SPID_PREFIX, "");
53
- const box = shapeBox(strAttr(shape, "style"));
54
53
  if (shapeId && box) boxes.set(shapeId, box);
55
54
  const client = asObject(shape["ClientData"]);
56
55
  if (!client) continue;
56
+ const anchor = anchorOf(client);
57
+ if (shapeId && anchor) anchors.set(shapeId, anchor);
57
58
  if (!(flatText(client["PrintObject"])?.toLowerCase() !== "false")) {
58
59
  if (shapeId) nonPrinting.add(shapeId);
59
60
  continue;
60
61
  }
61
62
  const objectType = strAttr(client, "ObjectType");
63
+ if (objectType === "Note") {
64
+ const note = noteOf(shape, client, box);
65
+ if (note) notes.push(note);
66
+ continue;
67
+ }
62
68
  if (!objectType || !CONTROL_TYPES.has(objectType)) continue;
63
69
  const control = { objectType };
64
70
  if (shapeId) control.shapeId = shapeId;
65
71
  if (box) control.box = box;
72
+ if (anchor) control.anchor = anchor;
66
73
  const caption = textboxText(shape["textbox"]);
67
74
  if (caption) control.caption = caption;
68
75
  const fontSizePt = textboxFontSizePt(shape["textbox"]);
@@ -73,10 +80,68 @@ function parseVmlDrawing(data) {
73
80
  }
74
81
  return {
75
82
  controls: out,
83
+ notes,
76
84
  boxes,
85
+ anchors,
77
86
  nonPrinting
78
87
  };
79
88
  }
89
+ /** `<x:Anchor>`'s eight whole numbers, or undefined when it is not that. */
90
+ function anchorOf(client) {
91
+ const numbers = (flatText(client["Anchor"]) ?? "").split(",").map((n) => Number(n.trim()));
92
+ return numbers.length === 8 && numbers.every((n) => Number.isInteger(n)) ? numbers : void 0;
93
+ }
94
+ function noteOf(shape, client, box) {
95
+ const row = Number(flatText(client["Row"])?.trim());
96
+ const column = Number(flatText(client["Column"])?.trim());
97
+ if (!Number.isInteger(row) || !Number.isInteger(column) || row < 0 || column < 0) return;
98
+ const anchor = anchorOf(client);
99
+ const shadowNode = asObject(shape["shadow"]);
100
+ const shadowOn = shadowNode ? strAttr(shadowNode, "on") : void 0;
101
+ const textAlign = /text-align:\s*(left|center|right)/i.exec(styleWithin(shape["textbox"]) ?? "")?.[1]?.toLowerCase();
102
+ return {
103
+ row,
104
+ column,
105
+ visible: "Visible" in client,
106
+ ...anchor ? { anchor } : {},
107
+ ...box ? { box } : {},
108
+ fillHex: vmlColorHex(strAttr(shape, "fillcolor")) ?? "FFFFE1",
109
+ lineHex: vmlColorHex(strAttr(shape, "strokecolor")) ?? "000000",
110
+ shadow: shadowOn === "t" || shadowOn === "true",
111
+ ...textAlign ? { textAlign } : {}
112
+ };
113
+ }
114
+ /** The first `style` declared anywhere under `node` — a textbox's `<div style>`. */
115
+ function styleWithin(node) {
116
+ if (Array.isArray(node)) {
117
+ for (const n of node) {
118
+ const found = styleWithin(n);
119
+ if (found !== void 0) return found;
120
+ }
121
+ return;
122
+ }
123
+ const obj = asObject(node);
124
+ if (!obj) return void 0;
125
+ const own = strAttr(obj, "style");
126
+ if (own !== void 0 && /text-align/i.test(own)) return own;
127
+ for (const [key, value] of Object.entries(obj)) {
128
+ if (key.startsWith("@_")) continue;
129
+ const found = styleWithin(value);
130
+ if (found !== void 0) return found;
131
+ }
132
+ }
133
+ /** A VML colour — `#rrggbb`, `#rgb`, or one of the few names notes use — as 6-hex. */
134
+ function vmlColorHex(value) {
135
+ if (value === void 0) return void 0;
136
+ const v = value.trim().toLowerCase();
137
+ const long = /^#([0-9a-f]{6})\b/.exec(v);
138
+ if (long) return long[1].toUpperCase();
139
+ const short = /^#([0-9a-f]{3})\b/.exec(v);
140
+ if (short) return [...short[1]].map((c) => c + c).join("").toUpperCase();
141
+ if (v.startsWith("infobackground")) return "FFFFE1";
142
+ if (v.startsWith("infotext") || v.startsWith("black")) return "000000";
143
+ if (v.startsWith("white")) return "FFFFFF";
144
+ }
80
145
  /** CSS length → points. A bare number is pixels, VML's implicit unit. */
81
146
  var CSS_UNITS = new Map([
82
147
  ["pt", 1],
@@ -96,25 +161,93 @@ function cssLengthPt(value) {
96
161
  const factor = unit === "" ? .75 : CSS_UNITS.get(unit);
97
162
  return factor === void 0 ? void 0 : n * factor;
98
163
  }
99
- /** The shape's rectangle from its `style` declaration; undefined if incomplete. */
100
- function shapeBox(style) {
101
- if (style === void 0) return void 0;
164
+ /** A `style` attribute's declarations, by lower-cased name. */
165
+ function styleProps(style) {
102
166
  const props = /* @__PURE__ */ new Map();
103
- for (const decl of style.split(";")) {
167
+ for (const decl of (style ?? "").split(";")) {
104
168
  const i = decl.indexOf(":");
105
169
  if (i < 0) continue;
106
170
  props.set(decl.slice(0, i).trim().toLowerCase(), decl.slice(i + 1).trim());
107
171
  }
172
+ return props;
173
+ }
174
+ /** The shape's rectangle from its `style` declaration; undefined if incomplete. */
175
+ function shapeBox(style) {
176
+ if (style === void 0) return void 0;
177
+ const props = styleProps(style);
108
178
  const widthPt = cssLengthPt(props.get("width"));
109
179
  const heightPt = cssLengthPt(props.get("height"));
110
180
  if (widthPt === void 0 || heightPt === void 0) return void 0;
111
181
  return {
112
- xPt: cssLengthPt(props.get("margin-left")) ?? 0,
113
- yPt: cssLengthPt(props.get("margin-top")) ?? 0,
182
+ xPt: (cssLengthPt(props.get("margin-left")) ?? 0) + (cssLengthPt(props.get("left")) ?? 0),
183
+ yPt: (cssLengthPt(props.get("margin-top")) ?? 0) + (cssLengthPt(props.get("top")) ?? 0),
114
184
  widthPt,
115
185
  heightPt
116
186
  };
117
187
  }
188
+ function groupSpace(group, box) {
189
+ const pair = (value, fallback) => {
190
+ const [a, b] = (value ?? "").split(",").map((n) => Number.parseFloat(n));
191
+ return [Number.isFinite(a) ? a : fallback, Number.isFinite(b) ? b : fallback];
192
+ };
193
+ const [ox, oy] = pair(strAttr(group, "coordorigin"), 0);
194
+ const [cw, ch] = pair(strAttr(group, "coordsize"), 1e3);
195
+ const sx = cw !== 0 ? box.widthPt / cw : 0;
196
+ const sy = ch !== 0 ? box.heightPt / ch : 0;
197
+ return {
198
+ x: (u) => box.xPt + (u - ox) * sx,
199
+ y: (u) => box.yPt + (u - oy) * sy,
200
+ sx,
201
+ sy
202
+ };
203
+ }
204
+ /** A child's box in its group's space: bare numbers, the group's units. */
205
+ function boxInGroup(style, space) {
206
+ const props = styleProps(style);
207
+ const units = (name) => {
208
+ const n = Number.parseFloat(props.get(name) ?? "");
209
+ return Number.isFinite(n) ? n : void 0;
210
+ };
211
+ const width = units("width");
212
+ const height = units("height");
213
+ if (width === void 0 || height === void 0) return void 0;
214
+ const left = (units("margin-left") ?? 0) + (units("left") ?? 0);
215
+ const top = (units("margin-top") ?? 0) + (units("top") ?? 0);
216
+ return {
217
+ xPt: space.x(left),
218
+ yPt: space.y(top),
219
+ widthPt: width * space.sx,
220
+ heightPt: height * space.sy
221
+ };
222
+ }
223
+ /**
224
+ * Every shape in the drawing with its box on the sheet — those at the top and
225
+ * those inside a `<v:group>`, whose `left`/`top` count in the group's own
226
+ * space. Reading only the top level lost every control a group held:
227
+ * 45540_form_Footer.xlsx groups the 27 check boxes of its "What industry are
228
+ * you in?" block, and they ended up in a list after the form.
229
+ */
230
+ function shapesOf(root) {
231
+ const out = [];
232
+ const visit = (node, space, depth) => {
233
+ const boxOf = (style) => space ? boxInGroup(style, space) : shapeBox(style);
234
+ for (const raw of asArray(node["shape"])) {
235
+ const shape = asObject(raw);
236
+ if (shape) out.push({
237
+ shape,
238
+ box: boxOf(strAttr(shape, "style"))
239
+ });
240
+ }
241
+ if (depth >= 8) return;
242
+ for (const raw of asArray(node["group"])) {
243
+ const group = asObject(raw);
244
+ const box = group ? boxOf(strAttr(group, "style")) : void 0;
245
+ if (group && box) visit(group, groupSpace(group, box), depth + 1);
246
+ }
247
+ };
248
+ visit(root, void 0, 0);
249
+ return out;
250
+ }
118
251
  /** `<v:textbox>`'s `<font size>` — twentieths of a point (160 ⇒ 8pt). */
119
252
  function textboxFontSizePt(node) {
120
253
  const box = asObject(node);
@@ -3,7 +3,7 @@ import { resolveInternalEntities } from "../core/opc/xml-entities.js";
3
3
  import { parseCellRef } from "./cell-reference.js";
4
4
  import { parseAreaRef } from "./defined-name-ref.js";
5
5
  import { decodeXstring } from "./escaped-text.js";
6
- import { parseDxf } from "./styles-parser.js";
6
+ import { parseDxf, workbookColorHex } from "./styles-parser.js";
7
7
  import { XMLParser } from "fast-xml-parser";
8
8
  //#region src/excel/worksheet-parser.ts
9
9
  var decoder = new TextDecoder("utf-8");
@@ -49,11 +49,16 @@ function parseWorksheet(data, theme) {
49
49
  const rowBreaks = parseBreaks(wsObj, "rowBreaks");
50
50
  const colBreaks = parseBreaks(wsObj, "colBreaks");
51
51
  const pane = parsePane(wsObj);
52
+ const showGridLines = parseShowGridLines(wsObj);
53
+ const rightToLeft = parseRightToLeft(wsObj);
52
54
  const drawingNode = wsObj["drawing"];
53
55
  const drawingRelId = drawingNode && typeof drawingNode === "object" ? strAttr(drawingNode, "id") : void 0;
54
56
  const legacyNode = wsObj["legacyDrawing"];
55
57
  const legacyDrawingRelId = legacyNode && typeof legacyNode === "object" ? strAttr(legacyNode, "id") : void 0;
56
- const conditionalFormats = [...parseConditionalFormatting(wsObj), ...parseX14ConditionalFormatting(wsObj, {
58
+ const conditionalFormats = [...parseConditionalFormatting(wsObj, {
59
+ ...theme ? { theme } : {},
60
+ indexed: INDEXED_COLORS
61
+ }, x14DataBars(wsObj)), ...parseX14ConditionalFormatting(wsObj, {
57
62
  ...theme ? { theme } : {},
58
63
  indexed: INDEXED_COLORS
59
64
  })];
@@ -73,7 +78,10 @@ function parseWorksheet(data, theme) {
73
78
  ...rowBreaks.length > 0 ? { rowBreaks } : {},
74
79
  ...colBreaks.length > 0 ? { colBreaks } : {},
75
80
  ...pane ? { pane } : {},
81
+ ...showGridLines === false ? { showGridLines } : {},
82
+ ...rightToLeft ? { rightToLeft } : {},
76
83
  ...drawingRelId !== void 0 ? { drawingRelId } : {},
84
+ ...tree["worksheet"] === void 0 ? { chartSheet: true } : {},
77
85
  ...legacyDrawingRelId !== void 0 ? { legacyDrawingRelId } : {},
78
86
  ...conditionalFormats.length > 0 ? { conditionalFormats } : {},
79
87
  ...dataValidations.length > 0 ? { dataValidations } : {},
@@ -257,6 +265,24 @@ function parsePane(ws) {
257
265
  frozenCols
258
266
  };
259
267
  }
268
+ function parseShowGridLines(ws) {
269
+ const views = ws["sheetViews"];
270
+ if (!views || typeof views !== "object") return void 0;
271
+ const viewRaw = views["sheetView"];
272
+ const view = Array.isArray(viewRaw) ? viewRaw[0] : viewRaw;
273
+ if (!view || typeof view !== "object") return void 0;
274
+ const raw = strAttr(view, "showGridLines");
275
+ return raw === "0" || raw === "false" ? false : void 0;
276
+ }
277
+ function parseRightToLeft(ws) {
278
+ const views = ws["sheetViews"];
279
+ if (!views || typeof views !== "object") return void 0;
280
+ const viewRaw = views["sheetView"];
281
+ const view = Array.isArray(viewRaw) ? viewRaw[0] : viewRaw;
282
+ if (!view || typeof view !== "object") return void 0;
283
+ const raw = strAttr(view, "rightToLeft");
284
+ return raw === "1" || raw === "true" ? true : void 0;
285
+ }
260
286
  function parseBreaks(ws, tag) {
261
287
  const node = ws[tag];
262
288
  if (!node || typeof node !== "object") return [];
@@ -369,7 +395,44 @@ function parseMerges(ws) {
369
395
  }
370
396
  return out;
371
397
  }
372
- function parseConditionalFormatting(ws) {
398
+ /**
399
+ * The 2009 extension's half of each data bar (`<x14:cfRule type="dataBar"
400
+ * id>`): what the 2006 rule of the same id cannot say — whether the bar is
401
+ * faded or solid. Excel writes the two halves of one rule apart, the 2006
402
+ * one pointing at this by `<x14:id>`.
403
+ *
404
+ * @param ws The worksheet element.
405
+ * @returns The extension's options by rule id.
406
+ */
407
+ function x14DataBars(ws) {
408
+ const out = /* @__PURE__ */ new Map();
409
+ for (const ext of toArray(asObjectNode(ws["extLst"])?.["ext"])) {
410
+ const group = asObjectNode(asObjectNode(ext)?.["conditionalFormattings"]);
411
+ for (const cf of toArray(group?.["conditionalFormatting"])) for (const rn of toArray(asObjectNode(cf)?.["cfRule"])) {
412
+ const node = asObjectNode(rn);
413
+ const bar = asObjectNode(node?.["dataBar"]);
414
+ const id = node ? strAttr(node, "id") : void 0;
415
+ if (!bar || id === void 0) continue;
416
+ const gradient = strAttr(bar, "gradient");
417
+ const minLength = parseNumericAttr(bar, "minLength");
418
+ const maxLength = parseNumericAttr(bar, "maxLength");
419
+ out.set(id, {
420
+ ...gradient === "0" || gradient === "false" ? { gradient: false } : {},
421
+ ...minLength !== void 0 ? { minLength } : {},
422
+ ...maxLength !== void 0 ? { maxLength } : {}
423
+ });
424
+ }
425
+ }
426
+ return out;
427
+ }
428
+ /** A 2006 rule's `<extLst><ext><x14:id>` — the id of its 2009 half, if any. */
429
+ function x14IdOf(rule) {
430
+ for (const ext of toArray(asObjectNode(rule["extLst"])?.["ext"])) {
431
+ const id = asObjectNode(ext)?.["id"];
432
+ if (typeof id === "string") return id;
433
+ }
434
+ }
435
+ function parseConditionalFormatting(ws, colors, x14Bars) {
373
436
  const raw = ws["conditionalFormatting"];
374
437
  const items = Array.isArray(raw) ? raw : raw !== void 0 ? [raw] : [];
375
438
  const out = [];
@@ -385,7 +448,7 @@ function parseConditionalFormatting(ws) {
385
448
  const rules = [];
386
449
  for (const rn of ruleItems) {
387
450
  if (!rn || typeof rn !== "object") continue;
388
- const rule = parseCfRule(rn);
451
+ const rule = parseCfRule(rn, colors, x14Bars);
389
452
  if (rule) rules.push(rule);
390
453
  }
391
454
  if (rules.length > 0) out.push({
@@ -418,7 +481,7 @@ function parseX14ConditionalFormatting(ws, colors) {
418
481
  for (const rn of toArray(obj["cfRule"])) {
419
482
  const node = asObjectNode(rn);
420
483
  if (!node) continue;
421
- const rule = parseCfRule(asBaseCfRule(node));
484
+ const rule = parseCfRule(asBaseCfRule(node), colors);
422
485
  if (!rule) continue;
423
486
  const dxf = parseDxf(node["dxf"], colors);
424
487
  rules.push("dxfId" in rule && Object.keys(dxf).length > 0 ? {
@@ -564,13 +627,21 @@ var CF_OPERATORS = new Set([
564
627
  "between",
565
628
  "notBetween"
566
629
  ]);
567
- function parseCfRule(obj) {
630
+ function parseCfRule(obj, colors, x14Bars) {
568
631
  const priority = parseNumericAttr(obj, "priority") ?? 0;
569
632
  const type = strAttr(obj, "type");
570
633
  switch (type) {
571
634
  case "cellIs": return parseCellIsRule(obj, priority);
572
- case "colorScale": return parseColorScaleRule(obj, priority);
573
- case "dataBar": return parseDataBarRule(obj, priority);
635
+ case "colorScale": return parseColorScaleRule(obj, priority, colors);
636
+ case "dataBar": {
637
+ const rule = parseDataBarRule(obj, priority, colors);
638
+ const id = x14IdOf(obj);
639
+ const ext = id !== void 0 ? x14Bars?.get(id) : void 0;
640
+ return rule && ext ? {
641
+ ...rule,
642
+ ...ext
643
+ } : rule;
644
+ }
574
645
  case "iconSet": return parseIconSetRule(obj, priority);
575
646
  case "top10": return parseTop10Rule(obj, priority);
576
647
  case "aboveAverage": return parseAboveAverageRule(obj, priority);
@@ -682,21 +753,24 @@ function parseIconSetRule(obj, priority) {
682
753
  const iconSet = strAttr(isObj, "iconSet") ?? "3TrafficLights1";
683
754
  const reverseRaw = strAttr(isObj, "reverse");
684
755
  const reverse = reverseRaw === "1" || reverseRaw === "true";
756
+ const showValueRaw = strAttr(isObj, "showValue");
757
+ const showValue = showValueRaw === "0" || showValueRaw === "false" ? false : void 0;
685
758
  return {
686
759
  type: "iconSet",
687
760
  priority,
688
761
  iconSet,
689
762
  cfvos,
690
- ...reverse ? { reverse } : {}
763
+ ...reverse ? { reverse } : {},
764
+ ...showValue === false ? { showValue } : {}
691
765
  };
692
766
  }
693
- function parseDataBarRule(obj, priority) {
767
+ function parseDataBarRule(obj, priority, colors) {
694
768
  const db = obj["dataBar"];
695
769
  if (!db || typeof db !== "object") return void 0;
696
770
  const dbObj = db;
697
771
  const cfvos = parseCfvos(dbObj["cfvo"]);
698
772
  if (cfvos.length < 2) return void 0;
699
- const colorHex = colorRgbHex(dbObj["color"]);
773
+ const colorHex = workbookColorHex(dbObj["color"], colors);
700
774
  if (!colorHex) return void 0;
701
775
  const minLength = parseNumericAttr(dbObj, "minLength");
702
776
  const maxLength = parseNumericAttr(dbObj, "maxLength");
@@ -743,12 +817,12 @@ var CFVO_TYPES = new Set([
743
817
  "autoMin",
744
818
  "autoMax"
745
819
  ]);
746
- function parseColorScaleRule(obj, priority) {
820
+ function parseColorScaleRule(obj, priority, colors) {
747
821
  const cs = obj["colorScale"];
748
822
  if (!cs || typeof cs !== "object") return void 0;
749
823
  const csObj = cs;
750
824
  const cfvos = parseCfvos(csObj["cfvo"]);
751
- const colorsHex = parseScaleColors(csObj["color"]);
825
+ const colorsHex = parseScaleColors(csObj["color"], colors);
752
826
  if (cfvos.length < 2 || cfvos.length !== colorsHex.length) return void 0;
753
827
  return {
754
828
  type: "colorScale",
@@ -773,11 +847,11 @@ function parseCfvos(raw) {
773
847
  }
774
848
  return out;
775
849
  }
776
- function parseScaleColors(raw) {
850
+ function parseScaleColors(raw, colors) {
777
851
  const items = Array.isArray(raw) ? raw : raw !== void 0 ? [raw] : [];
778
852
  const out = [];
779
853
  for (const it of items) {
780
- const hex = colorRgbHex(it);
854
+ const hex = workbookColorHex(it, colors);
781
855
  if (!hex) return [];
782
856
  out.push(hex);
783
857
  }