reamkit 1.15.4 → 1.17.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 (127) hide show
  1. package/dist/esm/core/bidi/segments.js +1 -2
  2. package/dist/esm/core/converter/facade.d.ts +5 -0
  3. package/dist/esm/core/converter/project.js +5 -1
  4. package/dist/esm/core/converter/ream.d.ts +6 -0
  5. package/dist/esm/core/converter/ream.js +34 -8
  6. package/dist/esm/core/document-model/index.d.ts +1 -1
  7. package/dist/esm/core/document-model/types.d.ts +659 -7
  8. package/dist/esm/core/drawingml/chart-geometry.d.ts +34 -10
  9. package/dist/esm/core/drawingml/chart-geometry.js +424 -92
  10. package/dist/esm/core/drawingml/chart-parser.js +301 -11
  11. package/dist/esm/core/drawingml/colors.d.ts +7 -7
  12. package/dist/esm/core/drawingml/colors.js +84 -18
  13. package/dist/esm/core/drawingml/preset-geometry.js +240 -6
  14. package/dist/esm/core/drawingml/shape-render.d.ts +12 -0
  15. package/dist/esm/core/drawingml/shape-render.js +116 -3
  16. package/dist/esm/core/drawingml/sparkline-geometry.js +6 -4
  17. package/dist/esm/core/drawingml/theme-parser.d.ts +51 -0
  18. package/dist/esm/core/drawingml/theme-parser.js +92 -1
  19. package/dist/esm/core/font/measure.d.ts +5 -0
  20. package/dist/esm/core/font/measure.js +21 -1
  21. package/dist/esm/core/font/ttf-subset.js +26 -1
  22. package/dist/esm/core/images.d.ts +2 -2
  23. package/dist/esm/core/images.js +353 -43
  24. package/dist/esm/core/indexed-colors.d.ts +11 -0
  25. package/dist/esm/core/indexed-colors.js +81 -0
  26. package/dist/esm/core/ir/flow.d.ts +23 -1
  27. package/dist/esm/core/ir/sheet.d.ts +61 -0
  28. package/dist/esm/{excel → core}/number-format.d.ts +37 -0
  29. package/dist/esm/core/number-format.js +800 -0
  30. package/dist/esm/core/numbering/apply.d.ts +5 -3
  31. package/dist/esm/core/numbering/apply.js +42 -11
  32. package/dist/esm/core/numbering/index.js +2 -0
  33. package/dist/esm/core/numbering/state.d.ts +13 -1
  34. package/dist/esm/core/numbering/state.js +256 -15
  35. package/dist/esm/core/opc/alternate-content.d.ts +13 -0
  36. package/dist/esm/core/opc/alternate-content.js +72 -0
  37. package/dist/esm/core/opc/core-properties.js +1 -0
  38. package/dist/esm/core/opc/package.d.ts +14 -1
  39. package/dist/esm/core/opc/package.js +43 -4
  40. package/dist/esm/core/opc/relationships.js +1 -0
  41. package/dist/esm/core/opc/xml-entities.d.ts +12 -0
  42. package/dist/esm/core/opc/xml-entities.js +91 -0
  43. package/dist/esm/core/po-helpers.js +16 -1
  44. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  45. package/dist/esm/core/spreadsheet-model/types.d.ts +137 -0
  46. package/dist/esm/core/style-cascade/resolver.js +66 -11
  47. package/dist/esm/core/style-cascade/table.js +35 -11
  48. package/dist/esm/core/style-cascade/types.d.ts +42 -1
  49. package/dist/esm/core/style-cascade/types.js +4 -0
  50. package/dist/esm/core/vector.d.ts +30 -0
  51. package/dist/esm/excel/activex-parser.js +1 -0
  52. package/dist/esm/excel/column-bands.d.ts +26 -1
  53. package/dist/esm/excel/column-bands.js +156 -20
  54. package/dist/esm/excel/comments-parser.js +5 -3
  55. package/dist/esm/excel/conditional-format.d.ts +25 -2
  56. package/dist/esm/excel/conditional-format.js +63 -19
  57. package/dist/esm/excel/escaped-text.d.ts +9 -0
  58. package/dist/esm/excel/escaped-text.js +20 -0
  59. package/dist/esm/excel/form-control-parser.js +1 -0
  60. package/dist/esm/excel/formula/dates.js +1 -1
  61. package/dist/esm/excel/header-footer.d.ts +9 -1
  62. package/dist/esm/excel/header-footer.js +162 -14
  63. package/dist/esm/excel/index.d.ts +1 -1
  64. package/dist/esm/excel/index.js +2 -2
  65. package/dist/esm/excel/pivot-table-parser.js +1 -0
  66. package/dist/esm/excel/print-model.d.ts +75 -0
  67. package/dist/esm/excel/print-model.js +899 -77
  68. package/dist/esm/excel/printer-settings.d.ts +16 -0
  69. package/dist/esm/excel/printer-settings.js +36 -0
  70. package/dist/esm/excel/rich-value.d.ts +13 -0
  71. package/dist/esm/excel/rich-value.js +120 -0
  72. package/dist/esm/excel/shared-strings-parser.js +11 -7
  73. package/dist/esm/excel/sheet-drawing.d.ts +11 -0
  74. package/dist/esm/excel/sheet-drawing.js +26 -3
  75. package/dist/esm/excel/sheet-shape-parser.d.ts +53 -1
  76. package/dist/esm/excel/sheet-shape-parser.js +461 -32
  77. package/dist/esm/excel/sheet-to-flow.d.ts +23 -2
  78. package/dist/esm/excel/sheet-to-flow.js +553 -36
  79. package/dist/esm/excel/slicer-parser.js +1 -0
  80. package/dist/esm/excel/styles-parser.d.ts +30 -2
  81. package/dist/esm/excel/styles-parser.js +177 -40
  82. package/dist/esm/excel/table-parser.js +1 -0
  83. package/dist/esm/excel/vml-drawing.d.ts +68 -0
  84. package/dist/esm/excel/vml-drawing.js +158 -0
  85. package/dist/esm/excel/workbook-parser.d.ts +5 -0
  86. package/dist/esm/excel/workbook-parser.js +5 -1
  87. package/dist/esm/excel/worksheet-parser.d.ts +2 -1
  88. package/dist/esm/excel/worksheet-parser.js +150 -61
  89. package/dist/esm/excel/xlsx-reader.js +242 -11
  90. package/dist/esm/excel/xlsx-to-pdf.js +12 -1
  91. package/dist/esm/html/html-writer.js +29 -6
  92. package/dist/esm/layout/page-doc.d.ts +87 -1
  93. package/dist/esm/layout/styled-layout.d.ts +46 -2
  94. package/dist/esm/layout/styled-layout.js +2345 -274
  95. package/dist/esm/pdf/cid-font.js +20 -9
  96. package/dist/esm/pdf/objects.d.ts +13 -0
  97. package/dist/esm/pdf/objects.js +18 -1
  98. package/dist/esm/pdf/shading.js +76 -6
  99. package/dist/esm/pdf/styled-page-emitter.js +355 -49
  100. package/dist/esm/pdf/text-page-renderer.js +1 -0
  101. package/dist/esm/pdf/vector-graphics.d.ts +31 -2
  102. package/dist/esm/pdf/vector-graphics.js +103 -3
  103. package/dist/esm/pptx/pptx-reader.js +2 -0
  104. package/dist/esm/pptx/slide-parser.d.ts +21 -4
  105. package/dist/esm/pptx/slide-parser.js +98 -24
  106. package/dist/esm/svg/svg-writer.js +23 -4
  107. package/dist/esm/word/document-parser.d.ts +96 -7
  108. package/dist/esm/word/document-parser.js +538 -116
  109. package/dist/esm/word/docx-reader.js +135 -43
  110. package/dist/esm/word/docx-writer.js +6 -1
  111. package/dist/esm/word/drawing-parser.d.ts +86 -5
  112. package/dist/esm/word/drawing-parser.js +1590 -54
  113. package/dist/esm/word/index.d.ts +4 -4
  114. package/dist/esm/word/numbering-parser.d.ts +2 -1
  115. package/dist/esm/word/numbering-parser.js +108 -21
  116. package/dist/esm/word/paragraph-properties.d.ts +11 -1
  117. package/dist/esm/word/paragraph-properties.js +219 -10
  118. package/dist/esm/word/run-properties.js +45 -1
  119. package/dist/esm/word/settings-parser.d.ts +24 -0
  120. package/dist/esm/word/settings-parser.js +32 -5
  121. package/dist/esm/word/shading.d.ts +11 -0
  122. package/dist/esm/word/shading.js +64 -0
  123. package/dist/esm/word/styles-parser.js +9 -2
  124. package/dist/esm/word/table-parser.d.ts +9 -1
  125. package/dist/esm/word/table-parser.js +182 -30
  126. package/package.json +7 -3
  127. package/dist/esm/excel/number-format.js +0 -476
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The colour a `w:shd` actually paints: its `w:fill` background with the
3
+ * pattern colour blended in at the pattern's density. `clear` (and an absent
4
+ * `w:val`) is the fill alone; `nil` paints nothing.
5
+ *
6
+ * @param val `w:val` — the pattern.
7
+ * @param color `w:color` — the pattern's own colour (`auto` ⇒ black).
8
+ * @param fill `w:fill` — the background (`auto` ⇒ white).
9
+ * @returns The 6-hex colour to paint, or `undefined` when nothing is painted.
10
+ */
11
+ export declare function shadingFillHex(val: string | undefined, color: string | undefined, fill: string | undefined): string | undefined;
@@ -0,0 +1,64 @@
1
+ //#region src/word/shading.ts
2
+ /** §17.18.78 ST_Shd — the pattern densities, as the share of `color` over `fill`. */
3
+ var PATTERN_DENSITY = new Map([
4
+ ["solid", 1],
5
+ ["pct5", .05],
6
+ ["pct10", .1],
7
+ ["pct12", .125],
8
+ ["pct15", .15],
9
+ ["pct20", .2],
10
+ ["pct25", .25],
11
+ ["pct30", .3],
12
+ ["pct35", .35],
13
+ ["pct37", .375],
14
+ ["pct40", .4],
15
+ ["pct45", .45],
16
+ ["pct50", .5],
17
+ ["pct55", .55],
18
+ ["pct60", .6],
19
+ ["pct62", .625],
20
+ ["pct65", .65],
21
+ ["pct70", .7],
22
+ ["pct75", .75],
23
+ ["pct80", .8],
24
+ ["pct85", .85],
25
+ ["pct87", .875],
26
+ ["pct90", .9],
27
+ ["pct95", .95],
28
+ ["thinHorzStripe", .25],
29
+ ["thinVertStripe", .25],
30
+ ["thinDiagStripe", .25],
31
+ ["thinHorzCross", .3],
32
+ ["thinDiagCross", .3],
33
+ ["horzStripe", .4],
34
+ ["vertStripe", .4],
35
+ ["diagStripe", .4],
36
+ ["horzCross", .5],
37
+ ["diagCross", .5]
38
+ ]);
39
+ var HEX6 = /^[0-9A-Fa-f]{6}$/u;
40
+ /**
41
+ * The colour a `w:shd` actually paints: its `w:fill` background with the
42
+ * pattern colour blended in at the pattern's density. `clear` (and an absent
43
+ * `w:val`) is the fill alone; `nil` paints nothing.
44
+ *
45
+ * @param val `w:val` — the pattern.
46
+ * @param color `w:color` — the pattern's own colour (`auto` ⇒ black).
47
+ * @param fill `w:fill` — the background (`auto` ⇒ white).
48
+ * @returns The 6-hex colour to paint, or `undefined` when nothing is painted.
49
+ */
50
+ function shadingFillHex(val, color, fill) {
51
+ if (val === "nil") return void 0;
52
+ const bg = fill !== void 0 && HEX6.test(fill) ? fill.toUpperCase() : void 0;
53
+ const density = val !== void 0 ? PATTERN_DENSITY.get(val) : void 0;
54
+ if (density === void 0) return bg;
55
+ return blend(color !== void 0 && HEX6.test(color) ? color.toUpperCase() : "000000", bg ?? "FFFFFF", density);
56
+ }
57
+ /** `a` over `b` at `t` (0..1), per channel. */
58
+ function blend(a, b, t) {
59
+ const ch = (hex, i) => parseInt(hex.slice(i * 2, i * 2 + 2), 16);
60
+ const mix = (i) => Math.round(ch(a, i) * t + ch(b, i) * (1 - t)).toString(16).padStart(2, "0");
61
+ return (mix(0) + mix(1) + mix(2)).toUpperCase();
62
+ }
63
+ //#endregion
64
+ export { shadingFillHex };
@@ -1,13 +1,14 @@
1
1
  import { eighthPtToPt, twipsToPt } from "../core/ir/units.js";
2
2
  import { EMPTY_STYLE_SHEET } from "../core/style-cascade/resolver.js";
3
3
  import "../core/style-cascade/index.js";
4
- import { asArray, asElement, getAttr, getVal } from "./xml-helpers.js";
4
+ import { asArray, asElement, getAttr, getVal, parseIntAttr } from "./xml-helpers.js";
5
5
  import { parseRunProperties } from "./run-properties.js";
6
6
  import { parseParagraphProperties } from "./paragraph-properties.js";
7
7
  import { XMLParser } from "fast-xml-parser";
8
8
  //#region src/word/styles-parser.ts
9
9
  var decoder = new TextDecoder("utf-8");
10
10
  var parser = new XMLParser({
11
+ htmlEntities: true,
11
12
  ignoreAttributes: false,
12
13
  attributeNamePrefix: "@_",
13
14
  textNodeName: "#text",
@@ -118,12 +119,17 @@ function parseTableStyleLayer(el) {
118
119
  const tcPr = asElement(el["w:tcPr"]);
119
120
  const borders = parseFlatBorders(tblPr ? asElement(tblPr["w:tblBorders"]) : void 0) ?? parseFlatBorders(tcPr ? asElement(tcPr["w:tcBorders"]) : void 0);
120
121
  const cellMargins = parseFlatCellMargins(tblPr ? asElement(tblPr["w:tblCellMar"]) : void 0);
122
+ const tblInd = tblPr ? asElement(tblPr["w:tblInd"]) : void 0;
123
+ const indW = tblInd ? parseIntAttr(tblInd, "w") : void 0;
124
+ const indType = tblInd ? getAttr(tblInd, "type") : void 0;
125
+ const indentPt = indW !== void 0 && (indType === void 0 || indType === "dxa") ? twipsToPt(indW) : void 0;
121
126
  const shading = parseFlatShading(tcPr ? tcPr["w:shd"] : void 0);
122
127
  const runProperties = parseRunProperties(el["w:rPr"]);
123
128
  const paragraphProperties = parseParagraphProperties(el["w:pPr"]);
124
129
  const layer = {
125
130
  ...borders ? { borders } : {},
126
131
  ...cellMargins ? { cellMargins } : {},
132
+ ...indentPt !== void 0 ? { indentPt } : {},
127
133
  ...shading ? { shading } : {},
128
134
  ...Object.keys(runProperties).length > 0 ? { runProperties } : {},
129
135
  ...Object.keys(paragraphProperties).length > 0 ? { paragraphProperties } : {}
@@ -136,7 +142,8 @@ var FLAT_BORDER_STYLES = new Set([
136
142
  "double",
137
143
  "thick",
138
144
  "dotted",
139
- "dashed"
145
+ "dashed",
146
+ "dashSmallGap"
140
147
  ]);
141
148
  function parseFlatBorder(node) {
142
149
  const el = asElement(node);
@@ -1,4 +1,4 @@
1
- import { Table } from '../core/document-model/index.js';
1
+ import { CellBorders, Table } from '../core/document-model/index.js';
2
2
  import { PoNode } from '../core/po-helpers.js';
3
3
  import { ParseContext } from './document-parser.js';
4
4
  /**
@@ -13,3 +13,11 @@ import { ParseContext } from './document-parser.js';
13
13
  * @returns The parsed table.
14
14
  */
15
15
  export declare function parseTable(tbl: PoNode, ctx?: ParseContext): Table;
16
+ /**
17
+ * A `w:tcBorders` / `w:tblBorders` / §17.6.10 `w:pgBorders` element: the rule on
18
+ * each edge. Every one of them is spelled the same way.
19
+ *
20
+ * @param node The borders element, or `undefined`.
21
+ * @returns The edges that name a rule, or `undefined` when none do.
22
+ */
23
+ export declare function parseBorders(node: PoNode | undefined): CellBorders | undefined;
@@ -1,6 +1,6 @@
1
- import { eighthPtToPt, twipsToPt } from "../core/ir/units.js";
2
- import { poAttr, poChildren, poChildrenWith, poFirstChild, poIntAttr, poToggle, poVal } from "../core/po-helpers.js";
3
- import { DEFAULT_PARSE_CONTEXT, parseBodyElements } from "./document-parser.js";
1
+ import { eighthPtToPt, pt, twipsToPt } from "../core/ir/units.js";
2
+ import { poAttr, poChildren, poChildrenWith, poFirstChild, poIntAttr, poIs, poToggle, poVal } from "../core/po-helpers.js";
3
+ import { DEFAULT_PARSE_CONTEXT, parseBodyElements, sdtRunProperties } from "./document-parser.js";
4
4
  //#region src/word/table-parser.ts
5
5
  var BORDER_STYLES = new Set([
6
6
  "none",
@@ -8,7 +8,23 @@ var BORDER_STYLES = new Set([
8
8
  "double",
9
9
  "thick",
10
10
  "dotted",
11
- "dashed"
11
+ "dashed",
12
+ "dashSmallGap"
13
+ ]);
14
+ var MULTI_LINE_BORDERS = new Set([
15
+ "triple",
16
+ "doubleWave",
17
+ "thinThickSmallGap",
18
+ "thickThinSmallGap",
19
+ "thinThickThinSmallGap",
20
+ "thinThickMediumGap",
21
+ "thickThinMediumGap",
22
+ "thinThickThinMediumGap",
23
+ "thinThickLargeGap",
24
+ "thickThinLargeGap",
25
+ "thinThickThinLargeGap",
26
+ "outset",
27
+ "inset"
12
28
  ]);
13
29
  var WIDTH_TYPES = new Set([
14
30
  "auto",
@@ -21,6 +37,34 @@ var HEIGHT_RULES = new Set([
21
37
  "atLeast",
22
38
  "exact"
23
39
  ]);
40
+ function parseTablePosition(tblpPr) {
41
+ const horz = poAttr(tblpPr, "horzAnchor");
42
+ const vert = poAttr(tblpPr, "vertAnchor");
43
+ const hRel = horz === "page" ? "page" : horz === "margin" ? "margin" : "column";
44
+ const vRel = vert === "page" ? "page" : vert === "margin" ? "margin" : "paragraph";
45
+ const xSpec = poAttr(tblpPr, "tblpXSpec");
46
+ const align = xSpec === "center" ? "center" : xSpec === "right" ? "right" : xSpec === "left" ? "left" : void 0;
47
+ const x = poIntAttr(tblpPr, "tblpX");
48
+ const y = poIntAttr(tblpPr, "tblpY");
49
+ const dist = (name) => twipsToPt(poIntAttr(tblpPr, name) ?? 0);
50
+ return {
51
+ wrap: "square",
52
+ posH: {
53
+ relativeFrom: hRel,
54
+ ...align ? { align } : { offsetPt: twipsToPt(x ?? 0) }
55
+ },
56
+ posV: {
57
+ relativeFrom: vRel,
58
+ offsetPt: twipsToPt(y ?? 0)
59
+ },
60
+ wrapDist: {
61
+ topPt: dist("topFromText"),
62
+ bottomPt: dist("bottomFromText"),
63
+ leftPt: dist("leftFromText"),
64
+ rightPt: dist("rightFromText")
65
+ }
66
+ };
67
+ }
24
68
  /**
25
69
  * Parse a `w:tbl` (ECMA-376 Part 1 §17.4) in preserveOrder shape into the typed
26
70
  * {@link Table} model: its properties (style ref, look, width, layout, borders,
@@ -36,26 +80,53 @@ function parseTable(tbl, ctx = DEFAULT_PARSE_CONTEXT) {
36
80
  const properties = parseTableProperties(poFirstChild(tbl, "w:tblPr"));
37
81
  const grid = parseTableGrid(poFirstChild(tbl, "w:tblGrid"));
38
82
  const draftRows = [];
39
- for (const tr of poChildrenWith(tbl, "w:tr")) draftRows.push(parseTableRow(tr, ctx));
83
+ for (const tr of poChildrenThroughSdt(tbl, "w:tr")) {
84
+ if (isDeletedRow(tr)) continue;
85
+ draftRows.push(parseTableRow(tr, ctx));
86
+ }
40
87
  const roles = resolveMergeRoles(draftRows.map((r) => r.cells));
88
+ const rows = draftRows.map((draft, rowIdx) => ({
89
+ properties: draft.properties,
90
+ cells: draft.cells.map((d, cellIdx) => {
91
+ const merge = roles[rowIdx][cellIdx];
92
+ return merge ? {
93
+ ...d.cell,
94
+ properties: {
95
+ ...d.cell.properties,
96
+ merge
97
+ }
98
+ } : d.cell;
99
+ })
100
+ }));
101
+ if (poFirstChild(poFirstChild(tbl, "w:tblPr"), "w:bidiVisual")) return {
102
+ properties,
103
+ grid: [...grid].reverse(),
104
+ rows: rows.map((row) => ({
105
+ ...row,
106
+ cells: [...row.cells].reverse()
107
+ }))
108
+ };
41
109
  return {
42
110
  properties,
43
111
  grid,
44
- rows: draftRows.map((draft, rowIdx) => ({
45
- properties: draft.properties,
46
- cells: draft.cells.map((d, cellIdx) => {
47
- const merge = roles[rowIdx][cellIdx];
48
- return merge ? {
49
- ...d.cell,
50
- properties: {
51
- ...d.cell.properties,
52
- merge
53
- }
54
- } : d.cell;
55
- })
56
- }))
112
+ rows
57
113
  };
58
114
  }
115
+ function poChildrenThroughSdt(node, tag) {
116
+ return childrenThroughSdt(node, tag).map((c) => c.node);
117
+ }
118
+ function childrenThroughSdt(node, tag) {
119
+ const out = [];
120
+ for (const child of poChildren(node)) if (poIs(child, tag)) out.push({ node: child });
121
+ else if (poIs(child, "w:sdt")) {
122
+ const props = sdtRunProperties(child);
123
+ for (const inner of poChildrenWith(poFirstChild(child, "w:sdtContent"), tag)) out.push({
124
+ node: inner,
125
+ ...props ? { sdtRunProps: props } : {}
126
+ });
127
+ }
128
+ return out;
129
+ }
59
130
  function resolveMergeRoles(rows) {
60
131
  const out = rows.map((r) => new Array(r.length).fill(void 0));
61
132
  const colSlots = /* @__PURE__ */ new Map();
@@ -109,11 +180,23 @@ function parseTableProperties(tblPr) {
109
180
  else if (w !== void 0) out.widthPt = twipsToPt(w);
110
181
  if (type && WIDTH_TYPES.has(type)) out.widthType = type;
111
182
  }
183
+ const tblInd = poFirstChild(tblPr, "w:tblInd");
184
+ if (tblInd) {
185
+ const w = poIntAttr(tblInd, "w");
186
+ const type = poAttr(tblInd, "type");
187
+ if (w !== void 0 && (type === void 0 || type === "dxa")) out.indentPt = twipsToPt(w);
188
+ }
189
+ const tblpPr = poFirstChild(tblPr, "w:tblpPr");
190
+ if (tblpPr) out.float = parseTablePosition(tblpPr);
112
191
  const tblLayout = poFirstChild(tblPr, "w:tblLayout");
113
192
  if (tblLayout) {
114
193
  const t = poAttr(tblLayout, "type");
115
194
  if (t === "fixed" || t === "auto") out.layout = t;
116
195
  }
196
+ const tblJc = poVal(poFirstChild(tblPr, "w:jc"));
197
+ if (tblJc === "center") out.alignment = "center";
198
+ else if (tblJc === "right" || tblJc === "end") out.alignment = "right";
199
+ else if (tblJc === "left" || tblJc === "start") out.alignment = "left";
117
200
  const borders = parseBorders(poFirstChild(tblPr, "w:tblBorders"));
118
201
  if (borders) out.borders = borders;
119
202
  const margins = parseCellMargins(poFirstChild(tblPr, "w:tblCellMar"));
@@ -154,15 +237,61 @@ function parseTableGrid(tblGrid) {
154
237
  }
155
238
  return cols;
156
239
  }
240
+ /**
241
+ * §17.13.5.15 — whether a row was deleted, under the reader's accept-all
242
+ * reading of tracked changes (the same one that drops `w:del` runs).
243
+ *
244
+ * Two spellings. `w:trPr/w:del` says it outright. The other is how a producer
245
+ * writes a row whose CONTENT was taken away: every paragraph mark in it carries
246
+ * `w:del` or `w:moveFrom`, which deletes the mark, and a row whose every mark
247
+ * is gone is a row that is gone. TC-table-DnD-move.docx moves a table that way,
248
+ * and accepting only the runs left a ghost of empty bordered rows behind where
249
+ * Word and LibreOffice leave nothing.
250
+ *
251
+ * @param tr The `w:tr` element.
252
+ * @returns Whether to drop the row.
253
+ */
254
+ function isDeletedRow(tr) {
255
+ const trPr = poFirstChild(tr, "w:trPr");
256
+ if (trPr && (poFirstChild(trPr, "w:del") ?? poFirstChild(trPr, "w:moveFrom"))) return true;
257
+ const cells = poChildrenThroughSdt(tr, "w:tc");
258
+ if (cells.length === 0) return false;
259
+ let marks = 0;
260
+ for (const tc of cells) {
261
+ for (const p of poChildrenWith(tc, "w:p")) {
262
+ const rPr = poFirstChild(poFirstChild(p, "w:pPr"), "w:rPr");
263
+ if (!rPr || !(poFirstChild(rPr, "w:del") ?? poFirstChild(rPr, "w:moveFrom"))) return false;
264
+ marks++;
265
+ }
266
+ if (poChildrenWith(tc, "w:tbl").length > 0) return false;
267
+ }
268
+ return marks > 0;
269
+ }
157
270
  function parseTableRow(tr, ctx) {
158
271
  const properties = parseRowProperties(poFirstChild(tr, "w:trPr"));
159
272
  const cells = [];
160
- for (const tc of poChildrenWith(tr, "w:tc")) cells.push(parseTableCell(tc, ctx));
273
+ for (const { node, sdtRunProps } of childrenThroughSdt(tr, "w:tc")) cells.push(parseTableCell(node, ctx, sdtRunProps));
161
274
  return {
162
275
  properties,
163
276
  cells
164
277
  };
165
278
  }
279
+ function parseRowConditional(cnf) {
280
+ if (!cnf) return void 0;
281
+ const bits = poAttr(cnf, "val");
282
+ const bit = (i) => bits?.[i] === "1";
283
+ const flag = (name) => {
284
+ const v = poAttr(cnf, name);
285
+ return v === void 0 ? void 0 : v === "1" || v === "true" || v === "on";
286
+ };
287
+ const firstRow = flag("firstRow") ?? bit(0);
288
+ const lastRow = flag("lastRow") ?? bit(1);
289
+ if (!firstRow && !lastRow) return void 0;
290
+ return {
291
+ ...firstRow ? { firstRow } : {},
292
+ ...lastRow ? { lastRow } : {}
293
+ };
294
+ }
166
295
  function parseRowProperties(trPr) {
167
296
  if (!trPr) return {};
168
297
  const out = {};
@@ -172,17 +301,22 @@ function parseRowProperties(trPr) {
172
301
  const rule = poAttr(trHeight, "hRule");
173
302
  if (val !== void 0) out.height = twipsToPt(val);
174
303
  if (rule && HEIGHT_RULES.has(rule)) out.heightRule = rule;
304
+ else if (val !== void 0) out.heightRule = "atLeast";
175
305
  }
176
306
  if (poFirstChild(trPr, "w:cantSplit")) out.cantSplit = poToggle(poFirstChild(trPr, "w:cantSplit")) ?? true;
177
307
  if (poFirstChild(trPr, "w:tblHeader")) out.isHeader = poToggle(poFirstChild(trPr, "w:tblHeader")) ?? true;
308
+ const before = poIntAttr(poFirstChild(trPr, "w:gridBefore"), "val");
309
+ if (before !== void 0 && before > 0) out.gridBefore = before;
310
+ const cnf = parseRowConditional(poFirstChild(trPr, "w:cnfStyle"));
311
+ if (cnf) out.conditional = cnf;
178
312
  return out;
179
313
  }
180
- function parseTableCell(tc, ctx) {
314
+ function parseTableCell(tc, ctx, sdtRunProps) {
181
315
  const { properties, vMerge } = parseCellProperties(poFirstChild(tc, "w:tcPr"));
182
316
  return {
183
317
  cell: {
184
318
  properties,
185
- content: parseBodyElements(poChildren(tc), ctx)
319
+ content: parseBodyElements(poChildren(tc), ctx, void 0, sdtRunProps)
186
320
  },
187
321
  ...vMerge ? { vMerge } : {}
188
322
  };
@@ -194,7 +328,12 @@ function parseCellProperties(tcPr) {
194
328
  const tcW = poFirstChild(tcPr, "w:tcW");
195
329
  if (tcW) {
196
330
  const w = poIntAttr(tcW, "w");
197
- if (w !== void 0) out.width = twipsToPt(w);
331
+ const type = poAttr(tcW, "type");
332
+ if (type === "pct" || type === "dxa" || type === "auto" || type === "nil") out.widthType = type;
333
+ if (w !== void 0) {
334
+ if (type === "pct") out.widthFraction = w / 5e3;
335
+ else if (type !== "auto" && type !== "nil") out.width = twipsToPt(w);
336
+ }
198
337
  }
199
338
  const gridSpan = poFirstChild(tcPr, "w:gridSpan");
200
339
  if (gridSpan) {
@@ -212,11 +351,21 @@ function parseCellProperties(tcPr) {
212
351
  const fill = poAttr(shd, "fill");
213
352
  if (fill && fill !== "auto" && /^[0-9A-Fa-f]{6}$/.test(fill)) out.shading = { colorHex: fill.toUpperCase() };
214
353
  }
354
+ const vAlign = poVal(poFirstChild(tcPr, "w:vAlign"));
355
+ if (vAlign === "center" || vAlign === "bottom" || vAlign === "top") out.verticalAlign = vAlign;
356
+ if (poFirstChild(tcPr, "w:hideMark")) out.hideMark = true;
215
357
  return {
216
358
  properties: out,
217
359
  ...rawVMerge ? { vMerge: rawVMerge } : {}
218
360
  };
219
361
  }
362
+ /**
363
+ * A `w:tcBorders` / `w:tblBorders` / §17.6.10 `w:pgBorders` element: the rule on
364
+ * each edge. Every one of them is spelled the same way.
365
+ *
366
+ * @param node The borders element, or `undefined`.
367
+ * @returns The edges that name a rule, or `undefined` when none do.
368
+ */
220
369
  function parseBorders(node) {
221
370
  if (!node) return void 0;
222
371
  const out = {};
@@ -236,12 +385,15 @@ function parseBorders(node) {
236
385
  }
237
386
  function parseBorder(node) {
238
387
  if (!node) return void 0;
239
- const val = poVal(node);
240
- if (!val || !BORDER_STYLES.has(val)) return void 0;
388
+ const raw = poVal(node);
389
+ if (!raw) return void 0;
390
+ const val = raw === "nil" ? "none" : BORDER_STYLES.has(raw) ? raw : MULTI_LINE_BORDERS.has(raw) ? "double" : "single";
241
391
  const sz = poIntAttr(node, "sz");
242
392
  const color = poAttr(node, "color");
243
393
  const out = { style: val };
244
- if (sz !== void 0) out.width = eighthPtToPt(sz);
394
+ if (sz !== void 0) out.width = eighthPtToPt(MULTI_LINE_BORDERS.has(raw) ? sz * 3 : sz);
395
+ const space = poIntAttr(node, "space");
396
+ if (space !== void 0) out.spacePt = pt(space);
245
397
  if (color && color !== "auto" && /^[0-9A-Fa-f]{6}$/.test(color)) out.colorHex = color.toUpperCase();
246
398
  return out;
247
399
  }
@@ -252,11 +404,11 @@ function parseCellMargins(node) {
252
404
  const bottom = poIntAttr(poFirstChild(node, "w:bottom"), "w");
253
405
  const left = poIntAttr(poFirstChild(node, "w:left") ?? poFirstChild(node, "w:start"), "w");
254
406
  const right = poIntAttr(poFirstChild(node, "w:right") ?? poFirstChild(node, "w:end"), "w");
255
- if (top !== void 0) out.top = twipsToPt(top);
256
- if (bottom !== void 0) out.bottom = twipsToPt(bottom);
257
- if (left !== void 0) out.left = twipsToPt(left);
258
- if (right !== void 0) out.right = twipsToPt(right);
407
+ if (top !== void 0) out.top = twipsToPt(Math.max(0, top));
408
+ if (bottom !== void 0) out.bottom = twipsToPt(Math.max(0, bottom));
409
+ if (left !== void 0) out.left = twipsToPt(Math.max(0, left));
410
+ if (right !== void 0) out.right = twipsToPt(Math.max(0, right));
259
411
  return Object.keys(out).length > 0 ? out : void 0;
260
412
  }
261
413
  //#endregion
262
- export { parseTable };
414
+ export { parseBorders, parseTable };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reamkit",
3
- "version": "1.15.4",
3
+ "version": "1.17.0",
4
4
  "description": "Ream — convert DOCX, XLSX, PPTX and PDF to PDF, SVG, HTML, DOCX and XLSX, built from scratch on the ECMA-376 and ISO 32000 specifications. Parse once, convert anywhere.",
5
5
  "license": "MIT",
6
6
  "author": "Alex Krassavin <info@reamkit.dev>",
@@ -60,7 +60,7 @@
60
60
  "test:types": "tsc -p tsconfig.json --noEmit",
61
61
  "test:eslint": "eslint .",
62
62
  "test:build": "publint --strict && attw --pack . --profile esm-only",
63
- "typecheck": "tsc -p tsconfig.json --noEmit",
63
+ "typecheck": "tsc -p tsconfig.eslint.json --noEmit",
64
64
  "lint": "eslint .",
65
65
  "lint:fix": "eslint . --fix",
66
66
  "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/**/*.ts\"",
@@ -73,7 +73,11 @@
73
73
  "corpus:roundtrip:xlsx": "tsx scripts/corpus/xlsx-roundtrip.ts",
74
74
  "corpus:xlsx:invariants": "tsx scripts/corpus/xlsx-invariants.ts",
75
75
  "corpus:fixtures": "tsx scripts/corpus/sync-real-fixtures.ts",
76
- "corpus:golden": "tsx scripts/corpus/make-golden.ts"
76
+ "corpus:golden": "tsx scripts/corpus/make-golden.ts",
77
+ "corpus:visual": "tsx scripts/corpus/visual-diff.ts",
78
+ "corpus:scout": "tsx scripts/corpus/scout.ts",
79
+ "corpus:pixel": "tsx scripts/corpus/pixel-scout.ts",
80
+ "typecheck:dist": "tsc -p tsconfig.json --noEmit"
77
81
  },
78
82
  "dependencies": {
79
83
  "fast-xml-parser": "5.7.0",