documonster 0.18.1 → 0.18.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.
- package/README.md +1 -1
- package/README_zh.md +1 -1
- package/dist/esm/modules/excel/xlsb/pivot-cache.js +7 -15
- package/dist/esm/modules/excel/xlsb/pivot-model.js +12 -1
- package/dist/esm/modules/excel/xlsb/pivot-view.js +58 -23
- package/dist/esm/modules/excel/xlsx/xform/pivot-table/cache-field-xform.js +2 -1
- package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-cache-records-xform.js +2 -1
- package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +214 -125
- 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 +53 -53
- 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 +1 -1
- package/dist/iife/documonster.pdf.iife.min.js +1 -1
- package/dist/iife/documonster.stream.iife.min.js +1 -1
- package/dist/iife/documonster.word.iife.min.js +58 -58
- package/dist/iife/documonster.xml.iife.min.js +1 -1
- package/dist/types/modules/excel/core/pivot-table.d.ts +18 -0
- package/dist/types/modules/excel/xlsb/pivot-view.d.ts +16 -0
- package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +45 -3
- 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.18.
|
|
214
|
+
<script src="https://unpkg.com/documonster@0.18.2/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.18.
|
|
201
|
+
<script src="https://unpkg.com/documonster@0.18.2/dist/iife/documonster.excel.iife.min.js"></script>
|
|
202
202
|
<script>
|
|
203
203
|
const { Workbook, Cell } = Documonster.Excel;
|
|
204
204
|
const wb = Workbook.create();
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* this exact sequence. An earlier version of this module's documentation claimed no such authority existed;
|
|
13
13
|
* it does, in the same chapter as the filter example that was already being relied on.
|
|
14
14
|
*/
|
|
15
|
+
import { encodeWideString } from "./binary.js";
|
|
15
16
|
import { BinaryWriter, concatUint8Arrays } from "../../../utils/binary.js";
|
|
16
17
|
import { dateToExcel } from "#platform/utils/utils";
|
|
17
18
|
/**
|
|
@@ -21,15 +22,6 @@ import { dateToExcel } from "#platform/utils/utils";
|
|
|
21
22
|
*/
|
|
22
23
|
const CACHE_APP_VERSION = 8;
|
|
23
24
|
const CACHE_MIN_VERSION = 3;
|
|
24
|
-
/** An `XLWideString`: a four-byte character count, then UTF-16. */
|
|
25
|
-
function wideString(value) {
|
|
26
|
-
const characters = [...value];
|
|
27
|
-
const writer = new BinaryWriter().writeUint32(characters.length);
|
|
28
|
-
for (const character of characters.join("")) {
|
|
29
|
-
writer.writeUint16(character.charCodeAt(0));
|
|
30
|
-
}
|
|
31
|
-
return writer.toUint8Array();
|
|
32
|
-
}
|
|
33
25
|
/** An `RfX`: four unsigned 32-bit bounds, first row, last row, first column, last column. */
|
|
34
26
|
function rfx(rowFirst, rowLast, columnFirst, columnLast) {
|
|
35
27
|
return new BinaryWriter()
|
|
@@ -66,7 +58,7 @@ export function pivotCacheIdRecords(caches) {
|
|
|
66
58
|
"BrtBeginPivotCacheID",
|
|
67
59
|
concatUint8Arrays([
|
|
68
60
|
new BinaryWriter().writeUint32(cache.cacheId).toUint8Array(),
|
|
69
|
-
|
|
61
|
+
encodeWideString(cache.relationshipId)
|
|
70
62
|
])
|
|
71
63
|
]);
|
|
72
64
|
records.push(["BrtEndPivotCacheID", undefined]);
|
|
@@ -180,7 +172,7 @@ function cacheDefinitionPayload(cache) {
|
|
|
180
172
|
.writeUint32(cache.records.length);
|
|
181
173
|
const parts = [writer.toUint8Array()];
|
|
182
174
|
if (hasRecords) {
|
|
183
|
-
parts.push(
|
|
175
|
+
parts.push(encodeWideString(cache.recordsRelationshipId));
|
|
184
176
|
}
|
|
185
177
|
// Present because `fLoadRefreshedWho` is 0 — see the note above.
|
|
186
178
|
parts.push(new BinaryWriter().writeUint32(0).toUint8Array());
|
|
@@ -203,7 +195,7 @@ function sourceRangePayload(cache) {
|
|
|
203
195
|
// single sheet, which is what makes `sheetName` meaningful.
|
|
204
196
|
.writeUint8(0x02)
|
|
205
197
|
.toUint8Array(),
|
|
206
|
-
|
|
198
|
+
encodeWideString(cache.sheetName),
|
|
207
199
|
rfx(cache.range.rowFirst, cache.range.rowLast, cache.range.columnFirst, cache.range.columnLast)
|
|
208
200
|
]);
|
|
209
201
|
}
|
|
@@ -228,7 +220,7 @@ function cacheFieldPayload(field) {
|
|
|
228
220
|
// `cIsxtmps` — member-property field indices, which only an OLAP cache has.
|
|
229
221
|
.writeUint32(0)
|
|
230
222
|
.toUint8Array(),
|
|
231
|
-
|
|
223
|
+
encodeWideString(field.name)
|
|
232
224
|
]);
|
|
233
225
|
}
|
|
234
226
|
/**
|
|
@@ -393,7 +385,7 @@ function sharedItemRecord(item) {
|
|
|
393
385
|
// A value none of the above: written as text. `String(item)` on a `Date` is what used to happen here, and
|
|
394
386
|
// it put `"Mon Jan 15 2024 00:00:00 GMT+0800 (China Standard Time)"` into the cache — a sentence whose
|
|
395
387
|
// content depends on the machine's locale and timezone.
|
|
396
|
-
return ["BrtPCDIString",
|
|
388
|
+
return ["BrtPCDIString", encodeWideString(String(item))];
|
|
397
389
|
}
|
|
398
390
|
/**
|
|
399
391
|
* `PCDIDateTime`, MS-XLSB 2.5.101 — a calendar date and a wall-clock time, not a serial number.
|
|
@@ -470,7 +462,7 @@ function cacheRecordPayload(row, fields) {
|
|
|
470
462
|
parts.push(encodePcdiDateTime(value));
|
|
471
463
|
continue;
|
|
472
464
|
}
|
|
473
|
-
parts.push(
|
|
465
|
+
parts.push(encodeWideString(value === null || value === undefined ? "" : String(value)));
|
|
474
466
|
}
|
|
475
467
|
return concatUint8Arrays(parts);
|
|
476
468
|
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { METRIC_DISPLAY_NAMES, VALID_SUBTOTALS } from "../core/pivot-table-types.js";
|
|
17
17
|
import { decodeCell } from "../utils/address.js";
|
|
18
18
|
import { PIT_DATA, PIT_DEFAULT, PIT_GRAND } from "./pivot-view.js";
|
|
19
|
+
import { parseXsdBoolean } from "../xlsx/xform/xsd-values.js";
|
|
19
20
|
/**
|
|
20
21
|
* Reduce one source value to what a cache record holds for it: an index into the field's shared items, or the
|
|
21
22
|
* value itself when the field has none.
|
|
@@ -195,7 +196,17 @@ export function pivotParts(pivot, recordsRelationshipId) {
|
|
|
195
196
|
`${METRIC_DISPLAY_NAMES[subtotal]} of ${pivot.cacheFields[field]?.name ?? ""}`
|
|
196
197
|
};
|
|
197
198
|
}),
|
|
198
|
-
|
|
199
|
+
// A loaded table carries its own view strings; a new one has only the default data caption. They were
|
|
200
|
+
// all dropped here, so converting a workbook to XLSB reset every caption the user had typed.
|
|
201
|
+
dataCaption: pivot.dataCaption ?? "Values",
|
|
202
|
+
grandTotalCaption: pivot.grandTotalCaption,
|
|
203
|
+
rowHeaderCaption: pivot.rowHeaderCaption,
|
|
204
|
+
colHeaderCaption: pivot.colHeaderCaption,
|
|
205
|
+
showError: parseXsdBoolean(pivot.showError),
|
|
206
|
+
errorCaption: pivot.errorCaption,
|
|
207
|
+
showMissing: parseXsdBoolean(pivot.showMissing),
|
|
208
|
+
missingCaption: pivot.missingCaption,
|
|
209
|
+
tag: pivot.tag
|
|
199
210
|
};
|
|
200
211
|
return { cache, view };
|
|
201
212
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* and only the declared `0x3B` reveals that the trailing `reserved` is inside the `PivotNumFmt` rather than
|
|
12
12
|
* beside it.
|
|
13
13
|
*/
|
|
14
|
+
import { encodeWideString } from "./binary.js";
|
|
14
15
|
import { encodeTableStyleClient } from "./table-style.js";
|
|
15
16
|
import { BinaryWriter, concatUint8Arrays } from "../../../utils/binary.js";
|
|
16
17
|
/**
|
|
@@ -101,15 +102,6 @@ export const PIT_GRAND = 0x0d;
|
|
|
101
102
|
const PIVOT_APP_VERSION = 8;
|
|
102
103
|
/** `bVerSxUpdateableMin` — the oldest version allowed to update the view. Three, as Excel writes. */
|
|
103
104
|
const PIVOT_MIN_VERSION = 3;
|
|
104
|
-
/** An `XLWideString`: a four-byte character count, then UTF-16. */
|
|
105
|
-
function wideString(value) {
|
|
106
|
-
const characters = [...value];
|
|
107
|
-
const writer = new BinaryWriter().writeUint32(characters.length);
|
|
108
|
-
for (const character of characters.join("")) {
|
|
109
|
-
writer.writeUint16(character.charCodeAt(0));
|
|
110
|
-
}
|
|
111
|
-
return writer.toUint8Array();
|
|
112
|
-
}
|
|
113
105
|
/** A counted array of 32-bit field indices, which four of these collections are. */
|
|
114
106
|
function indexArray(indices) {
|
|
115
107
|
const writer = new BinaryWriter().writeUint32(indices.length);
|
|
@@ -300,13 +292,24 @@ export function pivotViewRecords(view) {
|
|
|
300
292
|
/**
|
|
301
293
|
* `BrtBeginSXView` — MS-XLSB 2.4.278. Thirty-two fixed bytes and up to eleven conditional strings.
|
|
302
294
|
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
295
|
+
* Every string after `irstName` has a **presence** flag, and two of them are **inverted**: `fDisplayData`
|
|
296
|
+
* (bit 19 of the second word) says `irstData` *is* present and MUST be 1, while `fEmptyDisplayErrorString`
|
|
297
|
+
* and `fEmptyDisplayNullString` say their strings are *absent*. So a record without those strings has to
|
|
298
|
+
* *set* the two "empty" bits — reading them as ordinary "has a value" flags produces a record whose string
|
|
299
|
+
* fields are read from whatever follows it.
|
|
300
|
+
*
|
|
301
|
+
* Note the specification's own slip: it says `irstColHdrName` is governed by `fUseRwHdrName`. Each header
|
|
302
|
+
* string has its own flag (`fUseColHdrName` for the column one), and the column string comes first.
|
|
308
303
|
*/
|
|
309
304
|
function viewPayload(view) {
|
|
305
|
+
// The conditional strings, each paired with the flag that declares it. A flag and its string must agree or
|
|
306
|
+
// every later string is read from the wrong offset, so both come from the same `!== undefined` test.
|
|
307
|
+
const grand = limitedCaption(view.grandTotalCaption, SHORT_STRING_LIMIT);
|
|
308
|
+
const error = limitedCaption(view.errorCaption, SHORT_STRING_LIMIT);
|
|
309
|
+
const missing = limitedCaption(view.missingCaption, SHORT_STRING_LIMIT);
|
|
310
|
+
const tag = limitedCaption(view.tag, SHORT_STRING_LIMIT);
|
|
311
|
+
const columnHeader = limitedCaption(view.colHeaderCaption, HEADER_STRING_LIMIT);
|
|
312
|
+
const rowHeader = limitedCaption(view.rowHeaderCaption, HEADER_STRING_LIMIT);
|
|
310
313
|
// Every value below was read out of an XLSB that Excel wrote for this pivot table. Where a bit's purpose is
|
|
311
314
|
// known it is named; where it is not, it is still written, because a view Excel will not open is worth less
|
|
312
315
|
// than a view carrying a flag nobody here can explain.
|
|
@@ -325,20 +328,25 @@ function viewPayload(view) {
|
|
|
325
328
|
(1 << 6) | // fEnableFieldDialog
|
|
326
329
|
(1 << 7) | // fPreserveFormatting
|
|
327
330
|
(1 << 8) | // fAutoFormat
|
|
328
|
-
(1 <<
|
|
331
|
+
(view.showError === true ? 1 << 9 : 0) | // fDisplayErrorString
|
|
332
|
+
(view.showMissing === false ? 0 : 1 << 10) | // fDisplayNullString
|
|
329
333
|
(1 << 13) | // fRwGrand
|
|
330
334
|
(1 << 14) | // fColGrand
|
|
331
335
|
(1 << 17) | // fRepeatItemsOnEachPrintedPage
|
|
332
336
|
(1 << 19) | // fDisplayData — MUST be 1, `irstData` follows
|
|
333
|
-
(1 <<
|
|
337
|
+
(grand !== undefined ? 1 << 20 : 0) | // fDisplayGrand — `irstGrand` follows
|
|
338
|
+
(1 << 29) | // ibitAtrProt
|
|
339
|
+
(tag !== undefined ? 1 << 30 : 0) // fDisplayTag — `irstTag` follows
|
|
334
340
|
)
|
|
335
341
|
// Third flag word, `0x000002D0`. `fCompactData` is **clear**: it was set here to "match what Excel writes
|
|
336
342
|
// for a new pivot table", and Excel writes it clear for this one — as does this library's XLSX writer,
|
|
337
343
|
// which declares `compactData="0"` in the file Excel opens.
|
|
338
344
|
.writeUint32((1 << (36 - 32)) | // fNewDropZones
|
|
339
|
-
(1 << (38 - 32)) | // fEmptyDisplayErrorString
|
|
340
|
-
(1 << (39 - 32)) | // fEmptyDisplayNullString
|
|
341
|
-
(1 << (41 - 32)) // fSingleFilterPerField
|
|
345
|
+
(error === undefined ? 1 << (38 - 32) : 0) | // fEmptyDisplayErrorString — inverted
|
|
346
|
+
(missing === undefined ? 1 << (39 - 32) : 0) | // fEmptyDisplayNullString — inverted
|
|
347
|
+
(1 << (41 - 32)) | // fSingleFilterPerField
|
|
348
|
+
(rowHeader !== undefined ? 1 << (42 - 32) : 0) | // fUseRwHdrName
|
|
349
|
+
(columnHeader !== undefined ? 1 << (43 - 32) : 0) // fUseColHdrName
|
|
342
350
|
)
|
|
343
351
|
// `sxaxis4Data`: the axis the data field sits on — see {@link dataFieldAxis} for why this is a constant.
|
|
344
352
|
.writeUint8(dataFieldAxis())
|
|
@@ -356,13 +364,40 @@ function viewPayload(view) {
|
|
|
356
364
|
// `dwCrtFmtId`: the next pivot-chart identifier. 0 because no pivot chart is written.
|
|
357
365
|
.writeUint32(0)
|
|
358
366
|
.writeUint32(view.cacheId);
|
|
367
|
+
// In the record's order: name, data, grand, error, null, page-field style, table style, vacate style, tag,
|
|
368
|
+
// column header, row header. The three styles are never written, so their flags stay clear.
|
|
369
|
+
const strings = [
|
|
370
|
+
view.name,
|
|
371
|
+
// `irstData`, present because `fDisplayData` is 1.
|
|
372
|
+
limitedCaption(view.dataCaption, SHORT_STRING_LIMIT),
|
|
373
|
+
grand,
|
|
374
|
+
error,
|
|
375
|
+
missing,
|
|
376
|
+
tag,
|
|
377
|
+
columnHeader,
|
|
378
|
+
rowHeader
|
|
379
|
+
];
|
|
359
380
|
return concatUint8Arrays([
|
|
360
381
|
writer.toUint8Array(),
|
|
361
|
-
|
|
362
|
-
// `irstData`, present because `fDisplayData` is 1.
|
|
363
|
-
wideString(view.dataCaption)
|
|
382
|
+
...strings.flatMap(value => (value === undefined ? [] : [encodeWideString(value)]))
|
|
364
383
|
]);
|
|
365
384
|
}
|
|
385
|
+
/** `XLWideString` limit for the view's captions — 255 characters, as Excel's own user interface enforces. */
|
|
386
|
+
const SHORT_STRING_LIMIT = 255;
|
|
387
|
+
/** `irstRwHdrName` / `irstColHdrName` may be longer when `bVerSxMacro` is 3 or more, which this writer's is. */
|
|
388
|
+
const HEADER_STRING_LIMIT = 32767;
|
|
389
|
+
/**
|
|
390
|
+
* A caption cut to the length the record allows. XLSX has no such limit, so a longer caption from a loaded
|
|
391
|
+
* file has to be shortened rather than written into a record a reader is entitled to reject; the cut is made
|
|
392
|
+
* on a code-unit boundary that does not split a surrogate pair.
|
|
393
|
+
*/
|
|
394
|
+
function limitedCaption(value, limit) {
|
|
395
|
+
if (value === undefined || value.length <= limit) {
|
|
396
|
+
return value;
|
|
397
|
+
}
|
|
398
|
+
const end = /[\uD800-\uDBFF]/.test(value[limit - 1]) ? limit - 1 : limit;
|
|
399
|
+
return value.slice(0, end);
|
|
400
|
+
}
|
|
366
401
|
/**
|
|
367
402
|
* `BrtBeginSXLocation` — MS-XLSB 2.4.259. The body's extent, then four positions inside it.
|
|
368
403
|
*
|
|
@@ -670,6 +705,6 @@ function dataItemPayload(item) {
|
|
|
670
705
|
// `fLoadDisplayName` — the caption follows.
|
|
671
706
|
.writeUint8(1)
|
|
672
707
|
.toUint8Array(),
|
|
673
|
-
|
|
708
|
+
encodeWideString(item.caption)
|
|
674
709
|
]);
|
|
675
710
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { pivotError } from "../../../core/pivot-table-types.js";
|
|
2
2
|
import { BaseXform } from "../base-xform.js";
|
|
3
3
|
import { RawXmlCollector } from "./raw-xml-collector.js";
|
|
4
|
+
import { parseXsdBoolean } from "../xsd-values.js";
|
|
4
5
|
import { parseOoxmlDate } from "#platform/utils/utils";
|
|
5
6
|
/** Attribute keys on <cacheField> that are individually parsed (not collected into extraAttrs). */
|
|
6
7
|
const KNOWN_CACHE_FIELD_KEYS = new Set(["name", "numFmtId"]);
|
|
@@ -189,7 +190,7 @@ function parseSharedItemValue(tag, attributes) {
|
|
|
189
190
|
}
|
|
190
191
|
return parseFloat(attributes.v);
|
|
191
192
|
case "b":
|
|
192
|
-
return attributes.v
|
|
193
|
+
return parseXsdBoolean(attributes.v) ?? false;
|
|
193
194
|
case "e":
|
|
194
195
|
return pivotError(attributes.v ?? "");
|
|
195
196
|
case "m":
|
|
@@ -2,6 +2,7 @@ import { isPivotError } from "../../../core/pivot-table-types.js";
|
|
|
2
2
|
import { PivotTableError } from "../../../errors.js";
|
|
3
3
|
import { BaseXform } from "../base-xform.js";
|
|
4
4
|
import { formatDateForExcel } from "./cache-field.js";
|
|
5
|
+
import { parseXsdBoolean } from "../xsd-values.js";
|
|
5
6
|
import { parseOoxmlDate } from "#platform/utils/utils";
|
|
6
7
|
import { xmlEncode } from "../../../../xml/encode.js";
|
|
7
8
|
import { StdDocAttributes } from "../../../../xml/writer.js";
|
|
@@ -236,7 +237,7 @@ class PivotCacheRecordsXform extends BaseXform {
|
|
|
236
237
|
if (this.currentRecord) {
|
|
237
238
|
this.currentRecord.push({
|
|
238
239
|
type: "b",
|
|
239
|
-
value: attributes.v
|
|
240
|
+
value: parseXsdBoolean(attributes.v) ?? false
|
|
240
241
|
});
|
|
241
242
|
}
|
|
242
243
|
break;
|