xml-xlsx-lite
Version:
🚀 Lightweight Excel XLSX generator with full Excel features: dynamic pivot tables, charts, styles, and Chinese support. Fast, TypeScript-friendly Excel file creation library. | 輕量級 Excel XLSX 生成器,支援樞紐分析表、圖表、樣式,完整繁體中文支援。
1,576 lines (1,563 loc) • 150 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var JSZip2 = require('jszip');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var JSZip2__default = /*#__PURE__*/_interopDefault(JSZip2);
// xml-xlsx-lite – Minimal XLSX writer using raw XML + JSZip
// https://github.com/mikemikex1/xml-xlsx-lite
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
// src/utils.ts
var COL_A_CODE = "A".charCodeAt(0);
var EXCEL_EPOCH = new Date(Date.UTC(1899, 11, 30));
function colToNumber(col) {
let n = 0;
for (let i = 0; i < col.length; i++) {
n = n * 26 + (col.charCodeAt(i) - COL_A_CODE + 1);
}
return n;
}
function numberToCol(n) {
let col = "";
while (n > 0) {
const rem = (n - 1) % 26;
col = String.fromCharCode(COL_A_CODE + rem) + col;
n = Math.floor((n - 1) / 26);
}
return col;
}
function parseAddress(addr) {
const m = /^([A-Z]+)(\d+)$/.exec(addr.toUpperCase());
if (!m) throw new Error(`Invalid cell address: ${addr}`);
return { col: colToNumber(m[1]), row: parseInt(m[2], 10) };
}
function addrFromRC(row, col) {
return `${numberToCol(col)}${row}`;
}
function isDate(val) {
return val instanceof Date;
}
function excelSerialFromDate(d) {
const msPerDay = 24 * 60 * 60 * 1e3;
const diff = (Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()) - EXCEL_EPOCH.getTime()) / msPerDay;
return diff;
}
function getCellType(value) {
if (value === null || value === void 0) return null;
if (typeof value === "number") return "n";
if (typeof value === "boolean") return "b";
if (isDate(value)) return "d";
if (typeof value === "string") return "inlineStr";
return "inlineStr";
}
function escapeXmlText(str) {
return String(str).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
}
function escapeXmlAttr(str) {
return escapeXmlText(str);
}
function hashPassword(password) {
let hash = 0;
for (let i = 0; i < password.length; i++) {
const char = password.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
return Math.abs(hash).toString(16);
}
function verifyPassword(password, hash) {
return hashPassword(password) === hash;
}
function generateUniqueId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
// src/cell.ts
var CellModel = class {
constructor(address) {
this.address = address;
this.value = null;
this.type = null;
this.options = {};
}
/**
* 設定儲存格值
*/
setValue(value) {
this.value = value;
}
/**
* 設定儲存格選項
*/
setOptions(options) {
this.options = { ...this.options, ...options };
}
/**
* 清除儲存格內容
*/
clear() {
this.value = null;
this.type = null;
this.options = {};
}
/**
* 檢查儲存格是否為空
*/
isEmpty() {
return this.value === null || this.value === void 0 || this.value === "";
}
/**
* 取得儲存格顯示值
*/
getDisplayValue() {
if (this.value === null || this.value === void 0) return "";
if (this.value instanceof Date) return this.value.toLocaleDateString();
return String(this.value);
}
};
// src/protection.ts
var WorksheetProtection = class {
constructor() {
this._isProtected = false;
this._passwordHash = null;
this._options = {};
this._options = {
selectLockedCells: false,
selectUnlockedCells: true,
formatCells: false,
formatColumns: false,
formatRows: false,
insertColumns: false,
insertRows: false,
insertHyperlinks: false,
deleteColumns: false,
deleteRows: false,
sort: false,
autoFilter: false,
pivotTables: false,
objects: false,
scenarios: false
};
}
/**
* 保護工作表
*/
protect(password, options) {
this._isProtected = true;
if (password) {
this._passwordHash = hashPassword(password);
}
if (options) {
this._options = { ...this._options, ...options };
}
}
/**
* 解除工作表保護
*/
unprotect(password) {
if (this._isProtected && this._passwordHash && password) {
if (!verifyPassword(password, this._passwordHash)) {
throw new Error("Incorrect password");
}
}
this._isProtected = false;
this._passwordHash = null;
}
/**
* 檢查工作表是否受保護
*/
isProtected() {
return this._isProtected;
}
/**
* 取得保護選項
*/
getProtectionOptions() {
if (!this._isProtected) return null;
return { ...this._options };
}
/**
* 檢查操作是否被允許
*/
isOperationAllowed(operation) {
if (!this._isProtected) return true;
return this._options[operation] || false;
}
/**
* 驗證密碼
*/
validatePassword(password) {
if (!this._passwordHash) return true;
return verifyPassword(password, this._passwordHash);
}
};
var WorkbookProtection = class {
constructor() {
this._isProtected = false;
this._passwordHash = null;
this._options = {};
this._options = {
structure: false,
windows: false
};
}
/**
* 保護工作簿
*/
protect(password, options) {
this._isProtected = true;
if (password) {
this._passwordHash = hashPassword(password);
}
if (options) {
this._options = { ...this._options, ...options };
}
}
/**
* 解除工作簿保護
*/
unprotect(password) {
if (this._isProtected && this._passwordHash && password) {
if (!verifyPassword(password, this._passwordHash)) {
throw new Error("Incorrect password");
}
}
this._isProtected = false;
this._passwordHash = null;
}
/**
* 檢查工作簿是否受保護
*/
isProtected() {
return this._isProtected;
}
/**
* 取得保護選項
*/
getProtectionOptions() {
if (!this._isProtected) return null;
return { ...this._options };
}
/**
* 檢查操作是否被允許
*/
isOperationAllowed(operation) {
if (!this._isProtected) return true;
return this._options[operation] || false;
}
/**
* 驗證密碼
*/
validatePassword(password) {
if (!this._passwordHash) return true;
return verifyPassword(password, this._passwordHash);
}
};
// src/worksheet.ts
var WorksheetImpl = class {
constructor(name) {
this._cells = /* @__PURE__ */ new Map();
this._maxRow = 0;
this._maxCol = 0;
// Phase 3: 合併儲存格管理
this._mergedRanges = /* @__PURE__ */ new Set();
// Phase 6: 工作表保護
this._protection = new WorksheetProtection();
// Phase 6: 圖表支援
this._charts = /* @__PURE__ */ new Map();
this.name = name;
}
getCell(address) {
if (!this._cells.has(address)) {
const cell = new CellModel(address);
this._cells.set(address, cell);
const { row, col } = parseAddress(address);
this._maxRow = Math.max(this._maxRow, row);
this._maxCol = Math.max(this._maxCol, col);
}
return this._cells.get(address);
}
setCell(address, value, options = {}) {
if (this._protection.isProtected() && !this._protection.isOperationAllowed("formatCells")) {
throw new Error("Worksheet is protected. Cannot modify cells.");
}
const cell = this.getCell(address);
cell.value = value;
cell.type = getCellType(value);
cell.options = { ...cell.options, ...options };
const { row, col } = parseAddress(address);
this._maxRow = Math.max(this._maxRow, row);
this._maxCol = Math.max(this._maxCol, col);
return cell;
}
*rows() {
const rowMap = /* @__PURE__ */ new Map();
for (const [addr, cell] of this._cells) {
const { row, col } = parseAddress(addr);
if (!rowMap.has(row)) rowMap.set(row, /* @__PURE__ */ new Map());
rowMap.get(row).set(col, cell);
}
const sortedRows = Array.from(rowMap.keys()).sort((a, b) => a - b);
for (const row of sortedRows) {
yield [row, rowMap.get(row)];
}
}
// Phase 3: 合併儲存格實現
mergeCells(range) {
if (this._protection.isProtected() && !this._protection.isOperationAllowed("formatCells")) {
throw new Error("Worksheet is protected. Cannot merge cells.");
}
if (!/^[A-Z]+\d+:[A-Z]+\d+$/.test(range)) {
throw new Error(`Invalid range format: ${range}. Expected format: A1:B3`);
}
const [start, end] = range.split(":");
const startAddr = parseAddress(start);
const endAddr = parseAddress(end);
if (startAddr.row > endAddr.row || startAddr.col > endAddr.col) {
throw new Error(`Invalid range: start position must be before end position`);
}
for (const existingRange of this._mergedRanges) {
if (this._rangesOverlap(range, existingRange)) {
throw new Error(`Range ${range} overlaps with existing merged range ${existingRange}`);
}
}
this._mergedRanges.add(range);
const mainCell = this.getCell(start);
mainCell.options.mergeRange = range;
for (let row = startAddr.row; row <= endAddr.row; row++) {
for (let col = startAddr.col; col <= endAddr.col; col++) {
if (row === startAddr.row && col === startAddr.col) continue;
const addr = addrFromRC(row, col);
const cell = this.getCell(addr);
cell.value = null;
cell.options.mergedInto = range;
}
}
}
unmergeCells(range) {
if (this._protection.isProtected() && !this._protection.isOperationAllowed("formatCells")) {
throw new Error("Worksheet is protected. Cannot unmerge cells.");
}
if (!this._mergedRanges.has(range)) {
throw new Error(`Range ${range} is not merged`);
}
this._mergedRanges.delete(range);
const [start, end] = range.split(":");
const startAddr = parseAddress(start);
const endAddr = parseAddress(end);
for (let row = startAddr.row; row <= endAddr.row; row++) {
for (let col = startAddr.col; col <= endAddr.col; col++) {
const addr = addrFromRC(row, col);
if (this._cells.has(addr)) {
const cell = this._cells.get(addr);
delete cell.options.mergeRange;
delete cell.options.mergedInto;
}
}
}
}
getMergedRanges() {
return Array.from(this._mergedRanges).sort();
}
// Phase 3: 欄寬/列高設定
setColumnWidth(column, width) {
if (this._protection.isProtected() && !this._protection.isOperationAllowed("formatColumns")) {
throw new Error("Worksheet is protected. Cannot modify column width.");
}
const colNum = typeof column === "string" ? colToNumber(column) : column;
if (width < 0) {
throw new Error(`Column width cannot be negative: ${width}`);
}
if (!this._columnWidths) this._columnWidths = /* @__PURE__ */ new Map();
this._columnWidths.set(colNum, width);
}
getColumnWidth(column) {
const colNum = typeof column === "string" ? colToNumber(column) : column;
if (!this._columnWidths) return 8.43;
return this._columnWidths.get(colNum) || 8.43;
}
setRowHeight(row, height) {
if (this._protection.isProtected() && !this._protection.isOperationAllowed("formatRows")) {
throw new Error("Worksheet is protected. Cannot modify row height.");
}
if (height < 0) {
throw new Error(`Row height cannot be negative: ${height}`);
}
if (!this._rowHeights) this._rowHeights = /* @__PURE__ */ new Map();
this._rowHeights.set(row, height);
}
getRowHeight(row) {
if (!this._rowHeights) return 15;
return this._rowHeights.get(row) || 15;
}
// 凍結窗格
freezePanes(row, column) {
this._freezeRow = row;
this._freezeCol = column;
}
unfreezePanes() {
this._freezeRow = void 0;
this._freezeCol = void 0;
}
getFreezePanes() {
return { row: this._freezeRow, column: this._freezeCol };
}
// Phase 3: 公式支援
setFormula(address, formula, options = {}) {
if (this._protection.isProtected() && !this._protection.isOperationAllowed("formatCells")) {
throw new Error("Worksheet is protected. Cannot set formulas.");
}
const cell = this.getCell(address);
cell.options.formula = formula;
cell.options.formulaType = "shared";
cell.options.numFmt = "General";
cell.options.font = { bold: true };
cell.options.alignment = { horizontal: "center", vertical: "middle" };
cell.options.border = { style: "thin", color: "black" };
cell.options.fill = { type: "pattern", patternType: "solid", fgColor: "#FFFF00" };
return cell;
}
getFormula(address) {
const cell = this.getCell(address);
return cell.options.formula || null;
}
validateFormula(formula) {
return true;
}
getFormulaDependencies(address) {
return [];
}
// Phase 6: 工作表保護
protect(password, options) {
this._protection.protect(password, options);
}
unprotect(password) {
this._protection.unprotect(password);
}
isProtected() {
return this._protection.isProtected();
}
getProtectionOptions() {
return this._protection.getProtectionOptions();
}
// Phase 6: 圖表支援
addChart(chart) {
if (this._protection.isProtected() && !this._protection.isOperationAllowed("objects")) {
throw new Error("Worksheet is protected. Cannot add charts.");
}
this._charts.set(chart.name, chart);
}
removeChart(chartName) {
if (this._protection.isProtected() && !this._protection.isOperationAllowed("objects")) {
throw new Error("Worksheet is protected. Cannot remove charts.");
}
this._charts.delete(chartName);
}
getCharts() {
return Array.from(this._charts.values());
}
getChart(chartName) {
return this._charts.get(chartName);
}
// 內部方法
_rangesOverlap(range1, range2) {
const [start1, end1] = range1.split(":").map(parseAddress);
const [start2, end2] = range2.split(":").map(parseAddress);
return !(end1.row < start2.row || start1.row > end2.row || end1.col < start2.col || start1.col > end2.col);
}
// 取得內部屬性(用於 XML 生成)
get _maxRowValue() {
return this._maxRow;
}
get _maxColValue() {
return this._maxCol;
}
get _columnWidthsValue() {
return this._columnWidths;
}
get _rowHeightsValue() {
return this._rowHeights;
}
};
// src/charts.ts
var ChartImpl = class {
constructor(name, type, data, options = {}, position = { row: 1, col: 1 }) {
this.name = name;
this.type = type;
this.data = data;
this.options = {
title: "",
xAxisTitle: "",
yAxisTitle: "",
width: 400,
height: 300,
showLegend: true,
showDataLabels: false,
showGridlines: true,
theme: "light",
...options
};
this.position = position;
}
/**
* 添加資料系列
*/
addSeries(series) {
this.data.push(series);
}
/**
* 移除資料系列
*/
removeSeries(seriesName) {
const index = this.data.findIndex((s) => s.series === seriesName);
if (index !== -1) {
this.data.splice(index, 1);
}
}
/**
* 更新圖表選項
*/
updateOptions(options) {
this.options = { ...this.options, ...options };
}
/**
* 移動圖表位置
*/
moveTo(row, col) {
this.position = { row, col };
}
/**
* 調整圖表大小
*/
resize(width, height) {
this.options.width = width;
this.options.height = height;
}
/**
* 取得圖表 XML 表示
*/
toXml() {
const chartId = generateUniqueId();
const chartXml = this._buildChartXml(chartId);
const drawingXml = this._buildDrawingXml(chartId);
return {
chartXml,
drawingXml,
chartId
};
}
/**
* 驗證圖表資料
*/
validate() {
if (this.data.length === 0) return false;
for (const series of this.data) {
if (!series.series || !series.categories || !series.values) {
return false;
}
}
return true;
}
/**
* 建立圖表 XML
*/
_buildChartXml(chartId) {
const parts = [
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',
'<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">',
"<c:chart>",
this._buildChartTitle(),
this._buildChartType(),
this._buildChartSeries(),
this._buildChartAxes(),
this._buildChartLegend(),
"</c:chart>",
"</c:chartSpace>"
];
return parts.join("");
}
/**
* 建立圖表標題
*/
_buildChartTitle() {
if (!this.options.title) return "";
return [
"<c:title>",
"<c:tx>",
"<c:rich>",
"<a:bodyPr/>",
"<a:lstStyle/>",
"<a:p>",
"<a:r>",
"<a:t>" + this.options.title + "</a:t>",
"</a:r>",
"</a:p>",
"</c:rich>",
"</c:tx>",
"</c:title>"
].join("");
}
/**
* 建立圖表類型
*/
_buildChartType() {
const chartTypeMap = {
column: "bar",
line: "line",
pie: "pie",
bar: "bar",
area: "area",
scatter: "scatter",
doughnut: "doughnut",
radar: "radar"
};
const xmlType = chartTypeMap[this.type] || "bar";
if (xmlType === "bar") {
return [
"<c:barChart>",
'<c:barDir val="col"/>',
'<c:grouping val="clustered"/>',
this._buildChartSeries(),
"</c:barChart>"
].join("");
} else if (xmlType === "line") {
return [
"<c:lineChart>",
'<c:grouping val="standard"/>',
this._buildChartSeries(),
"</c:lineChart>"
].join("");
} else if (xmlType === "pie") {
return [
"<c:pieChart>",
this._buildChartSeries(),
"</c:pieChart>"
].join("");
}
return [
"<c:barChart>",
'<c:barDir val="col"/>',
'<c:grouping val="clustered"/>',
this._buildChartSeries(),
"</c:barChart>"
].join("");
}
/**
* 建立圖表資料系列
*/
_buildChartSeries() {
return this.data.map((series, index) => [
"<c:ser>",
'<c:idx val="' + index + '"/>',
'<c:order val="' + index + '"/>',
"<c:tx>",
"<c:strRef>",
"<c:f>" + series.series + "</c:f>",
"</c:strRef>",
"</c:tx>",
"<c:cat>",
"<c:strRef>",
"<c:f>" + series.categories + "</c:f>",
"</c:strRef>",
"</c:cat>",
"<c:val>",
"<c:numRef>",
"<c:f>" + series.values + "</c:f>",
"</c:numRef>",
"</c:val>",
"</c:ser>"
].join("")).join("");
}
/**
* 建立圖表軸線
*/
_buildChartAxes() {
if (this.type === "pie" || this.type === "doughnut") {
return "";
}
return [
"<c:catAx>",
'<c:axId val="100"/>',
"<c:scaling>",
'<c:orientation val="minMax"/>',
"</c:scaling>",
'<c:axPos val="b"/>',
'<c:crossAx val="200"/>',
'<c:tickLblPos val="nextTo"/>',
'<c:crosses val="autoZero"/>',
"</c:catAx>",
"<c:valAx>",
'<c:axId val="200"/>',
"<c:scaling>",
'<c:orientation val="minMax"/>',
"</c:scaling>",
'<c:axPos val="l"/>',
'<c:crossAx val="100"/>',
'<c:tickLblPos val="nextTo"/>',
'<c:crosses val="autoZero"/>',
"</c:valAx>"
].join("");
}
/**
* 建立圖表圖例
*/
_buildChartLegend() {
if (!this.options.showLegend) return "";
return [
"<c:legend>",
'<c:legendPos val="r"/>',
"<c:layout/>",
'<c:overlay val="0"/>',
"</c:legend>"
].join("");
}
/**
* 建立繪圖 XML
*/
_buildDrawingXml(chartId) {
const parts = [
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',
'<xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">',
"<xdr:twoCellAnchor>",
"<xdr:from>",
"<xdr:col>" + (this.position.col - 1) + "</xdr:col>",
"<xdr:colOff>0</xdr:colOff>",
"<xdr:row>" + (this.position.row - 1) + "</xdr:row>",
"<xdr:rowOff>0</xdr:rowOff>",
"</xdr:from>",
"<xdr:to>",
"<xdr:col>" + (this.position.col + Math.floor(this.options.width / 100)) + "</xdr:col>",
"<xdr:colOff>0</xdr:colOff>",
"<xdr:row>" + (this.position.row + Math.floor(this.options.height / 20)) + "</xdr:row>",
"<xdr:rowOff>0</xdr:rowOff>",
"</xdr:to>",
'<xdr:graphicFrame macro="">',
"<xdr:nvGraphicFramePr>",
'<xdr:cNvPr id="' + chartId + '" name="' + this.name + '"/>',
"<xdr:cNvGraphicFramePr/>",
"</xdr:nvGraphicFramePr>",
"<xdr:xfrm>",
'<a:off x="0" y="0"/>',
'<a:ext cx="' + this.options.width + '" cy="' + this.options.height + '"/>',
"</xdr:xfrm>",
"<a:graphic>",
'<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/chart">',
'<c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:embed="rId' + chartId + '"/>',
"</a:graphicData>",
"</a:graphic>",
"</xdr:graphicFrame>",
"</xdr:twoCellAnchor>",
"</xdr:wsDr>"
];
return parts.join("");
}
};
var ChartFactory = class {
/**
* 建立柱狀圖
*/
static createColumnChart(name, data, options, position) {
return new ChartImpl(name, "column", data, options, position);
}
/**
* 建立折線圖
*/
static createLineChart(name, data, options, position) {
return new ChartImpl(name, "line", data, options, position);
}
/**
* 建立圓餅圖
*/
static createPieChart(name, data, options, position) {
return new ChartImpl(name, "pie", data, options, position);
}
/**
* 建立長條圖
*/
static createBarChart(name, data, options, position) {
return new ChartImpl(name, "bar", data, options, position);
}
/**
* 建立面積圖
*/
static createAreaChart(name, data, options, position) {
return new ChartImpl(name, "area", data, options, position);
}
/**
* 建立散佈圖
*/
static createScatterChart(name, data, options, position) {
return new ChartImpl(name, "scatter", data, options, position);
}
};
// src/pivot-table-impl.ts
var PivotTableImpl = class {
constructor(name, config) {
this.name = name;
this.config = config;
this.data = [];
this.pivotData = [];
this.rowFields = [];
this.columnFields = [];
this.valueFields = [];
this.filters = /* @__PURE__ */ new Map();
this.initializeFields();
}
/**
* 初始化欄位分類
*/
initializeFields() {
for (const field of this.config.fields) {
switch (field.type) {
case "row":
this.rowFields.push(field);
break;
case "column":
this.columnFields.push(field);
break;
case "value":
this.valueFields.push(field);
break;
}
}
}
/**
* 設定來源資料
*/
setSourceData(data) {
this.data = data;
this.refresh();
}
/**
* 重新整理樞紐分析表
*/
refresh() {
if (this.data.length === 0) return;
const sourceData = this.parseSourceData();
this.buildPivotData(sourceData);
}
/**
* 解析來源資料
*/
parseSourceData() {
const sourceRange = this.config.sourceRange;
const { startRow, startCol, endRow, endCol } = this.parseRange(sourceRange);
const sourceData = [];
for (let row = startRow; row <= endRow; row++) {
const rowData = [];
for (let col = startCol; col <= endCol; col++) {
addrFromRC(row, col);
rowData.push(this.data[row - startRow]?.[col - startCol] || "");
}
sourceData.push(rowData);
}
return sourceData;
}
/**
* 解析範圍字串
*/
parseRange(range) {
const parts = range.split(":");
const start = parseAddress(parts[0]);
const end = parseAddress(parts[1]);
return {
startRow: start.row,
startCol: start.col,
endRow: end.row,
endCol: end.col
};
}
/**
* 建立樞紐分析表資料
*/
buildPivotData(sourceData) {
const headers = sourceData[0] || [];
const dataRows = sourceData.slice(1);
const fieldIndexMap = /* @__PURE__ */ new Map();
headers.forEach((header, index) => {
fieldIndexMap.set(header, index);
});
const uniqueValues = this.collectUniqueValues(dataRows, fieldIndexMap);
this.pivotData = this.createPivotStructure(uniqueValues, dataRows, fieldIndexMap);
}
/**
* 收集唯一值
*/
collectUniqueValues(dataRows, fieldIndexMap) {
const uniqueValues = /* @__PURE__ */ new Map();
for (const field of [...this.rowFields, ...this.columnFields]) {
const index = fieldIndexMap.get(field.sourceColumn);
if (index !== void 0) {
uniqueValues.set(field.sourceColumn, /* @__PURE__ */ new Set());
}
}
for (const row of dataRows) {
for (const field of [...this.rowFields, ...this.columnFields]) {
const index = fieldIndexMap.get(field.sourceColumn);
if (index !== void 0 && row[index] !== void 0) {
uniqueValues.get(field.sourceColumn).add(row[index]);
}
}
}
return uniqueValues;
}
/**
* 建立樞紐分析表結構
*/
createPivotStructure(uniqueValues, dataRows, fieldIndexMap) {
const pivotData = [];
const titleRow = this.createTitleRow(uniqueValues);
pivotData.push(titleRow);
const dataRowsResult = this.createDataRows(uniqueValues, dataRows, fieldIndexMap);
pivotData.push(...dataRowsResult);
return pivotData;
}
/**
* 建立標題行
*/
createTitleRow(uniqueValues) {
const titleRow = [];
for (const field of this.rowFields) {
const values = Array.from(uniqueValues.get(field.sourceColumn) || []);
titleRow.push(field.customName || field.sourceColumn);
titleRow.push(...values);
}
for (const field of this.columnFields) {
const values = Array.from(uniqueValues.get(field.sourceColumn) || []);
titleRow.push(field.customName || field.sourceColumn);
titleRow.push(...values);
}
for (const field of this.valueFields) {
titleRow.push(field.customName || field.sourceColumn);
}
return titleRow;
}
/**
* 建立資料行
*/
createDataRows(uniqueValues, dataRows, fieldIndexMap) {
const result = [];
for (const row of dataRows) {
const pivotRow = [];
for (const field of this.rowFields) {
const index = fieldIndexMap.get(field.sourceColumn);
if (index !== void 0) {
pivotRow.push(row[index]);
}
}
for (const field of this.valueFields) {
const index = fieldIndexMap.get(field.sourceColumn);
if (index !== void 0) {
pivotRow.push(row[index]);
}
}
result.push(pivotRow);
}
return result;
}
/**
* 更新來源資料
*/
updateSourceData(sourceRange) {
this.config.sourceRange = sourceRange;
this.refresh();
}
/**
* 取得欄位
*/
getField(fieldName) {
return this.config.fields.find((field) => field.name === fieldName);
}
/**
* 添加欄位
*/
addField(field) {
this.config.fields.push(field);
this.initializeFields();
this.refresh();
}
/**
* 移除欄位
*/
removeField(fieldName) {
const index = this.config.fields.findIndex((field) => field.name === fieldName);
if (index !== -1) {
this.config.fields.splice(index, 1);
this.initializeFields();
this.refresh();
}
}
/**
* 重新排序欄位
*/
reorderFields(fieldOrder) {
const newFields = [];
for (const fieldName of fieldOrder) {
const field = this.config.fields.find((f) => f.name === fieldName);
if (field) {
newFields.push(field);
}
}
this.config.fields = newFields;
this.initializeFields();
this.refresh();
}
/**
* 應用篩選
*/
applyFilter(fieldName, filterValues) {
this.filters.set(fieldName, filterValues);
this.refresh();
}
/**
* 清除篩選
*/
clearFilters() {
this.filters.clear();
this.refresh();
}
/**
* 取得資料
*/
getData() {
return this.pivotData;
}
/**
* 匯出到工作表
*/
exportToWorksheet(worksheetName) {
throw new Error("exportToWorksheet requires workbook instance");
}
};
var esc = (s) => String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
async function addPivotToWorkbookBuffer(buf, opt) {
const zip = await JSZip2__default.default.loadAsync(buf);
await PivotBuilder.attach(zip, opt);
return await zip.generateAsync({ type: "nodebuffer" });
}
var PivotBuilder = class {
static async attach(zip, opt) {
const refreshOnLoad = opt.refreshOnLoad ?? true;
const styleName = opt.styleName ?? "PivotStyleMedium9";
const map = await WorkbookMap.build(zip);
const { firstRow, lastRow } = rangeRows(opt.sourceRange);
const recordCount = lastRow - firstRow;
if (recordCount <= 0) throw new Error("sourceRange \u5FC5\u9808\u5305\u542B\u81F3\u5C11 1 \u7B46\u8CC7\u6599\uFF08\u542B\u6A19\u984C\u5217\uFF09");
const srcSheetPath = map.sheetFileByName(opt.sourceSheet);
const srcSheetXml = await mustRead(zip, srcSheetPath);
const headers = extractHeadersFromSheetXml(srcSheetXml, opt.sourceRange);
const types = inferFieldTypesFromSheetXml(srcSheetXml, opt.sourceRange, headers);
const name2idx = new Map(headers.map((h, i) => [h, i]));
const need = (n) => {
if (!name2idx.has(n)) throw new Error(`\u627E\u4E0D\u5230\u6B04\u4F4D\uFF1A${n}`);
return name2idx.get(n);
};
const rowIdxs = (opt.layout.rows ?? []).map((f) => need(f.name));
const colIdxs = (opt.layout.cols ?? []).map((f) => need(f.name));
const valIdxs = opt.layout.values.map((v) => need(v.name));
opt.layout.values.forEach((v) => {
const i = need(v.name);
if (types[i] !== "number") throw new Error(`\u503C\u6B04\u4F4D\u5FC5\u9808\u70BA\u6578\u503C\uFF1A${v.name}`);
});
const cacheId = await map.nextPivotCacheId();
const ptId = await map.nextPivotTableId();
const cacheDefPath = `xl/pivotCache/pivotCacheDefinition${cacheId}.xml`;
const cacheRecPath = `xl/pivotCache/pivotCacheRecords${cacheId}.xml`;
const ptPath = `xl/pivotTables/pivotTable${ptId}.xml`;
zip.file(cacheDefPath, genCacheDefinitionXml({
sourceSheet: opt.sourceSheet,
sourceRange: opt.sourceRange,
headers,
types,
recordCount,
refreshOnLoad
}));
zip.file(cacheRecPath, genEmptyCacheRecordsXml(recordCount));
await map.addPivotCache(cacheId, cacheDefPath);
zip.file(ptPath, genPivotTableXml({
cacheId,
headers,
rowIdxs,
colIdxs,
valIdxs,
values: opt.layout.values,
anchorCell: opt.anchorCell,
styleName
}));
const tgtSheetPath = map.sheetFileByName(opt.targetSheet);
await map.linkPivotToSheet(tgtSheetPath, ptPath);
await ensureContentTypes(zip, { cacheDefPath, cacheRecPath, ptPath });
}
};
var WorkbookMap = class _WorkbookMap {
constructor(zip, wbXml, wbRelsXml, sheetIdToTarget, nameToRid) {
this.zip = zip;
this.wbXml = wbXml;
this.wbRelsXml = wbRelsXml;
this.sheetIdToTarget = sheetIdToTarget;
this.nameToRid = nameToRid;
}
static async build(zip) {
const wbXml = await mustRead(zip, "xl/workbook.xml");
const wbRelsXml = await mustRead(zip, "xl/_rels/workbook.xml.rels");
const nameToRid = /* @__PURE__ */ new Map();
const sheetIdToTarget = /* @__PURE__ */ new Map();
for (const m of wbXml.matchAll(/<sheet[^>]*name="([^"]+)"[^>]*r:id="([^"]+)"/g)) {
nameToRid.set(m[1], m[2]);
}
for (const m of wbRelsXml.matchAll(/<Relationship[^>]*Id="([^"]+)"[^>]*Type="[^"]*worksheet"[^>]*Target="([^"]+)"/g)) {
sheetIdToTarget.set(m[1], `xl/${m[2].replace(/^\.\.\//, "")}`);
}
return new _WorkbookMap(zip, wbXml, wbRelsXml, sheetIdToTarget, nameToRid);
}
sheetFileByName(name) {
const rid = this.nameToRid.get(name);
if (!rid) throw new Error(`workbook \u627E\u4E0D\u5230\u5DE5\u4F5C\u8868\uFF1A${name}`);
const target = this.sheetIdToTarget.get(rid);
if (!target) throw new Error(`rels \u627E\u4E0D\u5230 target\uFF1A${name}(${rid})`);
return target;
}
async nextPivotCacheId() {
const ids = Array.from(this.wbXml.matchAll(/<pivotCache[^>]*cacheId="(\d+)"/g)).map((m) => +m[1]);
return (ids.length ? Math.max(...ids) : 0) + 1;
}
async nextPivotTableId() {
const files = this.zip.folder("xl/pivotTables")?.file(/.*/g) ?? [];
const ids = files.map((f) => +(f.name.match(/pivotTable(\d+)\.xml$/)?.[1] ?? 0));
return (ids.length ? Math.max(...ids) : 0) + 1;
}
async addPivotCache(cacheId, cacheDefPath) {
let rel = await mustRead(this.zip, "xl/_rels/workbook.xml.rels");
const newRelId = nextRelId(rel);
rel = rel.replace(
/<\/Relationships>\s*$/i,
` <Relationship Id="${newRelId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition" Target="pivotCache/${cacheDefPath.split("/").pop()}"/>
</Relationships>`
);
this.zip.file("xl/_rels/workbook.xml.rels", rel);
let wb = await mustRead(this.zip, "xl/workbook.xml");
if (/<pivotCaches>/.test(wb)) {
wb = wb.replace(
/<pivotCaches>([\s\S]*?)<\/pivotCaches>/,
(_m, inner) => `<pivotCaches>${inner}<pivotCache cacheId="${cacheId}" r:id="${newRelId}"/></pivotCaches>`
);
} else {
wb = wb.replace(
/<\/workbook>\s*$/i,
` <pivotCaches><pivotCache cacheId="${cacheId}" r:id="${newRelId}"/></pivotCaches>
</workbook>`
);
}
this.zip.file("xl/workbook.xml", wb);
}
async linkPivotToSheet(sheetPath, ptPath) {
const relPath = sheetPath.replace(/worksheets\/(sheet\d+\.xml)$/, "worksheets/_rels/$1.rels");
let rel = await readOrNull(this.zip, relPath) ?? `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
</Relationships>`;
const newRelId = nextRelId(rel);
rel = rel.replace(
/<\/Relationships>\s*$/i,
` <Relationship Id="${newRelId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable" Target="../pivotTables/${ptPath.split("/").pop()}"/>
</Relationships>`
);
this.zip.file(relPath, rel);
const ws = await mustRead(this.zip, sheetPath);
if (!/<sheetData/.test(ws)) {
this.zip.file(sheetPath, ws.replace(/<\/worksheet>\s*$/i, ` <sheetData/>
</worksheet>`));
}
}
};
function nextRelId(relXml) {
const ids = Array.from(relXml.matchAll(/Id="rId(\d+)"/g)).map((m) => +m[1]);
return `rId${(ids.length ? Math.max(...ids) : 0) + 1}`;
}
function genCacheDefinitionXml(p) {
const fields = p.headers.map((h, i) => {
const isNum = p.types[i] === "number";
const shared = isNum ? `<sharedItems containsNumber="1"/>` : `<sharedItems/>`;
return `<cacheField name="${esc(h)}" numFmtId="0">${shared}</cacheField>`;
}).join("");
const refresh = p.refreshOnLoad ? ` refreshOnLoad="1"` : ``;
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pivotCacheDefinition xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
invalid="1" recordCount="${p.recordCount}"${refresh}>
<cacheSource type="worksheet">
<worksheetSource sheet="${esc(p.sourceSheet)}" ref="${esc(p.sourceRange)}"/>
</cacheSource>
<cacheFields count="${p.headers.length}">
${fields}
</cacheFields>
</pivotCacheDefinition>`;
}
function genEmptyCacheRecordsXml(recordCount) {
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pivotCacheRecords xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${recordCount}">
</pivotCacheRecords>`;
}
function genPivotTableXml(p) {
const fields = p.headers.map((_, i) => {
if (p.rowIdxs.includes(i)) return `<pivotField axis="axisRow" showAll="0"/>`;
if (p.colIdxs.includes(i)) return `<pivotField axis="axisCol" showAll="0"/>`;
if (p.valIdxs.includes(i)) return `<pivotField dataField="1"/>`;
return `<pivotField/>`;
}).join("");
const rowFields = p.rowIdxs.length ? `<rowFields count="${p.rowIdxs.length}">${p.rowIdxs.map((x) => `<field x="${x}"/>`).join("")}</rowFields>` : "";
const colFields = p.colIdxs.length ? `<colFields count="${p.colIdxs.length}">${p.colIdxs.map((x) => `<field x="${x}"/>`).join("")}</colFields>` : "";
const dataFields = p.values.map((v, i) => {
const fld = p.valIdxs[i];
const subtotal = v.agg ?? "sum";
const name = esc(v.displayName ?? v.name);
const numFmtId = v.numFmtId ?? 0;
return `<dataField fld="${fld}" baseField="0" baseItem="0" subtotal="${subtotal}" name="${name}" numFmtId="${numFmtId}"/>`;
}).join("");
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pivotTableDefinition xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
name="PivotTable${p.cacheId}" cacheId="${p.cacheId}" dataCaption="\u503C" updatedVersion="3" createdVersion="3"
useAutoFormatting="1" applyNumberFormats="1" applyBorderFormats="1"
applyFontFormats="1" applyPatternFormats="1" applyAlignmentFormats="1" applyWidthHeightFormats="1">
<location ref="${esc(p.anchorCell)}" firstHeaderRow="1" firstDataRow="2" firstDataCol="1"/>
<pivotFields count="${p.headers.length}">
${fields}
</pivotFields>
${rowFields}
${colFields}
<dataFields count="${p.values.length}">
${dataFields}
</dataFields>
<pivotTableStyleInfo name="${esc(p.styleName)}" showRowHeaders="1" showColHeaders="1" showRowStripes="0" showColStripes="0" showLastColumn="0"/>
</pivotTableDefinition>`;
}
async function ensureContentTypes(zip, p) {
const path = "[Content_Types].xml";
let xml = await mustRead(zip, path);
const ensure = (part, type) => {
const tag = `<Override PartName="/${part}" ContentType="${type}"/>`;
if (!xml.includes(tag)) xml = xml.replace(/<\/Types>\s*$/i, ` ${tag}
</Types>`);
};
ensure(p.cacheDefPath, "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml");
ensure(p.cacheRecPath, "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml");
ensure(p.ptPath, "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml");
zip.file(path, xml);
}
function extractHeadersFromSheetXml(sheetXml, range) {
const { firstCol, lastCol, firstRow } = rangeColsRows(range);
const headers = [];
for (let c = firstCol; c <= lastCol; c++) {
const addr = colNumToName(c) + firstRow;
const cell = findCell(sheetXml, addr);
headers.push(readCellText(cell) || `F${c - firstCol + 1}`);
}
return headers;
}
function inferFieldTypesFromSheetXml(sheetXml, range, headers) {
const { firstCol, lastCol, firstRow, lastRow } = rangeColsRows(range);
const types = new Array(headers.length).fill("text");
for (let r = firstRow + 1; r <= Math.min(lastRow, firstRow + 20); r++) {
for (let c = firstCol; c <= lastCol; c++) {
const m = findCell(sheetXml, colNumToName(c) + r);
if (!m) continue;
if (/<v>\s*-?\d+(\.\d+)?\s*<\/v>/.test(m)) types[c - firstCol] = "number";
}
}
return types;
}
function findCell(sheetXml, addr) {
const re = new RegExp(`<c[^>]*\\br="${addr}"[^>]*>([\\s\\S]*?)</c>`, "i");
const m = sheetXml.match(re);
return m ? m[0] : null;
}
function readCellText(cellXml) {
if (!cellXml) return "";
let m = cellXml.match(/<is>\s*<t>([\s\S]*?)<\/t>\s*<\/is>/);
if (m) return decodeXml(m[1]);
m = cellXml.match(/<v>([\s\S]*?)<\/v>/);
return m ? decodeXml(m[1]) : "";
}
function decodeXml(s) {
return s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
}
function colNameToNum(name) {
let n = 0;
for (let i = 0; i < name.length; i++) n = n * 26 + (name.charCodeAt(i) - 64);
return n;
}
function colNumToName(n) {
let s = "";
while (n > 0) {
n--;
s = String.fromCharCode(65 + n % 26) + s;
n = Math.floor(n / 26);
}
return s;
}
function rangeColsRows(a1) {
const m = a1.match(/^([A-Z]+)(\d+):([A-Z]+)(\d+)$/i);
if (!m) throw new Error(`\u975E\u6CD5\u7BC4\u570D\uFF1A${a1}`);
const firstCol = colNameToNum(m[1].toUpperCase()), firstRow = +m[2], lastCol = colNameToNum(m[3].toUpperCase()), lastRow = +m[4];
return { firstCol, firstRow, lastCol, lastRow };
}
function rangeRows(a1) {
const { firstRow, lastRow } = rangeColsRows(a1);
return { firstRow, lastRow };
}
async function mustRead(zip, path) {
const f = zip.file(path);
if (!f) throw new Error(`\u627E\u4E0D\u5230\u6A94\u6848\uFF1A${path}`);
return await f.async("string");
}
async function readOrNull(zip, path) {
const f = zip.file(path);
return f ? await f.async("string") : null;
}
// src/pivot-table.ts
var PivotTableImpl2 = class {
constructor(name, config, workbook) {
this._sourceData = [];
this._processedData = [];
this._fieldValues = /* @__PURE__ */ new Map();
this._pivotCache = /* @__PURE__ */ new Map();
this.name = name;
this.config = config;
this._workbook = workbook;
this._cacheId = this._generateCacheId();
this._tableId = this._generateTableId();
this._loadSourceData();
this._processData();
}
refresh() {
this._loadSourceData();
this._processData();
}
updateSourceData(sourceRange) {
this.config.sourceRange = sourceRange;
this.refresh();
}
getField(fieldName) {
return this.config.fields.find((field) => field.name === fieldName);
}
addField(field) {
if (this.getField(field.name)) {
throw new Error(`Field "${field.name}" already exists in pivot table.`);
}
this.config.fields.push(field);
this.refresh();
}
removeField(fieldName) {
const index = this.config.fields.findIndex((field) => field.name === fieldName);
if (index === -1) {
throw new Error(`Field "${fieldName}" not found in pivot table.`);
}
this.config.fields.splice(index, 1);
this.refresh();
}
reorderFields(fieldOrder) {
const newFields = [];
for (const fieldName of fieldOrder) {
const field = this.getField(fieldName);
if (field) {
newFields.push(field);
}
}
this.config.fields = newFields;
this.refresh();
}
applyFilter(fieldName, filterValues) {
const field = this.getField(fieldName);
if (field) {
field.filterValues = filterValues;
this.refresh();
}
}
clearFilters() {
for (const field of this.config.fields) {
field.filterValues = void 0;
}
this.refresh();
}
getData() {
return this._processedData;
}
exportToWorksheet(worksheetName) {
const ws = this._workbook.getWorksheet(worksheetName);
this._clearTargetWorksheet(ws);
this._writePivotData(ws);
this._applyPivotStyles(ws);
return ws;
}
/**
* 生成 PivotCache 定義 XML(不包含記錄)
*/
generatePivotCacheXml() {
this.config.fields.filter((f) => f.type === "row");
this.config.fields.filter((f) => f.type === "column");
this.config.fields.filter((f) => f.type === "value");
this.config.fields.filter((f) => f.type === "filter");
let xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pivotCacheDefinition xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
id="${this._cacheId}"
recordCount="${this._sourceData.length - 1}"
refreshOnLoad="1">`;
xml += `
<cacheSource type="worksheet">
<worksheetSource ref="${this.config.sourceRange}" sheet="${this._getSourceSheetName()}"/>
</cacheSource>`;
xml += `
<cacheFields count="${this.config.fields.length}">`;
for (const field of this.config.fields) {
xml += this._generateCacheFieldXml(field);
}
xml += `
</cacheFields>`;
xml += `
</pivotCacheDefinition>`;
return xml;
}
/**
* 生成 PivotCache 記錄 XML(獨立檔案)
*/
generatePivotCacheRecordsXml() {
let xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pivotCacheRecords xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
count="${this._sourceData.length - 1}">`;
for (let i = 1; i < this._sourceData.length; i++) {
xml += this._generateCacheRecordXml(this._sourceData[i], i);
}
xml += `
</pivotCacheRecords>`;
return xml;
}
/**
* 生成 PivotCache 關聯 XML
*/
generatePivotCacheRelsXml() {
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheRecords" Target="pivotCacheRecords${this._cacheId}.xml"/>
</Relationships>`;
}
/**
* 生成 PivotTable XML
*/
generatePivotTableXml() {
const rowFields = this.config.fields.filter((f) => f.type === "row");
const columnFields = this.config.fields.filter((f) => f.type === "column");
const valueFields = this.config.fields.filter((f) => f.type === "value");
const filterFields = this.config.fields.filter((f) => f.type === "filter");
let xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pivotTableDefinition xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="xr"
xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision"
name="${this.name}"
cacheId="${this._cacheId}"
dataCaption="Values"
applyNumberFormatsInPivot="0"
applyBorderFormatsInPivot="0"
applyFontFormatsInPivot="0"
applyPatternFormatsInPivot="0"
applyAlignmentFormatsInPivot="0"
applyWidthHeightFormatsInPivot="0"
dataOnRows="0"
dataPosition="0"
grandTotalCaption="Grand Total"
multipleFieldFilters="0"
showDrill="1"
showMemberPropertyTips="0"
showMissing="0"
showMultipleLabel="0"
showPageMultipleLabel="0"
showPageSubtotals="0"
showRowGrandTotals="1"
showRowSubtotals="1"
showColGrandTotals="1"
showColSubtotals="0"
showItems="1"
showDataDropDown="1"
showError="0"
showExpandCollapse="1"
showOutline="1"
showEmptyRow="0"
showEmptyCol="0"
showHeaders="1"
compact="1"
outline="1"
outlineData="1"
gridDropZones="0"
indent="0"
pageWrap="0"
pageOverThenDown="0"
pageDownThenOver="0"
pageFieldsInReportFilter="0"
pageWrapCount="0"
pageBreakBetweenGroups="0"
subtotalHiddenItems="0"
printTitles="0"
fieldPrintTitles="0"
itemPrintTitles="0"
mergeTitles="0"
markAutoFormat="0"
autoFormat="0"
applyStyles="0"
baseStyles="0"
customListSort="1"
applyDataValidation="0"
enableDrill="1"
fieldListSortAscending="0"
mdxSubqueries="0"
customSubtotals="0"
visualTotals="1"
showDataAs="0"
calculatedMembers="0"
visualTotalsFilters="0"
showPageBreaks="0"
useAutoFormat="0"
pageGrandTotals="0"
subtotalPageItems="0"
rowGrandTotals="1"
colGrandTotals="1"
fieldSort="1"
compactData="1"
printDrill="0"
itemDrill="0"
drillThrough="0"
fieldList="0"
nonAutoSortDefault="0"
showNew="0"
autoShow="0"
rankBy="0"
defaultSubtotal="1"
multipleItemSelectionMode="0"
manualUpdate="0"
showCalcMbrs="0"
calculatedMembersInFilters="0"
visualTotalsForSets="0"
showASubtotalForPstTop="0"
showAllDrill="0"
showValue="1"
expandMembersInDetail="0"
dateFormatInPivot="0"
pivotShowAs="0"
enableWizard="0"
enableDrill="1"
enableFieldDialog="0"
preserveFormatting="1"
autoFormat="0"
autoRepublish="0"
showPageMultipleLabel="0"
showPageSubtotals="0"
showRowGrandTotals="1"
showRowSubtotals="1"
showColGrandTotals="1"
showColSubtotals="0"
showItems="1"
showDataDropDown="1"
showError="0"
showExpandCollapse