reamkit 1.31.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 (83) hide show
  1. package/README.md +10 -0
  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 +1 -0
  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/document-model/index.d.ts +1 -1
  10. package/dist/esm/core/document-model/types.d.ts +112 -0
  11. package/dist/esm/core/drawingml/chart-geometry.d.ts +21 -8
  12. package/dist/esm/core/drawingml/chart-geometry.js +198 -108
  13. package/dist/esm/core/drawingml/chart-parser.js +73 -2
  14. package/dist/esm/core/drawingml/colors.d.ts +6 -0
  15. package/dist/esm/core/drawingml/colors.js +23 -3
  16. package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
  17. package/dist/esm/core/drawingml/shape-render.js +4 -2
  18. package/dist/esm/core/font/measure.d.ts +9 -0
  19. package/dist/esm/core/font/measure.js +9 -1
  20. package/dist/esm/core/fonts/families.js +4 -2
  21. package/dist/esm/core/fonts/scripts.js +4 -2
  22. package/dist/esm/core/ir/flow.d.ts +6 -0
  23. package/dist/esm/core/ir/sheet.d.ts +43 -0
  24. package/dist/esm/core/number-format.js +16 -11
  25. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  26. package/dist/esm/core/spreadsheet-model/types.d.ts +96 -0
  27. package/dist/esm/core/style-cascade/resolver.js +4 -2
  28. package/dist/esm/excel/column-bands.d.ts +39 -6
  29. package/dist/esm/excel/column-bands.js +41 -17
  30. package/dist/esm/excel/comments-parser.js +19 -0
  31. package/dist/esm/excel/conditional-format.d.ts +7 -3
  32. package/dist/esm/excel/conditional-format.js +77 -39
  33. package/dist/esm/excel/formula/context.d.ts +18 -0
  34. package/dist/esm/excel/formula/eval.js +51 -0
  35. package/dist/esm/excel/formula/index.d.ts +2 -2
  36. package/dist/esm/excel/formula/lexer.d.ts +1 -1
  37. package/dist/esm/excel/formula/lexer.js +41 -0
  38. package/dist/esm/excel/formula/parser.d.ts +25 -0
  39. package/dist/esm/excel/formula/parser.js +95 -0
  40. package/dist/esm/excel/print-model.d.ts +38 -1
  41. package/dist/esm/excel/print-model.js +522 -80
  42. package/dist/esm/excel/shared-strings-parser.d.ts +9 -0
  43. package/dist/esm/excel/shared-strings-parser.js +11 -1
  44. package/dist/esm/excel/sheet-drawing.d.ts +38 -2
  45. package/dist/esm/excel/sheet-drawing.js +47 -13
  46. package/dist/esm/excel/sheet-shape-parser.d.ts +6 -2
  47. package/dist/esm/excel/sheet-shape-parser.js +18 -6
  48. package/dist/esm/excel/sheet-to-flow.d.ts +17 -0
  49. package/dist/esm/excel/sheet-to-flow.js +434 -47
  50. package/dist/esm/excel/styles-parser.d.ts +9 -0
  51. package/dist/esm/excel/styles-parser.js +80 -26
  52. package/dist/esm/excel/table-parser.js +5 -0
  53. package/dist/esm/excel/table-style-presets.d.ts +26 -0
  54. package/dist/esm/excel/table-style-presets.js +1555 -0
  55. package/dist/esm/excel/table-style.d.ts +13 -0
  56. package/dist/esm/excel/table-style.js +75 -0
  57. package/dist/esm/excel/tint.d.ts +10 -0
  58. package/dist/esm/excel/tint.js +75 -0
  59. package/dist/esm/excel/vml-drawing.d.ts +41 -3
  60. package/dist/esm/excel/vml-drawing.js +143 -10
  61. package/dist/esm/excel/worksheet-parser.js +89 -15
  62. package/dist/esm/excel/xls/biff-reader.js +10 -2
  63. package/dist/esm/excel/xlsx-reader.js +195 -14
  64. package/dist/esm/excel/xlsx-writer.js +8 -7
  65. package/dist/esm/html/html-writer.js +202 -20
  66. package/dist/esm/layout/fit-pages.d.ts +11 -0
  67. package/dist/esm/layout/fit-pages.js +86 -0
  68. package/dist/esm/layout/line-paint.d.ts +58 -0
  69. package/dist/esm/layout/line-paint.js +107 -0
  70. package/dist/esm/layout/page-doc.d.ts +51 -0
  71. package/dist/esm/layout/page-doc.js +141 -1
  72. package/dist/esm/layout/styled-layout.d.ts +7 -0
  73. package/dist/esm/layout/styled-layout.js +274 -51
  74. package/dist/esm/layout/turned-section.js +14 -2
  75. package/dist/esm/markdown/markdown-writer.js +17 -6
  76. package/dist/esm/pdf/styled-page-emitter.js +49 -71
  77. package/dist/esm/pdf/styled-page-renderer.js +1 -1
  78. package/dist/esm/pdf-reader/font.js +3 -3
  79. package/dist/esm/svg/svg-text.d.ts +58 -0
  80. package/dist/esm/svg/svg-text.js +322 -0
  81. package/dist/esm/svg/svg-writer.js +135 -71
  82. package/dist/esm/word/docx-writer.js +11 -2
  83. package/package.json +5 -1
@@ -1,8 +1,10 @@
1
+ import { pageLayers, paintPlan } from "../layout/page-doc.js";
1
2
  import { FEATURES } from "../core/ir/features.js";
2
3
  import { svgPathData } from "../core/vector.js";
3
4
  import { gradientSvgDef } from "../core/drawingml/shape-render.js";
4
- import { paintPlan } from "../layout/page-doc.js";
5
+ import { BORDER_DASHES, washVeil } from "../layout/line-paint.js";
5
6
  import { toBase64 } from "../core/bytes.js";
7
+ import { SvgGlyphs, emitSvgLine } from "./svg-text.js";
6
8
  //#region src/svg/svg-writer.ts
7
9
  var PAGE_GAP = 12;
8
10
  /**
@@ -23,17 +25,25 @@ function writeSvg(laid, opts = {}) {
23
25
  const width = Math.max(1, ...laid.pages.map((p) => p.width));
24
26
  const height = laid.pages.reduce((s, p) => s + p.height, 0) + gap * Math.max(0, laid.pages.length - 1);
25
27
  const parts = [];
26
- parts.push(`<svg xmlns="http://www.w3.org/2000/svg" width="${fmt(width)}" height="${fmt(height)}" viewBox="0 0 ${fmt(width)} ${fmt(height)}">`);
28
+ const ids = { n: 0 };
29
+ const glyphs = new SvgGlyphs();
30
+ const ctx = {
31
+ glyphs,
32
+ ids,
33
+ laid,
34
+ image: (out, placed) => emitPlaced(out, placed, laid, ids),
35
+ shape: (out, shape) => emitShape(out, shape, ids)
36
+ };
27
37
  let yOffset = 0;
28
- const idc = { n: 0 };
29
38
  laid.pages.forEach((page, i) => {
30
39
  parts.push(`<g transform="translate(0 ${fmt(yOffset)})" data-page="${i + 1}">`);
31
40
  parts.push(`<rect x="0" y="0" width="${fmt(page.width)}" height="${fmt(page.height)}" fill="#ffffff" stroke="#cccccc"/>`);
32
- emitPage(parts, page, laid, losses, idc);
41
+ emitPage(parts, page, ctx);
33
42
  parts.push("</g>");
34
43
  yOffset += page.height + gap;
35
44
  });
36
45
  parts.push("</svg>");
46
+ parts.unshift(`<svg xmlns="http://www.w3.org/2000/svg" width="${fmt(width)}" height="${fmt(height)}" viewBox="0 0 ${fmt(width)} ${fmt(height)}">`, glyphs.defsXml());
37
47
  return {
38
48
  bytes: new TextEncoder().encode(parts.join("\n")),
39
49
  losses
@@ -54,11 +64,24 @@ var svgWriter = {
54
64
  ]),
55
65
  write: (doc, opts) => writeSvg(doc, opts ?? {})
56
66
  };
57
- function emitPage(out, page, laid, losses, idc) {
58
- const plan = paintPlan(page.commands);
59
- for (const item of plan.behind) emitPageItem(out, item, laid, losses, idc);
67
+ function emitPage(out, page, ctx) {
68
+ for (const layer of pageLayers(page.commands)) {
69
+ const w = layer.window;
70
+ if (!w) {
71
+ emitLayer(out, layer.items, ctx);
72
+ continue;
73
+ }
74
+ const id = `win${String(ctx.ids.n++)}`;
75
+ out.push(`<clipPath id="${id}"><rect x="${fmt(w.x)}" y="${fmt(w.y)}" width="${fmt(w.width)}" height="${fmt(w.height)}"/></clipPath>`, `<g clip-path="url(#${id})">`);
76
+ emitLayer(out, layer.items, ctx);
77
+ out.push("</g>");
78
+ }
79
+ }
80
+ function emitLayer(out, commands, ctx) {
81
+ const plan = paintPlan(commands);
82
+ for (const item of plan.behind) emitPageItem(out, item, ctx);
60
83
  for (const f of plan.fills) out.push(`<rect x="${fmt(f.x)}" y="${fmt(f.y)}" width="${fmt(f.width)}" height="${fmt(f.height)}" fill="#${f.fillColorHex}"/>`);
61
- for (const img of plan.images) emitImage(out, img, laid, idc);
84
+ for (const img of plan.images) emitImage(out, img, ctx);
62
85
  for (const b of plan.borders) {
63
86
  const x2 = b.x + b.width;
64
87
  const yTop = b.y;
@@ -84,104 +107,148 @@ function emitPage(out, page, laid, losses, idc) {
84
107
  x2,
85
108
  yBottom
86
109
  ];
87
- out.push(`<line x1="${fmt(ax)}" y1="${fmt(ay)}" x2="${fmt(bx)}" y2="${fmt(by)}" stroke="#${b.borderColorHex}" stroke-width="${fmt(b.borderSizePt)}"${b.borderStyle === "dashed" ? " stroke-dasharray=\"8 2.5\"" : b.borderStyle === "dashSmallGap" ? " stroke-dasharray=\"3 1\"" : b.borderStyle === "dotted" ? " stroke-dasharray=\"0.5 1\"" : ""}/>`);
110
+ const dash = b.borderStyle !== void 0 ? BORDER_DASHES.get(b.borderStyle) : void 0;
111
+ out.push(`<line x1="${fmt(ax)}" y1="${fmt(ay)}" x2="${fmt(bx)}" y2="${fmt(by)}" stroke="#${b.borderColorHex}" stroke-width="${fmt(b.borderSizePt)}"${dash ? ` stroke-dasharray="${dash.map(fmt).join(" ")}"` : ""}/>`);
88
112
  }
89
- for (const sh of plan.shapes) emitShape(out, sh.shape, idc);
90
- for (const picture of plan.pictures) for (const item of picture) emitPageItem(out, item, laid, losses, idc);
91
- for (const run of plan.ordered) for (const item of run) emitPageItem(out, item, laid, losses, idc);
92
- for (const t of plan.lines) emitTextLine(out, t, losses, idc);
113
+ for (const sh of plan.shapes) emitShape(out, sh.shape, ctx.ids);
114
+ for (const picture of plan.pictures) for (const item of picture) emitPageItem(out, item, ctx);
115
+ for (const run of plan.ordered) for (const item of run) emitPageItem(out, item, ctx);
116
+ for (const t of plan.lines) emitSvgLine(out, t, ctx);
93
117
  }
94
118
  /**
95
- * One picture. §20.1.8.23 `a:duotone` recolours it between two colours — its
96
- * dark end and its light end — which is a luminance-to-two-tone map, and SVG
97
- * states exactly that: luminance into every channel, then a two-entry transfer
98
- * table per channel.
119
+ * One picture item, its placement and its effects: §20.1.8.23 `a:duotone`
120
+ * recolours it between its dark end and its light end — luminance into every
121
+ * channel, then a two-entry transfer table per channel — and a picture FILL
122
+ * (§20.1.8.14) is clipped to the outline of the shape it fills.
99
123
  *
100
124
  * @param out The SVG fragment sink.
101
125
  * @param img The image item.
102
- * @param laid The laid-out document, for the image's data URI.
103
- * @param idc The shared id counter, for the filter's id.
126
+ * @param ctx The document, its id counter, its drawing.
104
127
  */
105
- function emitImage(out, img, laid, idc) {
106
- const href = imageHref(img.imageResourceName, laid);
107
- if (!href) return;
108
- let filter = "";
128
+ function emitImage(out, img, ctx) {
129
+ let filter;
109
130
  if (img.duotone) {
110
- const id = `duo${String(idc.n++)}`;
131
+ const id = `duo${String(ctx.ids.n++)}`;
111
132
  const chan = (hex, at) => parseInt(hex.slice(at, at + 2), 16) / 255;
112
133
  const table = (at) => `${fmt(chan(img.duotone.shadowHex, at))} ${fmt(chan(img.duotone.highlightHex, at))}`;
113
134
  out.push(`<filter id="${id}" color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/><feComponentTransfer><feFuncR type="table" tableValues="${table(0)}"/><feFuncG type="table" tableValues="${table(2)}"/><feFuncB type="table" tableValues="${table(4)}"/></feComponentTransfer></filter>`);
114
- filter = ` filter="url(#${id})"`;
135
+ filter = id;
136
+ }
137
+ let clipped = false;
138
+ if (img.clip) {
139
+ const id = `pic${String(ctx.ids.n++)}`;
140
+ const [a, b, c, d, e, f] = img.clip.transform;
141
+ out.push(`<clipPath id="${id}">` + img.clip.paths.map((p) => `<path d="${pathData(p.segments)}" transform="matrix(${fmt(a)} ${fmt(b)} ${fmt(c)} ${fmt(d)} ${fmt(e)} ${fmt(f)})"/>`).join("") + `</clipPath>`, `<g clip-path="url(#${id})">`);
142
+ clipped = true;
143
+ }
144
+ emitPlaced(out, {
145
+ resourceName: img.imageResourceName,
146
+ x: img.x,
147
+ y: img.y,
148
+ width: img.width,
149
+ height: img.height,
150
+ ...img.crop ? { crop: img.crop } : {},
151
+ ...img.rotationDeg !== void 0 ? { rotationDeg: img.rotationDeg } : {},
152
+ ...img.flipH === true ? { flipH: true } : {},
153
+ ...img.flipV === true ? { flipV: true } : {},
154
+ ...img.alpha !== void 0 ? { alpha: img.alpha } : {}
155
+ }, ctx.laid, ctx.ids, filter, img.wash);
156
+ if (clipped) out.push("</g>");
157
+ }
158
+ /**
159
+ * A picture in its box, as the PDF places one: turned about the box's centre
160
+ * (clockwise, as DrawingML measures), mirrored about it, and — cropped by
161
+ * `a:srcRect` (§20.1.8.55) — drawn larger than the box with the box as its clip,
162
+ * so the part that was kept fills it. A wash (§14.1.2.10) is the veil laid over
163
+ * it.
164
+ */
165
+ function emitPlaced(out, placed, laid, ids, filter, wash) {
166
+ const href = imageHref(placed.resourceName, laid);
167
+ if (!href) return;
168
+ const { x, y, width, height } = placed;
169
+ const cx = x + width / 2;
170
+ const cy = y + height / 2;
171
+ const turns = [];
172
+ if (placed.rotationDeg) turns.push(`rotate(${fmt(placed.rotationDeg)} ${fmt(cx)} ${fmt(cy)})`);
173
+ if (placed.flipH === true || placed.flipV === true) {
174
+ const sx = placed.flipH === true ? -1 : 1;
175
+ const sy = placed.flipV === true ? -1 : 1;
176
+ turns.push(`matrix(${String(sx)} 0 0 ${String(sy)} ${fmt(sx === -1 ? 2 * cx : 0)} ${fmt(sy === -1 ? 2 * cy : 0)})`);
115
177
  }
116
- out.push(`<image x="${fmt(img.x)}" y="${fmt(img.y)}" width="${fmt(img.width)}" height="${fmt(img.height)}" href="${href}" preserveAspectRatio="none"${filter}/>`);
178
+ const open = turns.length > 0 ? `<g transform="${turns.join(" ")}">` : "";
179
+ if (open) out.push(open);
180
+ const crop = placed.crop;
181
+ let clipId;
182
+ let drawn = {
183
+ x,
184
+ y,
185
+ width,
186
+ height
187
+ };
188
+ if (crop) {
189
+ const keptW = 1 - crop.left - crop.right;
190
+ const keptH = 1 - crop.top - crop.bottom;
191
+ if (keptW > 0 && keptH > 0) {
192
+ const fullW = width / keptW;
193
+ const fullH = height / keptH;
194
+ drawn = {
195
+ x: x - crop.left * fullW,
196
+ y: y - crop.top * fullH,
197
+ width: fullW,
198
+ height: fullH
199
+ };
200
+ clipId = `crop${String(ids.n++)}`;
201
+ out.push(`<clipPath id="${clipId}"><rect x="${fmt(x)}" y="${fmt(y)}" width="${fmt(width)}" height="${fmt(height)}"/></clipPath>`);
202
+ }
203
+ }
204
+ const alpha = placed.alpha !== void 0 && placed.alpha < 1 ? ` opacity="${fmt(placed.alpha)}"` : "";
205
+ out.push(`<image x="${fmt(drawn.x)}" y="${fmt(drawn.y)}" width="${fmt(drawn.width)}" height="${fmt(drawn.height)}" href="${href}" preserveAspectRatio="none"${clipId ? ` clip-path="url(#${clipId})"` : ""}${filter ? ` filter="url(#${filter})"` : ""}${alpha}/>`);
206
+ const veil = washVeil(wash);
207
+ if (veil) {
208
+ const grey = Math.round(veil.grey * 255).toString(16).padStart(2, "0");
209
+ out.push(`<rect x="${fmt(x)}" y="${fmt(y)}" width="${fmt(width)}" height="${fmt(height)}" fill="#${grey}${grey}${grey}" opacity="${fmt(veil.alpha)}"/>`);
210
+ }
211
+ if (open) out.push("</g>");
117
212
  }
118
213
  /**
119
214
  * One page item, whatever its kind — for the runs that paint in their own order
120
215
  * (what stands behind the content, and a picture's own primitives) rather than
121
216
  * in the by-kind passes.
122
217
  *
123
- * @param out The SVG fragment sink.
124
- * @param item The item to draw.
125
- * @param laid The laid-out document, for image resources.
126
- * @param losses Where an unrenderable item records itself.
127
- * @param idc The shared id counter for generated defs.
218
+ * @param out The SVG fragment sink.
219
+ * @param item The item to draw.
220
+ * @param ctx The document, its glyphs, its id counter and its drawing.
128
221
  */
129
- function emitPageItem(out, item, laid, losses, idc) {
222
+ function emitPageItem(out, item, ctx) {
130
223
  if (item.type === "shape") {
131
- emitShape(out, item.shape, idc);
224
+ emitShape(out, item.shape, ctx.ids);
132
225
  return;
133
226
  }
134
227
  if (item.type === "line") {
135
- emitTextLine(out, item, losses, idc);
228
+ emitSvgLine(out, item, ctx);
136
229
  return;
137
230
  }
138
231
  if (item.type === "image") {
139
- emitImage(out, item, laid, idc);
232
+ emitImage(out, item, ctx);
140
233
  return;
141
234
  }
142
235
  if (item.type === "fill") out.push(`<rect x="${fmt(item.x)}" y="${fmt(item.y)}" width="${fmt(item.width)}" height="${fmt(item.height)}" fill="#${item.fillColorHex}"/>`);
143
236
  }
144
- function emitTextLine(out, item, losses, idc) {
145
- const clip = item.clip;
146
- if (clip) {
147
- const id = `clip${idc.n++}`;
148
- out.push(`<clipPath id="${id}"><rect x="${fmt(clip.x)}" y="${fmt(clip.y)}" width="${fmt(clip.width)}" height="${fmt(clip.height)}"/></clipPath>`, `<g clip-path="url(#${id})">`);
149
- }
150
- const y = item.baselineY;
151
- let x = item.originX;
152
- const rot = item.rotationDeg ? ` transform="rotate(${fmt(-item.rotationDeg)} ${fmt(item.originX)} ${fmt(item.baselineY)})"` : "";
153
- for (const tok of item.line.tokens) {
154
- if (tok.kind === "image") {
155
- x += tok.widthPt;
156
- continue;
157
- }
158
- if (tok.kind === "math") {
159
- losses.push({
160
- severity: "dropped",
161
- feature: FEATURES.math,
162
- detail: "inline math box not rendered by the SVG writer (v0)"
163
- });
164
- x += tok.widthPt;
165
- continue;
166
- }
167
- if (tok.text.trim().length > 0) out.push(`<text x="${fmt(x)}" y="${fmt(y)}"${rot} font-family="sans-serif" font-size="${fmt(tok.fontSizePt)}" fill="#${tok.resolvedRun.colorHex}">${escapeXml(tok.text)}</text>`);
168
- x += tok.widthPt;
169
- }
170
- if (clip) out.push("</g>");
171
- }
172
- function emitShape(out, shape, idc) {
237
+ function emitShape(out, shape, ids) {
173
238
  const [a, b, c, d, e, f] = shape.transform;
174
239
  const transform = `matrix(${fmt(a)} ${fmt(b)} ${fmt(c)} ${fmt(d)} ${fmt(e)} ${fmt(f)})`;
175
240
  let fill;
176
241
  if (shape.fillGradient) {
177
- const id = `grad${idc.n++}`;
242
+ const id = `grad${String(ids.n++)}`;
178
243
  out.push(gradientSvgDef(id, shape.fillGradient));
179
244
  fill = `url(#${id})`;
180
245
  } else fill = shape.fillColorHex ? `#${shape.fillColorHex}` : "none";
181
- const stroke = shape.stroke ? ` stroke="#${shape.stroke.colorHex}" stroke-width="${fmt(shape.stroke.widthPt)}"` : "";
246
+ const fillAlpha = shape.fillAlpha !== void 0 && shape.fillAlpha < 1 && fill !== "none" ? ` fill-opacity="${fmt(shape.fillAlpha)}"` : "";
247
+ const s = shape.stroke;
248
+ const stroke = s ? ` stroke="#${s.colorHex}" stroke-width="${fmt(s.widthPt)}"` + (s.cap !== void 0 ? ` stroke-linecap="${s.cap}"` : "") + (s.join !== void 0 ? ` stroke-linejoin="${s.join}"` : "") + (s.dash && s.dash.length > 0 ? ` stroke-dasharray="${s.dash.map(fmt).join(" ")}"` : "") : "";
182
249
  const shadow = shape.shadow;
183
250
  if (shadow) {
184
- const id = `shadow${idc.n++}`;
251
+ const id = `shadow${String(ids.n++)}`;
185
252
  if (shadow.blurPt > 0) out.push(`<filter id="${id}" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="${fmt(shadow.blurPt / 2)}"/></filter>`);
186
253
  const shTransform = `matrix(${fmt(a)} ${fmt(b)} ${fmt(c)} ${fmt(d)} ${fmt(e + shadow.dxPt)} ${fmt(f + shadow.dyPt)})`;
187
254
  out.push(`<g opacity="${fmt(shadow.alpha)}"${shadow.blurPt > 0 ? ` filter="url(#${id})"` : ""}>`);
@@ -194,7 +261,7 @@ function emitShape(out, shape, idc) {
194
261
  for (const path of shape.paths) {
195
262
  const d2 = pathData(path.segments);
196
263
  const rule = path.fillRule === "evenodd" ? " fill-rule=\"evenodd\"" : "";
197
- out.push(`<path d="${d2}" fill="${fill}"${rule}${stroke} transform="${transform}"/>`);
264
+ out.push(`<path d="${d2}" fill="${fill}"${fillAlpha}${rule}${stroke} transform="${transform}"/>`);
198
265
  }
199
266
  }
200
267
  function pathData(segments) {
@@ -209,9 +276,6 @@ function imageHref(resourceName, laid) {
209
276
  return `data:${res.prepared.mimeType};base64,${toBase64(bytes)}`;
210
277
  }
211
278
  }
212
- function escapeXml(s) {
213
- return s.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
214
- }
215
279
  function fmt(n) {
216
280
  const r = Math.round(n * 100) / 100;
217
281
  return Object.is(r, -0) ? "0" : String(r);
@@ -156,7 +156,8 @@ function writeDocx(flow) {
156
156
  familiesUsed: /* @__PURE__ */ new Map(),
157
157
  ...flow.faceOutlines ? { faceOutlines: flow.faceOutlines } : {},
158
158
  facesUsed: /* @__PURE__ */ new Set(),
159
- zRanks: zRanksOf(flow)
159
+ zRanks: zRanksOf(flow),
160
+ overlaid: /* @__PURE__ */ new Set()
160
161
  };
161
162
  const docScope = newScope();
162
163
  const extraParts = [];
@@ -355,7 +356,10 @@ function zRanksOf(flow) {
355
356
  for (const el of blocks) {
356
357
  const z = el.kind === "image" ? el.image.float?.zOrder : el.kind === "shape" ? el.shape.float?.zOrder : el.kind === "chart" ? el.chart.float?.zOrder : void 0;
357
358
  if (z !== void 0 && Number.isFinite(z)) seen.add(z);
358
- if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
359
+ if (el.kind === "table") {
360
+ for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
361
+ visit(el.table.overlay?.drawings ?? []);
362
+ }
359
363
  }
360
364
  };
361
365
  visit(flow.body);
@@ -593,6 +597,11 @@ function emitBlock(out, el, losses, state, scope, closingSectPr) {
593
597
  }
594
598
  if (el.kind === "table") {
595
599
  out.push(tableXml(el.table, losses, state, scope));
600
+ for (const drawing of el.table.overlay?.drawings ?? []) {
601
+ if (state.overlaid.has(drawing)) continue;
602
+ state.overlaid.add(drawing);
603
+ emitBlock(out, drawing, losses, state, scope);
604
+ }
596
605
  if (closingSectPr) out.push(`<w:p><w:pPr>${closingSectPr}</w:pPr></w:p>`);
597
606
  return;
598
607
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reamkit",
3
- "version": "1.31.0",
3
+ "version": "1.32.0",
4
4
  "description": "Ream — convert DOCX, XLSX, PPTX, PDF and legacy DOC, XLS and PPT to PDF, SVG, HTML, Markdown, 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>",
@@ -84,6 +84,10 @@
84
84
  "corpus:pixel": "tsx scripts/corpus/pixel-scout.ts",
85
85
  "stand": "tsx stand/serve.ts",
86
86
  "stand:build": "tsx stand/build.ts",
87
+ "stand:html": "tsx stand/serve.ts html",
88
+ "stand:html:build": "tsx stand/html-build.ts",
89
+ "stand:svg": "tsx stand/serve.ts svg",
90
+ "stand:svg:build": "tsx stand/svg-build.ts",
87
91
  "typecheck:dist": "tsc -p tsconfig.json --noEmit"
88
92
  },
89
93
  "dependencies": {