documonster 0.16.1 → 0.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.
- package/README.md +1 -1
- package/README_zh.md +1 -1
- package/dist/esm/modules/excel/core/worksheet.js +85 -33
- package/dist/esm/modules/mermaid/index.js +28 -1
- package/dist/esm/modules/pdf/render/layout-engine.js +44 -12
- package/dist/iife/documonster.archive.iife.min.js +1 -1
- package/dist/iife/documonster.csv.iife.min.js +1 -1
- package/dist/iife/documonster.draw.iife.min.js +1 -1
- package/dist/iife/documonster.excel.iife.min.js +45 -45
- package/dist/iife/documonster.formula.iife.min.js +1 -1
- package/dist/iife/documonster.markdown.iife.min.js +1 -1
- package/dist/iife/documonster.mermaid.iife.min.js +2 -2
- package/dist/iife/documonster.pdf.iife.min.js +17 -17
- package/dist/iife/documonster.stream.iife.min.js +1 -1
- package/dist/iife/documonster.word.iife.min.js +1 -1
- package/dist/iife/documonster.xml.iife.min.js +1 -1
- package/dist/types/modules/excel/core/worksheet.d.ts +28 -2
- package/dist/types/modules/excel/index.base.d.ts +1 -1
- package/dist/types/modules/mermaid/index.d.ts +29 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -211,7 +211,7 @@ const buffer = await Workbook.toBuffer(wb);
|
|
|
211
211
|
|
|
212
212
|
```html
|
|
213
213
|
<!-- Script tag (no bundler) — one IIFE per module, each under the shared `Documonster` global -->
|
|
214
|
-
<script src="https://unpkg.com/documonster@0.
|
|
214
|
+
<script src="https://unpkg.com/documonster@0.17.0/dist/iife/documonster.excel.iife.min.js"></script>
|
|
215
215
|
<script>
|
|
216
216
|
const { Workbook, Cell } = Documonster.Excel;
|
|
217
217
|
const wb = Workbook.create();
|
package/README_zh.md
CHANGED
|
@@ -198,7 +198,7 @@ const buffer = await Workbook.toBuffer(wb);
|
|
|
198
198
|
|
|
199
199
|
```html
|
|
200
200
|
<!-- Script 标签(无需打包工具)— 每个模块一个 IIFE,共享同一个 `Documonster` 全局 -->
|
|
201
|
-
<script src="https://unpkg.com/documonster@0.
|
|
201
|
+
<script src="https://unpkg.com/documonster@0.17.0/dist/iife/documonster.excel.iife.min.js"></script>
|
|
202
202
|
<script>
|
|
203
203
|
const { Workbook, Cell } = Documonster.Excel;
|
|
204
204
|
const wb = Workbook.create();
|
|
@@ -26,7 +26,7 @@ import { copyStyle } from "../utils/copy-style.js";
|
|
|
26
26
|
import { isExternalImage } from "../utils/drawing-utils.js";
|
|
27
27
|
import { applyMergeBorders, collectMergeBorders } from "../utils/merge-borders.js";
|
|
28
28
|
import { buildSheetProtection, verifySheetPassword } from "../utils/sheet-protection.js";
|
|
29
|
-
import { calculateAutoFitWidth, getMaxDigitWidth,
|
|
29
|
+
import { calculateAutoFitWidth, getMaxDigitWidth, charWidthToPixel, getPixelPadding, getCellTextWidthPx, getCellHeightPt } from "../utils/text-metrics.js";
|
|
30
30
|
// Worksheet requirements
|
|
31
31
|
// Operate as sheet inside workbook or standalone
|
|
32
32
|
// Load and Save from file and stream
|
|
@@ -1200,12 +1200,23 @@ export function removeConditionalFormatting(ws, filter) {
|
|
|
1200
1200
|
ws.conditionalFormattings = [];
|
|
1201
1201
|
}
|
|
1202
1202
|
}
|
|
1203
|
-
|
|
1203
|
+
/**
|
|
1204
|
+
* Set a column's width to fit its content, as Excel's AutoFit does.
|
|
1205
|
+
*
|
|
1206
|
+
* Cells in hidden rows are ignored unless `includeHiddenRows` is set. A
|
|
1207
|
+
* merged cell counts as visible while any row of the merge is, since that is
|
|
1208
|
+
* where Excel draws it; a merge spanning several columns is not measured,
|
|
1209
|
+
* because its width cannot be attributed to one of them.
|
|
1210
|
+
*/
|
|
1211
|
+
export function autoFitColumn(ws, col, options) {
|
|
1204
1212
|
const colNum = typeof col === "string" ? colCache.l2n(col) : col;
|
|
1205
|
-
_autoFitColumnImpl(ws, colNum);
|
|
1213
|
+
_autoFitColumnImpl(ws, colNum, options);
|
|
1206
1214
|
return ws;
|
|
1207
1215
|
}
|
|
1208
|
-
export function autoFitColumns(ws,
|
|
1216
|
+
export function autoFitColumns(ws, startColOrOptions, endCol, rangeOptions) {
|
|
1217
|
+
const rangeGiven = startColOrOptions === null || typeof startColOrOptions !== "object";
|
|
1218
|
+
const startCol = rangeGiven ? startColOrOptions : undefined;
|
|
1219
|
+
const options = rangeGiven ? rangeOptions : startColOrOptions;
|
|
1209
1220
|
const dims = getSheetDimensions(ws);
|
|
1210
1221
|
if (!dims || dims.left === undefined) {
|
|
1211
1222
|
return ws;
|
|
@@ -1217,7 +1228,7 @@ export function autoFitColumns(ws, startCol, endCol) {
|
|
|
1217
1228
|
: dims.left;
|
|
1218
1229
|
const end = endCol != null ? (typeof endCol === "string" ? colCache.l2n(endCol) : endCol) : dims.right;
|
|
1219
1230
|
for (let c = start; c <= end; c++) {
|
|
1220
|
-
_autoFitColumnImpl(ws, c);
|
|
1231
|
+
_autoFitColumnImpl(ws, c, options);
|
|
1221
1232
|
}
|
|
1222
1233
|
return ws;
|
|
1223
1234
|
}
|
|
@@ -1237,8 +1248,45 @@ export function autoFitRows(ws, startRow, endRow) {
|
|
|
1237
1248
|
}
|
|
1238
1249
|
return ws;
|
|
1239
1250
|
}
|
|
1240
|
-
|
|
1251
|
+
/** A row that has never been created has default visibility. */
|
|
1252
|
+
function _isRowHidden(ws, rowNumber) {
|
|
1253
|
+
const row = ws._rows[rowNumber - 1];
|
|
1254
|
+
return row ? rowHidden(row) : false;
|
|
1255
|
+
}
|
|
1256
|
+
function _isColumnHidden(ws, colNum) {
|
|
1257
|
+
return ws._columns[colNum - 1]?.hidden === true;
|
|
1258
|
+
}
|
|
1259
|
+
function _anyRowVisible(ws, top, bottom) {
|
|
1260
|
+
for (let r = top; r <= bottom; r++) {
|
|
1261
|
+
if (!_isRowHidden(ws, r)) {
|
|
1262
|
+
return true;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
return false;
|
|
1266
|
+
}
|
|
1267
|
+
function _anyColumnVisible(ws, left, right) {
|
|
1268
|
+
for (let c = left; c <= right; c++) {
|
|
1269
|
+
if (!_isColumnHidden(ws, c)) {
|
|
1270
|
+
return true;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
return false;
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* The merged range a master cell heads, or `undefined` for any other cell.
|
|
1277
|
+
*
|
|
1278
|
+
* A merge's content is stored only in its master, yet it is displayed wherever
|
|
1279
|
+
* any part of the merge is. Auto-fit therefore decides a master's visibility
|
|
1280
|
+
* over this range rather than its own row or column — which is what keeps a
|
|
1281
|
+
* merge whose master is hidden, one inside a collapsed outline group say, from
|
|
1282
|
+
* being dropped.
|
|
1283
|
+
*/
|
|
1284
|
+
function _mergeRangeOf(ws, cell) {
|
|
1285
|
+
return cellIsMerged(cell) ? ws._merges[cell.address] : undefined;
|
|
1286
|
+
}
|
|
1287
|
+
function _autoFitColumnImpl(ws, colNum, options) {
|
|
1241
1288
|
const mdw = getMaxDigitWidth(); // default font MDW
|
|
1289
|
+
const includeHiddenRows = options?.includeHiddenRows === true;
|
|
1242
1290
|
// Check if this column is under an autofilter
|
|
1243
1291
|
const hasAutoFilter = _isColumnInAutoFilter(ws, colNum);
|
|
1244
1292
|
let maxWidthPx = 0;
|
|
@@ -1247,26 +1295,23 @@ function _autoFitColumnImpl(ws, colNum) {
|
|
|
1247
1295
|
if (!row) {
|
|
1248
1296
|
return;
|
|
1249
1297
|
}
|
|
1250
|
-
// Skip hidden rows — Excel excludes them from auto-fit
|
|
1251
|
-
if (rowHidden(row)) {
|
|
1252
|
-
return;
|
|
1253
|
-
}
|
|
1254
1298
|
const cell = rowFindCell(row, colNum);
|
|
1255
1299
|
if (!cell) {
|
|
1256
1300
|
return;
|
|
1257
1301
|
}
|
|
1258
1302
|
// Skip merged cell slaves — the content belongs to the master cell.
|
|
1259
|
-
// For the master cell of a multi-column merge, skip too (the width
|
|
1260
|
-
// should not be attributed to a single column).
|
|
1261
1303
|
if (cellType(cell) === Enums.ValueType.Merge) {
|
|
1262
1304
|
return;
|
|
1263
1305
|
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1306
|
+
const merge = _mergeRangeOf(ws, cell);
|
|
1307
|
+
// A multi-column merge's width should not be attributed to one column.
|
|
1308
|
+
if (merge && merge.left !== merge.right) {
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
// Skip content in hidden rows — Excel excludes it from auto-fit
|
|
1312
|
+
if (!includeHiddenRows &&
|
|
1313
|
+
(merge ? !_anyRowVisible(ws, merge.top, merge.bottom) : rowHidden(row))) {
|
|
1314
|
+
return;
|
|
1270
1315
|
}
|
|
1271
1316
|
// Skip shrinkToFit cells — they adapt to the column, not vice versa
|
|
1272
1317
|
if (cellAlignment(cell)?.shrinkToFit) {
|
|
@@ -1303,19 +1348,17 @@ function _autoFitRowImpl(ws, rowNumber) {
|
|
|
1303
1348
|
if (cellType(cell) === Enums.ValueType.Merge) {
|
|
1304
1349
|
return;
|
|
1305
1350
|
}
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
return;
|
|
1311
|
-
}
|
|
1351
|
+
const merge = _mergeRangeOf(ws, cell);
|
|
1352
|
+
// A multi-row merge's height should not be attributed to one row.
|
|
1353
|
+
if (merge && merge.top !== merge.bottom) {
|
|
1354
|
+
return;
|
|
1312
1355
|
}
|
|
1313
|
-
// Skip
|
|
1314
|
-
const col =
|
|
1315
|
-
if (col
|
|
1356
|
+
// Skip content in hidden columns
|
|
1357
|
+
const col = cellCol(cell);
|
|
1358
|
+
if (merge ? !_anyColumnVisible(ws, merge.left, merge.right) : _isColumnHidden(ws, col)) {
|
|
1316
1359
|
return;
|
|
1317
1360
|
}
|
|
1318
|
-
const columnWidthPx = _getColumnContentWidthForCell(ws, cell, mdw);
|
|
1361
|
+
const columnWidthPx = _getColumnContentWidthForCell(ws, cell, mdw, merge?.left ?? col, merge?.right ?? col);
|
|
1319
1362
|
const heightPt = getCellHeightPt(cellView(cell), mdw, columnWidthPx);
|
|
1320
1363
|
if (heightPt > maxHeightPt) {
|
|
1321
1364
|
maxHeightPt = heightPt;
|
|
@@ -1326,14 +1369,23 @@ function _autoFitRowImpl(ws, rowNumber) {
|
|
|
1326
1369
|
row.customHeight = true;
|
|
1327
1370
|
}
|
|
1328
1371
|
}
|
|
1329
|
-
function _getColumnContentWidthForCell(ws, cell, mdw) {
|
|
1372
|
+
function _getColumnContentWidthForCell(ws, cell, mdw, left, right) {
|
|
1330
1373
|
if (!cellAlignment(cell)?.wrapText) {
|
|
1331
1374
|
return undefined;
|
|
1332
1375
|
}
|
|
1333
|
-
//
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1376
|
+
// Text wraps across the visible columns the cell spans, with the cell padding
|
|
1377
|
+
// charged once for the whole merge rather than once per column.
|
|
1378
|
+
let totalPx = 0;
|
|
1379
|
+
for (let c = left; c <= right; c++) {
|
|
1380
|
+
if (!_isColumnHidden(ws, c)) {
|
|
1381
|
+
totalPx += charWidthToPixel(_columnWidthChars(ws, c), mdw);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
return Math.max(0, totalPx - getPixelPadding(mdw));
|
|
1385
|
+
}
|
|
1386
|
+
/** Width of a column in characters, without creating it as a side effect. */
|
|
1387
|
+
function _columnWidthChars(ws, colNum) {
|
|
1388
|
+
return ws._columns[colNum - 1]?.width ?? ws.properties.defaultColWidth ?? 9;
|
|
1337
1389
|
}
|
|
1338
1390
|
function _isColumnInAutoFilter(ws, colNum) {
|
|
1339
1391
|
if (!ws.autoFilter) {
|
|
@@ -41,7 +41,7 @@ import { erDiagramNodes, erNodeSizer, erToFlowchart } from "./render/er.js";
|
|
|
41
41
|
import { flowchartNodes } from "./render/flowchart.js";
|
|
42
42
|
import { ganttDrawList } from "./render/gantt.js";
|
|
43
43
|
import { blockDrawList, c4DiagramNodes, c4NodeSizer, c4ToFlowchart, requirementDiagramNodes, requirementNodeSizer, requirementToFlowchart } from "./render/model.js";
|
|
44
|
-
import { backdrop } from "./render/shared.js";
|
|
44
|
+
import { backdrop, centredText } from "./render/shared.js";
|
|
45
45
|
import { pieDrawList, sequenceDrawList } from "./render/simple.js";
|
|
46
46
|
import { stateToFlowchart } from "./render/state.js";
|
|
47
47
|
import { journeyDrawList, timelineDrawList } from "./render/track.js";
|
|
@@ -177,3 +177,30 @@ export function mermaidToSvg(source, options = {}) {
|
|
|
177
177
|
}
|
|
178
178
|
export { MermaidSyntaxError, parseMermaid };
|
|
179
179
|
export { layoutFlowchart };
|
|
180
|
+
/**
|
|
181
|
+
* Draw a laid-out diagram yourself, in the colours and at the text positions the built-in
|
|
182
|
+
* renderer would have used.
|
|
183
|
+
*
|
|
184
|
+
* {@link layoutFlowchart} is public, so "use the layout and render it your own way" is a
|
|
185
|
+
* supported route — a caller wanting a different node shape, an annotation of their own, or
|
|
186
|
+
* an HTML/canvas target rather than a `DrawList` takes it. It was half-open: the geometry
|
|
187
|
+
* came back, and everything needed to *paint* it was internal.
|
|
188
|
+
*
|
|
189
|
+
* {@link Theme} was published without either the function that produces one or any signature
|
|
190
|
+
* accepting one, which made it a name for a thing a consumer could not obtain, could not
|
|
191
|
+
* construct — ten required `Rgba01` fields plus a `paletteText` callback — and could not pass
|
|
192
|
+
* anywhere. `resolveTheme` is what it was missing: {@link MermaidRenderOptions.theme} takes
|
|
193
|
+
* CSS strings, and this is the same resolution the renderer applies to them, so a
|
|
194
|
+
* self-rendered diagram matches one from {@link mermaidToSvg} instead of approximating it.
|
|
195
|
+
* Without it the only route to the palette was to copy the hex literals out of this module's
|
|
196
|
+
* source and hope they did not move.
|
|
197
|
+
*
|
|
198
|
+
* `centredText` is exported rather than the two constants behind it (`LINE_HEIGHT`,
|
|
199
|
+
* `BASELINE_SHIFT`) on purpose. A display list positions text by its baseline, because that
|
|
200
|
+
* is the one thing every backend can honour, so stacking `NodeBox.lines` in the middle of a
|
|
201
|
+
* box is arithmetic the caller has to do — and handing out the constants invites a ninth copy
|
|
202
|
+
* of that arithmetic, free to disagree with the eight inside. The box shape it takes is
|
|
203
|
+
* exactly what the layout already returns: `NodeBox`, `GroupBox` and `EdgeRoute.label` all
|
|
204
|
+
* carry `x`/`y`/`width`/`height`, and the last two carry the wrapped `lines` with them.
|
|
205
|
+
*/
|
|
206
|
+
export { centredText, resolveTheme };
|
|
@@ -1110,11 +1110,23 @@ function buildMergeMap(sheet) {
|
|
|
1110
1110
|
}
|
|
1111
1111
|
for (const rangeStr of merges) {
|
|
1112
1112
|
const range = parseRangeRef(rangeStr);
|
|
1113
|
+
const top = range.s.r + 1;
|
|
1114
|
+
const left = range.s.c + 1;
|
|
1115
|
+
const bottom = range.e.r + 1;
|
|
1116
|
+
const right = range.e.c + 1;
|
|
1117
|
+
const rowShown = (r) => !sheet.rows.get(r)?.hidden;
|
|
1118
|
+
const colShown = (c) => !sheet.columns.get(c)?.hidden;
|
|
1119
|
+
const [shownTop, shownBottom] = shownSpan(top, bottom, rowShown);
|
|
1120
|
+
const [shownLeft, shownRight] = shownSpan(left, right, colShown);
|
|
1113
1121
|
const region = {
|
|
1114
|
-
top
|
|
1115
|
-
left
|
|
1116
|
-
bottom
|
|
1117
|
-
right
|
|
1122
|
+
top,
|
|
1123
|
+
left,
|
|
1124
|
+
bottom,
|
|
1125
|
+
right,
|
|
1126
|
+
shownTop,
|
|
1127
|
+
shownLeft,
|
|
1128
|
+
shownBottom,
|
|
1129
|
+
shownRight
|
|
1118
1130
|
};
|
|
1119
1131
|
for (let r = region.top; r <= region.bottom; r++) {
|
|
1120
1132
|
for (let c = region.left; c <= region.right; c++) {
|
|
@@ -1124,6 +1136,21 @@ function buildMergeMap(sheet) {
|
|
|
1124
1136
|
}
|
|
1125
1137
|
return map;
|
|
1126
1138
|
}
|
|
1139
|
+
/**
|
|
1140
|
+
* The first and last tracks in `first..last` that are shown, or the range
|
|
1141
|
+
* itself when none is.
|
|
1142
|
+
*/
|
|
1143
|
+
function shownSpan(first, last, shown) {
|
|
1144
|
+
let lo = first;
|
|
1145
|
+
while (lo < last && !shown(lo)) {
|
|
1146
|
+
lo++;
|
|
1147
|
+
}
|
|
1148
|
+
let hi = last;
|
|
1149
|
+
while (hi > lo && !shown(hi)) {
|
|
1150
|
+
hi--;
|
|
1151
|
+
}
|
|
1152
|
+
return shown(lo) ? [lo, hi] : [first, last];
|
|
1153
|
+
}
|
|
1127
1154
|
/**
|
|
1128
1155
|
* How far a merged region reaches along one axis of one page, starting at the
|
|
1129
1156
|
* page track `at`.
|
|
@@ -1292,20 +1319,25 @@ function mergePieceInput(piece, sheet) {
|
|
|
1292
1319
|
const { region } = piece;
|
|
1293
1320
|
const borderAt = (row, col) => sheet.rows.get(row)?.cells.get(col)?.style?.border;
|
|
1294
1321
|
// Excel formats the whole region from the master and draws its value once, in
|
|
1295
|
-
// the piece that
|
|
1296
|
-
// repeat of its contents.
|
|
1322
|
+
// the piece that begins where the region is first displayed — the others are
|
|
1323
|
+
// the same box continued, not a repeat of its contents. That is the master's
|
|
1324
|
+
// own piece unless the master's row or column is hidden.
|
|
1297
1325
|
const master = sheet.rows.get(region.top)?.cells.get(region.left);
|
|
1298
|
-
const
|
|
1326
|
+
const holdsValue = piece.top === region.shownTop && piece.left === region.shownLeft;
|
|
1327
|
+
// Each edge is the region's own only where the piece reaches its displayed
|
|
1328
|
+
// boundary; the style is still read from the boundary cells that own it.
|
|
1299
1329
|
return {
|
|
1300
1330
|
styleCell: master,
|
|
1301
|
-
valueCell:
|
|
1331
|
+
valueCell: holdsValue ? master : undefined,
|
|
1302
1332
|
colSpan: piece.colSpan,
|
|
1303
1333
|
rowSpan: piece.rowSpan,
|
|
1304
1334
|
borders: {
|
|
1305
|
-
top: piece.top === region.
|
|
1306
|
-
bottom: piece.bottom === region.
|
|
1307
|
-
|
|
1308
|
-
|
|
1335
|
+
top: piece.top === region.shownTop ? borderAt(region.top, piece.left)?.top : undefined,
|
|
1336
|
+
bottom: piece.bottom === region.shownBottom
|
|
1337
|
+
? borderAt(region.bottom, piece.left)?.bottom
|
|
1338
|
+
: undefined,
|
|
1339
|
+
left: piece.left === region.shownLeft ? borderAt(piece.top, region.left)?.left : undefined,
|
|
1340
|
+
right: piece.right === region.shownRight ? borderAt(piece.top, region.right)?.right : undefined
|
|
1309
1341
|
}
|
|
1310
1342
|
};
|
|
1311
1343
|
}
|