@opengeni/artifact-tool 0.2.2

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 (203) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +104 -0
  3. package/dist/bounded-zip.d.ts +28 -0
  4. package/dist/chunk-4BSENMAE.js +39 -0
  5. package/dist/chunk-4BSENMAE.js.map +1 -0
  6. package/dist/chunk-4FRFNNBM.js +276 -0
  7. package/dist/chunk-4FRFNNBM.js.map +1 -0
  8. package/dist/chunk-5XIA6KHZ.js +49 -0
  9. package/dist/chunk-5XIA6KHZ.js.map +1 -0
  10. package/dist/chunk-66CT2ZRP.js +2229 -0
  11. package/dist/chunk-66CT2ZRP.js.map +1 -0
  12. package/dist/chunk-72SYDU4O.js +319 -0
  13. package/dist/chunk-72SYDU4O.js.map +1 -0
  14. package/dist/chunk-B2ZJRZDA.js +279 -0
  15. package/dist/chunk-B2ZJRZDA.js.map +1 -0
  16. package/dist/chunk-DCTLTNPW.js +2458 -0
  17. package/dist/chunk-DCTLTNPW.js.map +1 -0
  18. package/dist/chunk-FGZM4FQ6.js +473 -0
  19. package/dist/chunk-FGZM4FQ6.js.map +1 -0
  20. package/dist/chunk-HTJBLBDE.js +591 -0
  21. package/dist/chunk-HTJBLBDE.js.map +1 -0
  22. package/dist/chunk-IYFXZQ34.js +5306 -0
  23. package/dist/chunk-IYFXZQ34.js.map +1 -0
  24. package/dist/chunk-J6N7IR44.js +172 -0
  25. package/dist/chunk-J6N7IR44.js.map +1 -0
  26. package/dist/chunk-N2IHNWBS.js +1002 -0
  27. package/dist/chunk-N2IHNWBS.js.map +1 -0
  28. package/dist/chunk-NAG44K67.js +549 -0
  29. package/dist/chunk-NAG44K67.js.map +1 -0
  30. package/dist/chunk-O24KTEGP.js +33 -0
  31. package/dist/chunk-O24KTEGP.js.map +1 -0
  32. package/dist/chunk-PI2AZVGK.js +2074 -0
  33. package/dist/chunk-PI2AZVGK.js.map +1 -0
  34. package/dist/chunk-Q2FFHEY7.js +40 -0
  35. package/dist/chunk-Q2FFHEY7.js.map +1 -0
  36. package/dist/chunk-RBGQADXA.js +57 -0
  37. package/dist/chunk-RBGQADXA.js.map +1 -0
  38. package/dist/chunk-U5G2BKM2.js +613 -0
  39. package/dist/chunk-U5G2BKM2.js.map +1 -0
  40. package/dist/chunk-VOJHMSM3.js +39 -0
  41. package/dist/chunk-VOJHMSM3.js.map +1 -0
  42. package/dist/chunk-VUNV25KB.js +16 -0
  43. package/dist/chunk-VUNV25KB.js.map +1 -0
  44. package/dist/chunk-XIFUNVBO.js +101 -0
  45. package/dist/chunk-XIFUNVBO.js.map +1 -0
  46. package/dist/chunk-ZCJYRJGB.js +489 -0
  47. package/dist/chunk-ZCJYRJGB.js.map +1 -0
  48. package/dist/chunk-ZZXPEE4S.js +2260 -0
  49. package/dist/chunk-ZZXPEE4S.js.map +1 -0
  50. package/dist/document-docx-api.d.ts +49 -0
  51. package/dist/document-docx-codec.d.ts +9 -0
  52. package/dist/document-docx-codec.js +4765 -0
  53. package/dist/document-docx-codec.js.map +1 -0
  54. package/dist/document-docx-import.d.ts +261 -0
  55. package/dist/document-docx-packer.d.ts +18 -0
  56. package/dist/document-docx-state.d.ts +12 -0
  57. package/dist/document-render.d.ts +3 -0
  58. package/dist/document-render.js +492 -0
  59. package/dist/document-render.js.map +1 -0
  60. package/dist/document.d.ts +432 -0
  61. package/dist/document.js +37 -0
  62. package/dist/document.js.map +1 -0
  63. package/dist/errors.d.ts +23 -0
  64. package/dist/file-blob.d.ts +11 -0
  65. package/dist/index.d.ts +4 -0
  66. package/dist/index.js +186 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/inspection.d.ts +12 -0
  69. package/dist/kernel.d.ts +101 -0
  70. package/dist/lib-GQLPACFK.js +9746 -0
  71. package/dist/lib-GQLPACFK.js.map +1 -0
  72. package/dist/materializer-cli-entry.d.ts +2 -0
  73. package/dist/materializer-cli-entry.js +29 -0
  74. package/dist/materializer-cli-entry.js.map +1 -0
  75. package/dist/materializer-cli.d.ts +24 -0
  76. package/dist/materializer-cli.js +24 -0
  77. package/dist/materializer-cli.js.map +1 -0
  78. package/dist/native-raster.d.ts +10 -0
  79. package/dist/native.d.ts +129 -0
  80. package/dist/native.js +31 -0
  81. package/dist/native.js.map +1 -0
  82. package/dist/office-import.d.ts +48 -0
  83. package/dist/office-import.js +154 -0
  84. package/dist/office-import.js.map +1 -0
  85. package/dist/presentation-pptx-api.d.ts +60 -0
  86. package/dist/presentation-pptx-import.d.ts +5 -0
  87. package/dist/presentation-pptx-state-digest-765CZZED.js +10 -0
  88. package/dist/presentation-pptx-state-digest-765CZZED.js.map +1 -0
  89. package/dist/presentation-pptx-state-digest.d.ts +3 -0
  90. package/dist/presentation-pptx-state.d.ts +4 -0
  91. package/dist/presentation-pptx-xml.d.ts +29 -0
  92. package/dist/presentation-pptx.d.ts +5 -0
  93. package/dist/presentation-pptx.js +2365 -0
  94. package/dist/presentation-pptx.js.map +1 -0
  95. package/dist/presentation-render.d.ts +29 -0
  96. package/dist/presentation-render.js +375 -0
  97. package/dist/presentation-render.js.map +1 -0
  98. package/dist/presentation.d.ts +565 -0
  99. package/dist/presentation.js +71 -0
  100. package/dist/presentation.js.map +1 -0
  101. package/dist/production-composite.d.ts +68 -0
  102. package/dist/production-document.d.ts +2 -0
  103. package/dist/production-document.js +176 -0
  104. package/dist/production-document.js.map +1 -0
  105. package/dist/production-native-codecs.d.ts +94 -0
  106. package/dist/production-presentation.d.ts +2 -0
  107. package/dist/production-presentation.js +176 -0
  108. package/dist/production-presentation.js.map +1 -0
  109. package/dist/production-sha256.d.ts +2 -0
  110. package/dist/production-spreadsheet-native.d.ts +12 -0
  111. package/dist/production-spreadsheet.d.ts +2 -0
  112. package/dist/production-spreadsheet.js +176 -0
  113. package/dist/production-spreadsheet.js.map +1 -0
  114. package/dist/production.d.ts +96 -0
  115. package/dist/raster-image.d.ts +34 -0
  116. package/dist/reference.d.ts +17 -0
  117. package/dist/reference.js +574 -0
  118. package/dist/reference.js.map +1 -0
  119. package/dist/runtime-cli-entry.d.ts +2 -0
  120. package/dist/runtime-cli-entry.js +21 -0
  121. package/dist/runtime-cli-entry.js.map +1 -0
  122. package/dist/runtime-cli.d.ts +76 -0
  123. package/dist/runtime-cli.js +26 -0
  124. package/dist/runtime-cli.js.map +1 -0
  125. package/dist/runtime-development.d.ts +73 -0
  126. package/dist/runtime-development.js +30 -0
  127. package/dist/runtime-development.js.map +1 -0
  128. package/dist/runtime-receipt.d.ts +19 -0
  129. package/dist/runtime.d.ts +355 -0
  130. package/dist/runtime.js +42 -0
  131. package/dist/runtime.js.map +1 -0
  132. package/dist/snapshot.d.ts +26 -0
  133. package/dist/snapshot.js +61 -0
  134. package/dist/snapshot.js.map +1 -0
  135. package/dist/spreadsheet-address.d.ts +18 -0
  136. package/dist/spreadsheet-file.d.ts +17 -0
  137. package/dist/spreadsheet-formula.d.ts +67 -0
  138. package/dist/spreadsheet-image.d.ts +17 -0
  139. package/dist/spreadsheet-render.d.ts +5 -0
  140. package/dist/spreadsheet-render.js +752 -0
  141. package/dist/spreadsheet-render.js.map +1 -0
  142. package/dist/spreadsheet-types.d.ts +155 -0
  143. package/dist/spreadsheet-xlsx-api.d.ts +88 -0
  144. package/dist/spreadsheet-xlsx-codec.d.ts +16 -0
  145. package/dist/spreadsheet-xlsx-codec.js +16 -0
  146. package/dist/spreadsheet-xlsx-codec.js.map +1 -0
  147. package/dist/spreadsheet-xlsx-state.d.ts +16 -0
  148. package/dist/spreadsheet.d.ts +724 -0
  149. package/dist/spreadsheet.js +67 -0
  150. package/dist/spreadsheet.js.map +1 -0
  151. package/package.json +129 -0
  152. package/src/bounded-zip.ts +450 -0
  153. package/src/document-docx-api.ts +64 -0
  154. package/src/document-docx-codec.ts +2253 -0
  155. package/src/document-docx-import.ts +3814 -0
  156. package/src/document-docx-packer.ts +72 -0
  157. package/src/document-docx-state.ts +59 -0
  158. package/src/document-render.ts +615 -0
  159. package/src/document.ts +2800 -0
  160. package/src/errors.ts +45 -0
  161. package/src/file-blob.ts +43 -0
  162. package/src/index.ts +4 -0
  163. package/src/inspection.ts +50 -0
  164. package/src/kernel.ts +525 -0
  165. package/src/materializer-cli-entry.ts +11 -0
  166. package/src/materializer-cli.ts +743 -0
  167. package/src/native-raster.ts +141 -0
  168. package/src/native.ts +390 -0
  169. package/src/office-import.ts +243 -0
  170. package/src/presentation-pptx-api.ts +107 -0
  171. package/src/presentation-pptx-import.ts +2065 -0
  172. package/src/presentation-pptx-state-digest.ts +46 -0
  173. package/src/presentation-pptx-state.ts +18 -0
  174. package/src/presentation-pptx-xml.ts +473 -0
  175. package/src/presentation-pptx.ts +447 -0
  176. package/src/presentation-render.ts +532 -0
  177. package/src/presentation.ts +2599 -0
  178. package/src/production-composite.ts +805 -0
  179. package/src/production-document.ts +2 -0
  180. package/src/production-native-codecs.ts +1141 -0
  181. package/src/production-presentation.ts +2 -0
  182. package/src/production-sha256.ts +150 -0
  183. package/src/production-spreadsheet-native.ts +686 -0
  184. package/src/production-spreadsheet.ts +2 -0
  185. package/src/production.ts +1022 -0
  186. package/src/raster-image.ts +586 -0
  187. package/src/reference.ts +17 -0
  188. package/src/runtime-cli-entry.ts +13 -0
  189. package/src/runtime-cli.ts +670 -0
  190. package/src/runtime-development.ts +689 -0
  191. package/src/runtime-receipt.ts +169 -0
  192. package/src/runtime.ts +1463 -0
  193. package/src/snapshot.ts +88 -0
  194. package/src/spreadsheet-address.ts +90 -0
  195. package/src/spreadsheet-file.ts +71 -0
  196. package/src/spreadsheet-formula.ts +1212 -0
  197. package/src/spreadsheet-image.ts +242 -0
  198. package/src/spreadsheet-render.ts +985 -0
  199. package/src/spreadsheet-types.ts +171 -0
  200. package/src/spreadsheet-xlsx-api.ts +123 -0
  201. package/src/spreadsheet-xlsx-codec.ts +3063 -0
  202. package/src/spreadsheet-xlsx-state.ts +191 -0
  203. package/src/spreadsheet.ts +5195 -0
@@ -0,0 +1,752 @@
1
+ import {
2
+ rasterizeSvgToPng
3
+ } from "./chunk-XIFUNVBO.js";
4
+ import {
5
+ formatCellAddress,
6
+ parseRangeAddress,
7
+ spreadsheetImageSource
8
+ } from "./chunk-4FRFNNBM.js";
9
+ import {
10
+ ArtifactLimitError,
11
+ UnsupportedArtifactFeatureError
12
+ } from "./chunk-O24KTEGP.js";
13
+ import "./chunk-FGZM4FQ6.js";
14
+ import {
15
+ FileBlob
16
+ } from "./chunk-4BSENMAE.js";
17
+ import "./chunk-VUNV25KB.js";
18
+
19
+ // src/spreadsheet-render.ts
20
+ var MAX_RENDER_CELLS = 25e4;
21
+ var MAX_RENDER_DIMENSION = 16384;
22
+ var MAX_RENDER_SPARKLINE_POINTS = 25e4;
23
+ var DEFAULT_BACKGROUND = "#ffffff";
24
+ var MAX_NUMBER_FORMAT_DECIMALS = 100;
25
+ var numberFormatters = /* @__PURE__ */ new Map();
26
+ async function renderWorkbook(workbook, options = {}) {
27
+ const format = options.format ?? "png";
28
+ if (format !== "png" && format !== "svg") {
29
+ throw new Error("Spreadsheet render format must be png or svg");
30
+ }
31
+ if (format === "png" && (typeof process === "undefined" || !process.versions?.node)) {
32
+ throw new UnsupportedArtifactFeatureError("render", "Spreadsheet PNG rasterization", "browser");
33
+ }
34
+ const worksheet = options.sheetName ? workbook.worksheets.getItem(options.sheetName) : workbook.worksheets.getActiveWorksheet();
35
+ const address = renderAddress(worksheet, options);
36
+ const scale = options.scale ?? 1;
37
+ if (!Number.isFinite(scale) || scale <= 0 || scale > 8) {
38
+ throw new Error("Spreadsheet render scale must be greater than 0 and at most 8");
39
+ }
40
+ const svg = renderSvg(workbook, worksheet, address, {
41
+ background: options.background ?? DEFAULT_BACKGROUND,
42
+ scale
43
+ });
44
+ if (format === "svg") {
45
+ return new FileBlob([svg], {
46
+ type: "image/svg+xml",
47
+ name: `${safeFileStem(worksheet.name)}.svg`
48
+ });
49
+ }
50
+ const rendered = await rasterizeSvgToPng(svg, {
51
+ background: options.background ?? DEFAULT_BACKGROUND
52
+ });
53
+ return FileBlob.fromBytes(rendered, {
54
+ type: "image/png",
55
+ name: `${safeFileStem(worksheet.name)}.png`
56
+ });
57
+ }
58
+ function renderAddress(worksheet, options) {
59
+ if (options.range) return parseRangeAddress(options.range);
60
+ let used = worksheet.usedRangeAddress();
61
+ for (const chart of worksheet.charts.items) {
62
+ if (chart.position)
63
+ used = unionRanges(used, drawingRange(chart.position.from, chart.position.to));
64
+ }
65
+ for (const sparkline of worksheet.sparklineGroups.items) {
66
+ used = unionRanges(used, sparkline.targetRange.address);
67
+ }
68
+ for (const image of worksheet.images.items) {
69
+ const { from, extent } = image.config.anchor;
70
+ const approximate = {
71
+ row: from.row,
72
+ col: from.col,
73
+ rowCount: Math.max(1, Math.ceil(extent.heightPx / worksheet.rowHeight(from.row))),
74
+ colCount: Math.max(1, Math.ceil(extent.widthPx / worksheet.columnWidth(from.col)))
75
+ };
76
+ used = unionRanges(used, approximate);
77
+ }
78
+ return used ?? { row: 0, col: 0, rowCount: 1, colCount: 1 };
79
+ }
80
+ function renderSvg(workbook, worksheet, address, options) {
81
+ const cellCount = address.rowCount * address.colCount;
82
+ if (cellCount > MAX_RENDER_CELLS) {
83
+ throw new Error(
84
+ `Spreadsheet render range has ${cellCount.toLocaleString()} cells; maximum is ${MAX_RENDER_CELLS.toLocaleString()}`
85
+ );
86
+ }
87
+ const columns = cumulativeSizes(
88
+ address.colCount,
89
+ (index) => worksheet.columnWidth(address.col + index)
90
+ );
91
+ const rows = cumulativeSizes(
92
+ address.rowCount,
93
+ (index) => worksheet.rowHeight(address.row + index)
94
+ );
95
+ const logicalWidth = columns.at(-1) ?? 0;
96
+ const logicalHeight = rows.at(-1) ?? 0;
97
+ const width = logicalWidth * options.scale;
98
+ const height = logicalHeight * options.scale;
99
+ if (width > MAX_RENDER_DIMENSION || height > MAX_RENDER_DIMENSION) {
100
+ throw new Error(
101
+ `Spreadsheet render is ${Math.ceil(width)}x${Math.ceil(height)}px; each dimension must be at most ${MAX_RENDER_DIMENSION}px`
102
+ );
103
+ }
104
+ const definitions = [];
105
+ const elements = [];
106
+ elements.push(
107
+ `<rect x="0" y="0" width="${number(width)}" height="${number(height)}" fill="${escapeAttribute(normalizeColor(options.background, DEFAULT_BACKGROUND))}"/>`
108
+ );
109
+ elements.push(`<g transform="scale(${number(options.scale)})">`);
110
+ if (worksheet.showGridLines) {
111
+ const grid = gridPath(columns, rows);
112
+ if (grid) {
113
+ elements.push(
114
+ `<path d="${grid}" fill="none" stroke="#d9dde3" stroke-width="1" vector-effect="non-scaling-stroke" shape-rendering="crispEdges"/>`
115
+ );
116
+ }
117
+ }
118
+ const merges = worksheet.mergeRegions().filter((merge) => rangesOverlap(merge, address));
119
+ const entries = [...worksheet.cellEntries()].filter(({ row, col }) => contains(address, row, col)).sort((a, b) => a.row - b.row || a.col - b.col);
120
+ const mergeIndexByCell = indexMerges(merges, address);
121
+ const storedCellIndexes = new Set(
122
+ entries.map(({ row, col }) => cellIndexInRange(address, row, col))
123
+ );
124
+ for (const { row, col, data } of entries) {
125
+ const mergeIndex = mergeIndexByCell[cellIndexInRange(address, row, col)] ?? -1;
126
+ const merge = mergeIndex < 0 ? void 0 : merges[mergeIndex];
127
+ if (merge && (merge.row !== row || merge.col !== col)) continue;
128
+ const bounds = cellBounds(
129
+ merge ? intersect(merge, address) : { row, col, rowCount: 1, colCount: 1 },
130
+ address,
131
+ columns,
132
+ rows
133
+ );
134
+ if (!bounds) continue;
135
+ renderCell(
136
+ elements,
137
+ definitions,
138
+ bounds,
139
+ data.format,
140
+ workbook.valueAt(worksheet, { row, col }),
141
+ `cell-${row}-${col}`,
142
+ options.background
143
+ );
144
+ }
145
+ for (const merge of merges) {
146
+ const topLeftIndex = cellIndexInRange(address, merge.row, merge.col);
147
+ if (topLeftIndex >= 0 && storedCellIndexes.has(topLeftIndex)) continue;
148
+ const bounds = cellBounds(intersect(merge, address), address, columns, rows);
149
+ if (!bounds) continue;
150
+ elements.push(
151
+ `<rect x="${number(bounds.x)}" y="${number(bounds.y)}" width="${number(bounds.width)}" height="${number(bounds.height)}" fill="${escapeAttribute(normalizeColor(options.background, DEFAULT_BACKGROUND))}" stroke="#d9dde3"/>`
152
+ );
153
+ }
154
+ renderSparklines(elements, worksheet, address, columns, rows);
155
+ renderDrawings(elements, worksheet, address, columns, rows);
156
+ elements.push("</g>");
157
+ return [
158
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${number(width)}" height="${number(height)}" viewBox="0 0 ${number(width)} ${number(height)}">`,
159
+ definitions.length > 0 ? `<defs>${definitions.join("")}</defs>` : "",
160
+ ...elements,
161
+ "</svg>"
162
+ ].join("");
163
+ }
164
+ function indexMerges(merges, viewport) {
165
+ const indexes = new Int32Array(viewport.rowCount * viewport.colCount);
166
+ indexes.fill(-1);
167
+ for (let mergeIndex = 0; mergeIndex < merges.length; mergeIndex += 1) {
168
+ const visible = intersect(merges[mergeIndex], viewport);
169
+ const firstColumn = visible.col - viewport.col;
170
+ for (let row = visible.row; row < visible.row + visible.rowCount; row += 1) {
171
+ const start = (row - viewport.row) * viewport.colCount + firstColumn;
172
+ indexes.fill(mergeIndex, start, start + visible.colCount);
173
+ }
174
+ }
175
+ return indexes;
176
+ }
177
+ function cellIndexInRange(range, row, col) {
178
+ if (!contains(range, row, col)) return -1;
179
+ return (row - range.row) * range.colCount + col - range.col;
180
+ }
181
+ function renderCell(elements, definitions, bounds, format, value, id, background) {
182
+ const fill = normalizeColor(format.fill, background);
183
+ const border = borderMarkup(bounds, format);
184
+ if (format.fill || border) {
185
+ elements.push(
186
+ `<rect x="${number(bounds.x)}" y="${number(bounds.y)}" width="${number(bounds.width)}" height="${number(bounds.height)}" fill="${escapeAttribute(fill)}"/>`
187
+ );
188
+ }
189
+ if (border) elements.push(border);
190
+ const text = displayValue(value, format.numberFormat);
191
+ if (!text) return;
192
+ const fontSize = clamp(format.font?.size ?? 12, 6, 72);
193
+ const padding = 6;
194
+ const contentWidth = Math.max(1, bounds.width - padding * 2);
195
+ const lines = format.wrapText ? wrapLines(text, contentWidth, fontSize) : [text.replaceAll(/\r?\n/g, " ")];
196
+ const lineHeight = fontSize * 1.25;
197
+ const textHeight = lines.length * lineHeight;
198
+ const vertical = format.verticalAlignment ?? "center";
199
+ const startY = vertical === "top" ? bounds.y + padding + fontSize : vertical === "bottom" ? bounds.y + bounds.height - padding - textHeight + fontSize : bounds.y + (bounds.height - textHeight) / 2 + fontSize;
200
+ const horizontal = format.horizontalAlignment ?? inferredAlignment(value);
201
+ const anchor = horizontal === "center" ? "middle" : horizontal === "right" ? "end" : "start";
202
+ const x = horizontal === "center" ? bounds.x + bounds.width / 2 : horizontal === "right" ? bounds.x + bounds.width - padding : bounds.x + padding;
203
+ const clipId = `clip-${id}`;
204
+ definitions.push(
205
+ `<clipPath id="${clipId}"><rect x="${number(bounds.x + 1)}" y="${number(bounds.y + 1)}" width="${number(Math.max(0, bounds.width - 2))}" height="${number(Math.max(0, bounds.height - 2))}"/></clipPath>`
206
+ );
207
+ const decoration = format.font?.underline ? ' text-decoration="underline"' : "";
208
+ const tspans = lines.map(
209
+ (line, index) => `<tspan x="${number(x)}" y="${number(startY + index * lineHeight)}">${escapeText(line)}</tspan>`
210
+ ).join("");
211
+ elements.push(
212
+ `<text clip-path="url(#${clipId})" text-anchor="${anchor}" font-family="${escapeAttribute(format.font?.name ?? "Arial, sans-serif")}" font-size="${number(fontSize)}" font-weight="${format.font?.bold ? "700" : "400"}" font-style="${format.font?.italic ? "italic" : "normal"}" fill="${escapeAttribute(normalizeColor(format.font?.color, "#111827"))}"${decoration}>${tspans}</text>`
213
+ );
214
+ }
215
+ function borderMarkup(bounds, format) {
216
+ const borders = format.borders;
217
+ if (!borders || borders.preset === "none") return "";
218
+ const fallback = borders.style || borders.preset ? {
219
+ ...borders.style ? { style: borders.style } : {},
220
+ ...borders.color ? { color: borders.color } : {}
221
+ } : void 0;
222
+ const sides = {
223
+ top: borders.top ?? (borders.preset === "all" || borders.preset === "outside" ? fallback : void 0),
224
+ right: borders.right ?? (borders.preset === "all" || borders.preset === "outside" ? fallback : void 0),
225
+ bottom: borders.bottom ?? (borders.preset === "all" || borders.preset === "outside" || borders.preset === "doubleBottom" ? fallback : void 0),
226
+ left: borders.left ?? (borders.preset === "all" || borders.preset === "outside" ? fallback : void 0)
227
+ };
228
+ const commands = [];
229
+ const lines = [];
230
+ const append = (side, x1, y1, x2, y2) => {
231
+ const config = sides[side];
232
+ if (!config) return;
233
+ const weight = clamp(config.weight ?? (config.style === "thick" ? 3 : 1), 0.5, 8);
234
+ commands.push(`M${number(x1)} ${number(y1)}L${number(x2)} ${number(y2)}`);
235
+ lines.push(
236
+ `<path d="${commands.pop()}" fill="none" stroke="${escapeAttribute(normalizeColor(config.color, borders.color ?? "#4b5563"))}" stroke-width="${number(weight)}" vector-effect="non-scaling-stroke"/>`
237
+ );
238
+ if (config.style === "double" || side === "bottom" && borders.preset === "doubleBottom") {
239
+ const offset = 3;
240
+ const vertical = x1 === x2;
241
+ lines.push(
242
+ `<path d="M${number(x1 + (vertical ? offset : 0))} ${number(y1 + (vertical ? 0 : -offset))}L${number(x2 + (vertical ? offset : 0))} ${number(y2 + (vertical ? 0 : -offset))}" fill="none" stroke="${escapeAttribute(normalizeColor(config.color, borders.color ?? "#4b5563"))}" stroke-width="1" vector-effect="non-scaling-stroke"/>`
243
+ );
244
+ }
245
+ };
246
+ append("top", bounds.x, bounds.y, bounds.x + bounds.width, bounds.y);
247
+ append(
248
+ "right",
249
+ bounds.x + bounds.width,
250
+ bounds.y,
251
+ bounds.x + bounds.width,
252
+ bounds.y + bounds.height
253
+ );
254
+ append(
255
+ "bottom",
256
+ bounds.x,
257
+ bounds.y + bounds.height,
258
+ bounds.x + bounds.width,
259
+ bounds.y + bounds.height
260
+ );
261
+ append("left", bounds.x, bounds.y, bounds.x, bounds.y + bounds.height);
262
+ return lines.join("");
263
+ }
264
+ function gridPath(columns, rows) {
265
+ const width = columns.at(-1) ?? 0;
266
+ const height = rows.at(-1) ?? 0;
267
+ const commands = [];
268
+ for (const x of columns) commands.push(`M${number(x)} 0V${number(height)}`);
269
+ for (const y of rows) commands.push(`M0 ${number(y)}H${number(width)}`);
270
+ return commands.join("");
271
+ }
272
+ function cumulativeSizes(count, sizeAt) {
273
+ const values = new Array(count + 1);
274
+ values[0] = 0;
275
+ for (let index = 0; index < count; index += 1) {
276
+ values[index + 1] = values[index] + clamp(sizeAt(index), 1, MAX_RENDER_DIMENSION);
277
+ }
278
+ return values;
279
+ }
280
+ function cellBounds(cell, viewport, columns, rows) {
281
+ const relativeCol = cell.col - viewport.col;
282
+ const relativeRow = cell.row - viewport.row;
283
+ const endCol = relativeCol + cell.colCount;
284
+ const endRow = relativeRow + cell.rowCount;
285
+ const x = columns[relativeCol];
286
+ const y = rows[relativeRow];
287
+ const endX = columns[endCol];
288
+ const endY = rows[endRow];
289
+ if (x === void 0 || y === void 0 || endX === void 0 || endY === void 0) return null;
290
+ return { x, y, width: endX - x, height: endY - y };
291
+ }
292
+ function displayValue(value, numberFormat) {
293
+ if (value === null) return "";
294
+ if (value instanceof Date) {
295
+ if (numberFormat?.includes("h") || numberFormat?.includes("s")) return value.toISOString();
296
+ return value.toISOString().slice(0, 10);
297
+ }
298
+ if (typeof value === "number") {
299
+ const decimals = decimalPlaces(numberFormat);
300
+ const useGrouping = numberFormat?.includes(",") ?? false;
301
+ const formatted = (numericValue) => formatNumber(numericValue, decimals, useGrouping);
302
+ if (numberFormat?.includes("%")) return `${formatted(value * 100)}%`;
303
+ if (numberFormat?.includes("$")) return `$${formatted(value)}`;
304
+ if (numberFormat?.includes("\u20AC")) return `\u20AC${formatted(value)}`;
305
+ if (numberFormat?.includes("\xA3")) return `\xA3${formatted(value)}`;
306
+ return formatted(value);
307
+ }
308
+ return String(value);
309
+ }
310
+ function formatNumber(value, decimals, useGrouping = false) {
311
+ if (!Number.isFinite(value)) return String(value);
312
+ const normalizedDecimals = decimals === void 0 ? void 0 : Math.max(0, Math.min(MAX_NUMBER_FORMAT_DECIMALS, Math.floor(decimals)));
313
+ const key = `${normalizedDecimals ?? "auto"}:${useGrouping ? 1 : 0}`;
314
+ let formatter = numberFormatters.get(key);
315
+ if (!formatter) {
316
+ formatter = new Intl.NumberFormat("en-US", {
317
+ ...normalizedDecimals === void 0 ? { maximumFractionDigits: 12 } : {
318
+ minimumFractionDigits: normalizedDecimals,
319
+ maximumFractionDigits: normalizedDecimals
320
+ },
321
+ useGrouping
322
+ });
323
+ numberFormatters.set(key, formatter);
324
+ }
325
+ return formatter.format(value);
326
+ }
327
+ function decimalPlaces(numberFormat) {
328
+ if (!numberFormat) return void 0;
329
+ const decimal = /\.([0#]+)/.exec(numberFormat);
330
+ if (!decimal) return 0;
331
+ return decimal[1]?.length ?? 0;
332
+ }
333
+ function renderDrawings(elements, worksheet, viewport, columns, rows) {
334
+ for (const image of worksheet.images.items) {
335
+ const { from, extent } = image.config.anchor;
336
+ if (!contains(viewport, from.row, from.col)) continue;
337
+ const x = columns[from.col - viewport.col];
338
+ const y = rows[from.row - viewport.row];
339
+ if (x === void 0 || y === void 0) continue;
340
+ const source = spreadsheetImageSource(image.config);
341
+ elements.push(
342
+ `<image x="${number(x)}" y="${number(y)}" width="${number(extent.widthPx)}" height="${number(extent.heightPx)}" href="${escapeAttribute(source)}" preserveAspectRatio="xMidYMid meet">${image.config.alt ? `<title>${escapeText(image.config.alt)}</title>` : ""}</image>`
343
+ );
344
+ }
345
+ for (const chart of worksheet.charts.items) {
346
+ if (!chart.position) continue;
347
+ const range = drawingRange(chart.position.from, chart.position.to);
348
+ if (!rangesOverlap(range, viewport)) continue;
349
+ const visible = intersect(range, viewport);
350
+ const bounds = cellBounds(visible, viewport, columns, rows);
351
+ if (!bounds || bounds.width < 40 || bounds.height < 40) continue;
352
+ renderChart(elements, bounds, chart);
353
+ }
354
+ }
355
+ function renderSparklines(elements, worksheet, viewport, columns, rows) {
356
+ let renderedPoints = 0;
357
+ for (const group of worksheet.sparklineGroups.items) {
358
+ const visible = intersect(group.targetRange.address, viewport);
359
+ if (visible.rowCount <= 0 || visible.colCount <= 0) continue;
360
+ const groupPoints = visible.rowCount * visible.colCount * group.resourceUsage.pointsPerSparkline;
361
+ renderedPoints += groupPoints;
362
+ if (renderedPoints > MAX_RENDER_SPARKLINE_POINTS) {
363
+ throw new ArtifactLimitError(
364
+ "spreadsheet rendered sparkline points",
365
+ renderedPoints,
366
+ MAX_RENDER_SPARKLINE_POINTS
367
+ );
368
+ }
369
+ const dateAxis = group.dateAxisValues();
370
+ const axis = group.axis;
371
+ const markers = group.markers;
372
+ const colors = {
373
+ series: normalizeColor(group.seriesColor, "#2563eb"),
374
+ negative: normalizeColor(group.negativeColor, "#dc2626"),
375
+ axis: normalizeColor(group.axisColor, "#64748b"),
376
+ markers: normalizeColor(group.markersColor, group.seriesColor ?? "#2563eb"),
377
+ first: normalizeColor(group.firstMarkerColor, group.seriesColor ?? "#2563eb"),
378
+ last: normalizeColor(group.lastMarkerColor, group.seriesColor ?? "#2563eb"),
379
+ high: normalizeColor(group.highMarkerColor, "#16a34a"),
380
+ low: normalizeColor(group.lowMarkerColor, "#dc2626")
381
+ };
382
+ for (let row = visible.row; row < visible.row + visible.rowCount; row += 1) {
383
+ for (let col = visible.col; col < visible.col + visible.colCount; col += 1) {
384
+ const bounds = cellBounds({ row, col, rowCount: 1, colCount: 1 }, viewport, columns, rows);
385
+ if (!bounds || bounds.width < 8 || bounds.height < 8) continue;
386
+ const values = group.valuesForTargetCell(row, col);
387
+ if (!values.some((value) => value !== null)) continue;
388
+ const plot = {
389
+ x: bounds.x + 4,
390
+ y: bounds.y + 4,
391
+ width: Math.max(1, bounds.width - 8),
392
+ height: Math.max(1, bounds.height - 8)
393
+ };
394
+ elements.push(
395
+ `<g data-opengeni-sparkline="${escapeAttribute(group.id)}" data-target="${escapeAttribute(formatCellAddress({ row, col }))}">`
396
+ );
397
+ renderSparkline(elements, plot, values, dateAxis, group.type, {
398
+ axis,
399
+ markers,
400
+ colors,
401
+ lineWeight: group.lineWeight,
402
+ connectEmpty: group.displayEmptyCellsAs === 2
403
+ });
404
+ elements.push("</g>");
405
+ }
406
+ }
407
+ }
408
+ }
409
+ function renderSparkline(elements, plot, inputValues, dateAxis, type, options) {
410
+ const values = options.axis.rightToLeft ? [...inputValues].reverse() : [...inputValues];
411
+ const dates = options.axis.rightToLeft && dateAxis ? [...dateAxis].reverse() : dateAxis;
412
+ const numeric = numericSparklineExtent(values);
413
+ if (numeric.count === 0) return;
414
+ if (type === "stacked") {
415
+ renderStackedSparkline(elements, plot, values, options);
416
+ return;
417
+ }
418
+ const includeZero = type === "column" || options.axis.showAxis;
419
+ let minimum = options.axis.manualMin ?? (includeZero ? Math.min(0, numeric.min) : numeric.min);
420
+ let maximum = options.axis.manualMax ?? (includeZero ? Math.max(0, numeric.max) : numeric.max);
421
+ if (minimum >= maximum) {
422
+ const padding = Math.max(1, Math.abs(minimum) * 0.05);
423
+ minimum -= padding;
424
+ maximum += padding;
425
+ }
426
+ const span = maximum - minimum;
427
+ const baseline = plot.y + (maximum - clamp(0, minimum, maximum)) / span * plot.height;
428
+ if (options.axis.showAxis && minimum <= 0 && maximum >= 0) {
429
+ elements.push(
430
+ `<path d="M${number(plot.x)} ${number(baseline)}H${number(plot.x + plot.width)}" fill="none" stroke="${escapeAttribute(options.colors.axis)}" stroke-width="1" vector-effect="non-scaling-stroke"/>`
431
+ );
432
+ }
433
+ if (type === "column") {
434
+ const slot = plot.width / Math.max(1, values.length);
435
+ const width = Math.max(1, slot * 0.62);
436
+ values.forEach((value, index) => {
437
+ if (value === null) return;
438
+ const y = plot.y + (maximum - clamp(value, minimum, maximum)) / span * plot.height;
439
+ const top = Math.min(y, baseline);
440
+ const height = Math.max(0.75, Math.abs(baseline - y));
441
+ elements.push(
442
+ `<rect x="${number(plot.x + index * slot + (slot - width) / 2)}" y="${number(top)}" width="${number(width)}" height="${number(height)}" fill="${escapeAttribute(value < 0 ? options.colors.negative : options.colors.series)}"/>`
443
+ );
444
+ });
445
+ return;
446
+ }
447
+ const xPositions = sparklineXPositions(values.length, dates, plot);
448
+ const points = values.map(
449
+ (value, index) => value === null ? null : {
450
+ x: xPositions[index],
451
+ y: plot.y + (maximum - clamp(value, minimum, maximum)) / span * plot.height,
452
+ value,
453
+ index
454
+ }
455
+ );
456
+ const segments = [];
457
+ let segment = [];
458
+ for (const point of points) {
459
+ if (point) {
460
+ segment.push(point);
461
+ continue;
462
+ }
463
+ if (!options.connectEmpty && segment.length > 0) {
464
+ segments.push(segment);
465
+ segment = [];
466
+ }
467
+ }
468
+ if (segment.length > 0) segments.push(segment);
469
+ for (const line of segments) {
470
+ if (line.length === 1) {
471
+ elements.push(
472
+ `<circle cx="${number(line[0].x)}" cy="${number(line[0].y)}" r="${number(Math.max(1, options.lineWeight))}" fill="${escapeAttribute(options.colors.series)}"/>`
473
+ );
474
+ } else {
475
+ elements.push(
476
+ `<polyline points="${line.map((point) => `${number(point.x)},${number(point.y)}`).join(" ")}" fill="none" stroke="${escapeAttribute(options.colors.series)}" stroke-width="${number(options.lineWeight)}" stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke"/>`
477
+ );
478
+ }
479
+ }
480
+ const firstIndex = points.findIndex((point) => point !== null);
481
+ let lastIndex = -1;
482
+ for (let index = points.length - 1; index >= 0; index -= 1) {
483
+ if (points[index] !== null) {
484
+ lastIndex = index;
485
+ break;
486
+ }
487
+ }
488
+ const high = numeric.max;
489
+ const low = numeric.min;
490
+ for (const point of points) {
491
+ if (!point) continue;
492
+ const special = options.markers.show || options.markers.first && point.index === firstIndex || options.markers.last && point.index === lastIndex || options.markers.high && point.value === high || options.markers.low && point.value === low || options.markers.negative && point.value < 0;
493
+ if (!special) continue;
494
+ const color = options.markers.negative && point.value < 0 ? options.colors.negative : options.markers.high && point.value === high ? options.colors.high : options.markers.low && point.value === low ? options.colors.low : options.markers.first && point.index === firstIndex ? options.colors.first : options.markers.last && point.index === lastIndex ? options.colors.last : options.colors.markers;
495
+ elements.push(
496
+ `<circle cx="${number(point.x)}" cy="${number(point.y)}" r="${number(Math.max(1.5, options.lineWeight + 0.5))}" fill="${escapeAttribute(color)}"/>`
497
+ );
498
+ }
499
+ }
500
+ function renderStackedSparkline(elements, plot, values, options) {
501
+ const baseline = plot.y + plot.height / 2;
502
+ if (options.axis.showAxis) {
503
+ elements.push(
504
+ `<path d="M${number(plot.x)} ${number(baseline)}H${number(plot.x + plot.width)}" fill="none" stroke="${escapeAttribute(options.colors.axis)}" stroke-width="1" vector-effect="non-scaling-stroke"/>`
505
+ );
506
+ }
507
+ const slot = plot.width / Math.max(1, values.length);
508
+ const width = Math.max(1, slot * 0.62);
509
+ const height = Math.max(1, plot.height * 0.42);
510
+ values.forEach((value, index) => {
511
+ if (value === null || value === 0) return;
512
+ elements.push(
513
+ `<rect x="${number(plot.x + index * slot + (slot - width) / 2)}" y="${number(value > 0 ? baseline - height : baseline)}" width="${number(width)}" height="${number(height)}" fill="${escapeAttribute(value < 0 ? options.colors.negative : options.colors.series)}"/>`
514
+ );
515
+ });
516
+ }
517
+ function sparklineXPositions(count, dateAxis, plot) {
518
+ if (count === 1) return [plot.x + plot.width / 2];
519
+ if (dateAxis && dateAxis.length === count && dateAxis.every((value) => value !== null)) {
520
+ const extent = numericSparklineExtent(dateAxis);
521
+ const minimum = extent.min;
522
+ const maximum = extent.max;
523
+ if (minimum < maximum) {
524
+ return dateAxis.map(
525
+ (value) => plot.x + (value - minimum) / (maximum - minimum) * plot.width
526
+ );
527
+ }
528
+ }
529
+ return Array.from(
530
+ { length: count },
531
+ (_, index) => plot.x + index / Math.max(1, count - 1) * plot.width
532
+ );
533
+ }
534
+ function numericSparklineExtent(values) {
535
+ let min = Number.POSITIVE_INFINITY;
536
+ let max = Number.NEGATIVE_INFINITY;
537
+ let count = 0;
538
+ for (const value of values) {
539
+ if (value === null) continue;
540
+ min = Math.min(min, value);
541
+ max = Math.max(max, value);
542
+ count += 1;
543
+ }
544
+ return { min, max, count };
545
+ }
546
+ function renderChart(elements, bounds, chart) {
547
+ const titleHeight = chart.title ? 30 : 12;
548
+ const plot = {
549
+ x: bounds.x + 40,
550
+ y: bounds.y + titleHeight + 8,
551
+ width: Math.max(1, bounds.width - 56),
552
+ height: Math.max(1, bounds.height - titleHeight - 34)
553
+ };
554
+ elements.push(
555
+ `<g data-opengeni-chart="${escapeAttribute(chart.id)}"><rect x="${number(bounds.x)}" y="${number(bounds.y)}" width="${number(bounds.width)}" height="${number(bounds.height)}" rx="6" fill="#ffffff" stroke="#cbd5e1"/>`
556
+ );
557
+ if (chart.title) {
558
+ elements.push(
559
+ `<text x="${number(bounds.x + bounds.width / 2)}" y="${number(bounds.y + 20)}" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="700" fill="#0f172a">${escapeText(chart.title)}</text>`
560
+ );
561
+ }
562
+ const series = chart.series.items.filter((item) => item.values && item.values.length > 0);
563
+ const values = series.flatMap((item) => item.values ?? []);
564
+ if (series.length === 0 || values.length === 0) {
565
+ elements.push(
566
+ `<text x="${number(bounds.x + bounds.width / 2)}" y="${number(bounds.y + bounds.height / 2)}" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#64748b">${escapeText(chart.type)} chart</text></g>`
567
+ );
568
+ return;
569
+ }
570
+ if (chart.type === "pie" || chart.type === "doughnut") {
571
+ renderPie(elements, plot, series[0].values ?? [], chart.type === "doughnut");
572
+ } else if (chart.type === "bar" || chart.type === "histogram" || chart.type === "waterfall" || chart.type === "funnel") {
573
+ renderBars(elements, plot, series);
574
+ } else {
575
+ renderLines(elements, plot, series, chart.type === "area");
576
+ }
577
+ elements.push("</g>");
578
+ }
579
+ function renderBars(elements, plot, series) {
580
+ const values = series.flatMap((item) => item.values ?? []);
581
+ const maximum = Math.max(0, ...values);
582
+ const minimum = Math.min(0, ...values);
583
+ const span = maximum - minimum || 1;
584
+ const categories = Math.max(...series.map((item) => item.values?.length ?? 0));
585
+ const groupWidth = plot.width / Math.max(1, categories);
586
+ const barWidth = Math.max(1, groupWidth * 0.72 / series.length);
587
+ const baseline = plot.y + maximum / span * plot.height;
588
+ elements.push(
589
+ `<path d="M${number(plot.x)} ${number(baseline)}H${number(plot.x + plot.width)}" stroke="#94a3b8" fill="none"/>`
590
+ );
591
+ series.forEach((item, seriesIndex) => {
592
+ (item.values ?? []).forEach((value, categoryIndex) => {
593
+ const height = Math.abs(value / span) * plot.height;
594
+ const x = plot.x + categoryIndex * groupWidth + groupWidth * 0.14 + seriesIndex * barWidth;
595
+ const y = value >= 0 ? baseline - height : baseline;
596
+ elements.push(
597
+ `<rect x="${number(x)}" y="${number(y)}" width="${number(Math.max(1, barWidth - 1))}" height="${number(height)}" fill="${escapeAttribute(chartColor(item.fill, seriesIndex))}"/>`
598
+ );
599
+ });
600
+ });
601
+ }
602
+ function renderLines(elements, plot, series, area) {
603
+ const all = series.flatMap((item) => item.values ?? []);
604
+ const maximum = Math.max(...all);
605
+ const minimum = Math.min(...all);
606
+ const span = maximum - minimum || 1;
607
+ series.forEach((item, seriesIndex) => {
608
+ const values = item.values ?? [];
609
+ const points = values.map((value, index) => {
610
+ const x = plot.x + index / Math.max(1, values.length - 1) * plot.width;
611
+ const y = plot.y + (maximum - value) / span * plot.height;
612
+ return { x, y };
613
+ });
614
+ if (points.length === 0) return;
615
+ const color = chartColor(item.fill, seriesIndex);
616
+ const pointText = points.map((point) => `${number(point.x)},${number(point.y)}`).join(" ");
617
+ if (area) {
618
+ elements.push(
619
+ `<polygon points="${number(points[0].x)},${number(plot.y + plot.height)} ${pointText} ${number(points.at(-1).x)},${number(plot.y + plot.height)}" fill="${escapeAttribute(color)}" fill-opacity="0.22"/>`
620
+ );
621
+ }
622
+ elements.push(
623
+ `<polyline points="${pointText}" fill="none" stroke="${escapeAttribute(color)}" stroke-width="2"/>`
624
+ );
625
+ for (const point of points) {
626
+ elements.push(
627
+ `<circle cx="${number(point.x)}" cy="${number(point.y)}" r="2.5" fill="${escapeAttribute(color)}"/>`
628
+ );
629
+ }
630
+ });
631
+ }
632
+ function renderPie(elements, plot, values, doughnut) {
633
+ const positive = values.map((value) => Math.max(0, value));
634
+ const total = positive.reduce((sum, value) => sum + value, 0);
635
+ if (total <= 0) return;
636
+ const radius = Math.max(1, Math.min(plot.width, plot.height) / 2);
637
+ const centerX = plot.x + plot.width / 2;
638
+ const centerY = plot.y + plot.height / 2;
639
+ let angle = -Math.PI / 2;
640
+ positive.forEach((value, index) => {
641
+ if (value <= 0) return;
642
+ const next = angle + value / total * Math.PI * 2;
643
+ const start = polar(centerX, centerY, radius, angle);
644
+ const end = polar(centerX, centerY, radius, next);
645
+ const large = next - angle > Math.PI ? 1 : 0;
646
+ elements.push(
647
+ `<path d="M${number(centerX)} ${number(centerY)}L${number(start.x)} ${number(start.y)}A${number(radius)} ${number(radius)} 0 ${large} 1 ${number(end.x)} ${number(end.y)}Z" fill="${chartColor(void 0, index)}"/>`
648
+ );
649
+ angle = next;
650
+ });
651
+ if (doughnut) {
652
+ elements.push(
653
+ `<circle cx="${number(centerX)}" cy="${number(centerY)}" r="${number(radius * 0.52)}" fill="#ffffff"/>`
654
+ );
655
+ }
656
+ }
657
+ function polar(x, y, radius, angle) {
658
+ return { x: x + Math.cos(angle) * radius, y: y + Math.sin(angle) * radius };
659
+ }
660
+ function chartColor(value, index) {
661
+ if (value) return normalizeColor(value, "#2563eb");
662
+ return ["#2563eb", "#0d9488", "#f59e0b", "#dc2626", "#7c3aed", "#0891b2"][index % 6];
663
+ }
664
+ function drawingRange(from, to) {
665
+ return {
666
+ row: from.row,
667
+ col: from.col,
668
+ rowCount: Math.max(1, to.row + to.rowCount - from.row),
669
+ colCount: Math.max(1, to.col + to.colCount - from.col)
670
+ };
671
+ }
672
+ function unionRanges(a, b) {
673
+ if (!a) return { ...b };
674
+ const row = Math.min(a.row, b.row);
675
+ const col = Math.min(a.col, b.col);
676
+ const endRow = Math.max(a.row + a.rowCount, b.row + b.rowCount);
677
+ const endCol = Math.max(a.col + a.colCount, b.col + b.colCount);
678
+ return { row, col, rowCount: endRow - row, colCount: endCol - col };
679
+ }
680
+ function wrapLines(value, width, fontSize) {
681
+ const maxCharacters = Math.max(1, Math.floor(width / Math.max(1, fontSize * 0.56)));
682
+ const lines = [];
683
+ for (const explicitLine of value.split(/\r?\n/)) {
684
+ const words = explicitLine.split(/\s+/).filter(Boolean);
685
+ if (words.length === 0) {
686
+ lines.push("");
687
+ continue;
688
+ }
689
+ let line = "";
690
+ for (const word of words) {
691
+ if (word.length > maxCharacters) {
692
+ if (line) lines.push(line);
693
+ for (let index = 0; index < word.length; index += maxCharacters) {
694
+ lines.push(word.slice(index, index + maxCharacters));
695
+ }
696
+ line = "";
697
+ } else if (!line) {
698
+ line = word;
699
+ } else if (line.length + word.length + 1 <= maxCharacters) {
700
+ line += ` ${word}`;
701
+ } else {
702
+ lines.push(line);
703
+ line = word;
704
+ }
705
+ }
706
+ if (line) lines.push(line);
707
+ }
708
+ return lines.slice(0, 512);
709
+ }
710
+ function intersect(a, b) {
711
+ const row = Math.max(a.row, b.row);
712
+ const col = Math.max(a.col, b.col);
713
+ const endRow = Math.min(a.row + a.rowCount, b.row + b.rowCount);
714
+ const endCol = Math.min(a.col + a.colCount, b.col + b.colCount);
715
+ return { row, col, rowCount: endRow - row, colCount: endCol - col };
716
+ }
717
+ function rangesOverlap(a, b) {
718
+ return a.row < b.row + b.rowCount && b.row < a.row + a.rowCount && a.col < b.col + b.colCount && b.col < a.col + a.colCount;
719
+ }
720
+ function contains(address, row, col) {
721
+ return row >= address.row && col >= address.col && row < address.row + address.rowCount && col < address.col + address.colCount;
722
+ }
723
+ function inferredAlignment(value) {
724
+ return typeof value === "number" || value instanceof Date ? "right" : "left";
725
+ }
726
+ function normalizeColor(color, fallback) {
727
+ if (!color) return fallback;
728
+ if (/^#[0-9a-f]{6}$/i.test(color) || /^#[0-9a-f]{3}$/i.test(color)) return color;
729
+ if (/^[0-9a-f]{6}$/i.test(color)) return `#${color}`;
730
+ if (/^[0-9a-f]{8}$/i.test(color)) return `#${color.slice(2)}`;
731
+ if (/^(?:rgb|hsl)a?\(/i.test(color) || /^[a-z]+$/i.test(color)) return color;
732
+ return fallback;
733
+ }
734
+ function safeFileStem(name) {
735
+ return name.replaceAll(/[^A-Za-z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "") || "sheet";
736
+ }
737
+ function escapeText(value) {
738
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
739
+ }
740
+ function escapeAttribute(value) {
741
+ return escapeText(value).replaceAll('"', "&quot;");
742
+ }
743
+ function number(value) {
744
+ return Number(value.toFixed(3)).toString();
745
+ }
746
+ function clamp(value, min, max) {
747
+ return Math.min(max, Math.max(min, value));
748
+ }
749
+ export {
750
+ renderWorkbook
751
+ };
752
+ //# sourceMappingURL=spreadsheet-render.js.map