odf.js 1.13.2 → 2.0.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 +15 -3
- package/dist/index.cjs +8 -0
- package/dist/index.d.cts +6 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.js +6 -3
- package/dist/ns.cjs +2 -1
- package/dist/ns.d.cts +1 -0
- package/dist/ns.d.ts +1 -0
- package/dist/ns.js +2 -1
- package/dist/typed/draw/shapes.cjs +71 -27
- package/dist/typed/draw/shapes.d.cts +4 -1
- package/dist/typed/draw/shapes.d.ts +4 -1
- package/dist/typed/draw/shapes.js +71 -27
- package/dist/typed/formula/read.cjs +14 -0
- package/dist/typed/formula/read.d.cts +3 -2
- package/dist/typed/formula/read.d.ts +3 -2
- package/dist/typed/formula/read.js +14 -1
- package/dist/typed/odb/form.cjs +85 -0
- package/dist/typed/odb/form.d.cts +31 -0
- package/dist/typed/odb/form.d.ts +31 -0
- package/dist/typed/odb/form.js +84 -0
- package/dist/typed/odb/read.cjs +60 -23
- package/dist/typed/odb/read.d.cts +15 -4
- package/dist/typed/odb/read.d.ts +15 -4
- package/dist/typed/odb/read.js +60 -24
- package/dist/typed/odb/report.cjs +161 -0
- package/dist/typed/odb/report.d.cts +48 -0
- package/dist/typed/odb/report.d.ts +48 -0
- package/dist/typed/odb/report.js +160 -0
- package/dist/typed/odb/subdocument.cjs +16 -0
- package/dist/typed/odb/subdocument.d.cts +8 -0
- package/dist/typed/odb/subdocument.d.ts +8 -0
- package/dist/typed/odb/subdocument.js +15 -0
- package/dist/typed/ods/read.cjs +8 -0
- package/dist/typed/ods/read.js +9 -1
- package/dist/typed/shared/table.cjs +94 -7
- package/dist/typed/shared/table.d.cts +9 -2
- package/dist/typed/shared/table.d.ts +9 -2
- package/dist/typed/shared/table.js +94 -8
- package/package.json +1 -1
|
@@ -5,13 +5,24 @@ interface OdbConnectionInfo {
|
|
|
5
5
|
driverClass?: string;
|
|
6
6
|
url?: string;
|
|
7
7
|
}
|
|
8
|
+
interface OdbQueryInfo {
|
|
9
|
+
name: string;
|
|
10
|
+
command: string;
|
|
11
|
+
escapeProcessing?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface OdbComponentInfo {
|
|
14
|
+
name: string;
|
|
15
|
+
href: string;
|
|
16
|
+
asTemplate?: boolean;
|
|
17
|
+
}
|
|
8
18
|
interface OdbInventory {
|
|
9
19
|
connection: OdbConnectionInfo | undefined;
|
|
10
20
|
tables: string[];
|
|
11
|
-
queries:
|
|
12
|
-
forms:
|
|
13
|
-
reports:
|
|
21
|
+
queries: OdbQueryInfo[];
|
|
22
|
+
forms: OdbComponentInfo[];
|
|
23
|
+
reports: OdbComponentInfo[];
|
|
14
24
|
}
|
|
25
|
+
declare function resolveOdbComponent(pkg: Package, kind: 'form' | 'report', name: string): OdbComponentInfo;
|
|
15
26
|
declare function readOdbInventory(pkg: Package): OdbInventory;
|
|
16
27
|
//#endregion
|
|
17
|
-
export { OdbConnectionInfo, OdbInventory, readOdbInventory };
|
|
28
|
+
export { OdbComponentInfo, OdbConnectionInfo, OdbInventory, OdbQueryInfo, readOdbInventory, resolveOdbComponent };
|
package/dist/typed/odb/read.d.ts
CHANGED
|
@@ -5,13 +5,24 @@ interface OdbConnectionInfo {
|
|
|
5
5
|
driverClass?: string;
|
|
6
6
|
url?: string;
|
|
7
7
|
}
|
|
8
|
+
interface OdbQueryInfo {
|
|
9
|
+
name: string;
|
|
10
|
+
command: string;
|
|
11
|
+
escapeProcessing?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface OdbComponentInfo {
|
|
14
|
+
name: string;
|
|
15
|
+
href: string;
|
|
16
|
+
asTemplate?: boolean;
|
|
17
|
+
}
|
|
8
18
|
interface OdbInventory {
|
|
9
19
|
connection: OdbConnectionInfo | undefined;
|
|
10
20
|
tables: string[];
|
|
11
|
-
queries:
|
|
12
|
-
forms:
|
|
13
|
-
reports:
|
|
21
|
+
queries: OdbQueryInfo[];
|
|
22
|
+
forms: OdbComponentInfo[];
|
|
23
|
+
reports: OdbComponentInfo[];
|
|
14
24
|
}
|
|
25
|
+
declare function resolveOdbComponent(pkg: Package, kind: 'form' | 'report', name: string): OdbComponentInfo;
|
|
15
26
|
declare function readOdbInventory(pkg: Package): OdbInventory;
|
|
16
27
|
//#endregion
|
|
17
|
-
export { OdbConnectionInfo, OdbInventory, readOdbInventory };
|
|
28
|
+
export { OdbComponentInfo, OdbConnectionInfo, OdbInventory, OdbQueryInfo, readOdbInventory, resolveOdbComponent };
|
package/dist/typed/odb/read.js
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { decodeXmlText } from "../../xml/entities.js";
|
|
2
2
|
import { attrValue, childrenWithTag, findChildElement, rootElement } from "../../xml/query.js";
|
|
3
3
|
//#region src/typed/odb/read.ts
|
|
4
4
|
const CONTENT_PART = "content.xml";
|
|
5
5
|
const EMBEDDED_URL_PREFIX = "sdbc:embedded:";
|
|
6
|
-
const SUBDOCUMENT_CONTENT_SUFFIX = "/content.xml";
|
|
7
|
-
function isXmlMediaType(mediaType) {
|
|
8
|
-
return mediaType === "text/xml" || mediaType === "application/xml" || mediaType.endsWith("+xml");
|
|
9
|
-
}
|
|
10
6
|
function formatServerDatabaseUrl(serverDatabase) {
|
|
11
7
|
const dbType = attrValue(serverDatabase, "db:type");
|
|
12
8
|
if (dbType === void 0) return;
|
|
@@ -50,13 +46,24 @@ function readConnectionInfo(databaseElement) {
|
|
|
50
46
|
};
|
|
51
47
|
}
|
|
52
48
|
}
|
|
53
|
-
function
|
|
49
|
+
function collectQueryDefinitions(container, definitions) {
|
|
54
50
|
for (const child of container.children) {
|
|
55
51
|
if (child.type !== "element") continue;
|
|
56
52
|
if (child.tag === "db:query") {
|
|
57
53
|
const name = attrValue(child, "db:name");
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
const command = attrValue(child, "db:command");
|
|
55
|
+
if (name === void 0 || command === void 0) continue;
|
|
56
|
+
const decodedCommand = decodeXmlText(command);
|
|
57
|
+
const escapeProcessingRaw = attrValue(child, "db:escape-processing");
|
|
58
|
+
definitions.push(escapeProcessingRaw === void 0 ? {
|
|
59
|
+
name,
|
|
60
|
+
command: decodedCommand
|
|
61
|
+
} : {
|
|
62
|
+
name,
|
|
63
|
+
command: decodedCommand,
|
|
64
|
+
escapeProcessing: escapeProcessingRaw === "true"
|
|
65
|
+
});
|
|
66
|
+
} else if (child.tag === "db:query-collection") collectQueryDefinitions(child, definitions);
|
|
60
67
|
}
|
|
61
68
|
}
|
|
62
69
|
function collectTableNames(databaseElement) {
|
|
@@ -75,16 +82,47 @@ function collectTableNames(databaseElement) {
|
|
|
75
82
|
if (tableDefinitions !== void 0) for (const definition of childrenWithTag(tableDefinitions, "db:table-definition")) pushName(attrValue(definition, "db:name"));
|
|
76
83
|
return names;
|
|
77
84
|
}
|
|
78
|
-
function
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (
|
|
85
|
+
function collectComponents(container, components) {
|
|
86
|
+
for (const child of container.children) {
|
|
87
|
+
if (child.type !== "element") continue;
|
|
88
|
+
if (child.tag === "db:component-collection") {
|
|
89
|
+
collectComponents(child, components);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (child.tag !== "db:component") continue;
|
|
93
|
+
const name = attrValue(child, "db:name");
|
|
94
|
+
const rawHref = attrValue(child, "xlink:href");
|
|
95
|
+
if (name === void 0 || rawHref === void 0) continue;
|
|
96
|
+
const decodedHref = decodeXmlText(rawHref);
|
|
97
|
+
const href = decodedHref.endsWith("/") ? decodedHref.slice(0, -1) : decodedHref;
|
|
98
|
+
if (href.length === 0) continue;
|
|
99
|
+
const asTemplateRaw = attrValue(child, "db:as-template");
|
|
100
|
+
components.push(asTemplateRaw === void 0 ? {
|
|
101
|
+
name: decodeXmlText(name),
|
|
102
|
+
href
|
|
103
|
+
} : {
|
|
104
|
+
name: decodeXmlText(name),
|
|
105
|
+
href,
|
|
106
|
+
asTemplate: asTemplateRaw === "true"
|
|
107
|
+
});
|
|
86
108
|
}
|
|
87
|
-
|
|
109
|
+
}
|
|
110
|
+
function readComponents(databaseElement, tag) {
|
|
111
|
+
const container = findChildElement(databaseElement.children, tag);
|
|
112
|
+
if (container === void 0) return [];
|
|
113
|
+
const components = [];
|
|
114
|
+
collectComponents(container, components);
|
|
115
|
+
return components;
|
|
116
|
+
}
|
|
117
|
+
function resolveOdbComponent(pkg, kind, name) {
|
|
118
|
+
const inventory = readOdbInventory(pkg);
|
|
119
|
+
const components = kind === "form" ? inventory.forms : inventory.reports;
|
|
120
|
+
const match = components.find((component) => component.name === name);
|
|
121
|
+
if (match === void 0) {
|
|
122
|
+
const available = components.map((component) => component.name).join(", ");
|
|
123
|
+
throw new Error(`resolveOdbComponent: no ${kind} named "${name}" -- available: ${available === "" ? "(none)" : available}`);
|
|
124
|
+
}
|
|
125
|
+
return match;
|
|
88
126
|
}
|
|
89
127
|
function readOdbInventory(pkg) {
|
|
90
128
|
const contentPart = pkg.parts[CONTENT_PART];
|
|
@@ -96,16 +134,14 @@ function readOdbInventory(pkg) {
|
|
|
96
134
|
const connection = readConnectionInfo(databaseElement);
|
|
97
135
|
const queries = [];
|
|
98
136
|
const queriesElement = findChildElement(databaseElement.children, "db:queries");
|
|
99
|
-
if (queriesElement !== void 0)
|
|
100
|
-
const tables = collectTableNames(databaseElement);
|
|
101
|
-
const manifest = readManifest(pkg);
|
|
137
|
+
if (queriesElement !== void 0) collectQueryDefinitions(queriesElement, queries);
|
|
102
138
|
return {
|
|
103
139
|
connection,
|
|
104
|
-
tables,
|
|
140
|
+
tables: collectTableNames(databaseElement),
|
|
105
141
|
queries,
|
|
106
|
-
forms:
|
|
107
|
-
reports:
|
|
142
|
+
forms: readComponents(databaseElement, "db:forms"),
|
|
143
|
+
reports: readComponents(databaseElement, "db:reports")
|
|
108
144
|
};
|
|
109
145
|
}
|
|
110
146
|
//#endregion
|
|
111
|
-
export { readOdbInventory };
|
|
147
|
+
export { readOdbInventory, resolveOdbComponent };
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_xml_entities = require("../../xml/entities.cjs");
|
|
3
|
+
const require_xml_query = require("../../xml/query.cjs");
|
|
4
|
+
const require_typed_odb_read = require("./read.cjs");
|
|
5
|
+
const require_typed_odb_subdocument = require("./subdocument.cjs");
|
|
6
|
+
//#region src/typed/odb/report.ts
|
|
7
|
+
const CONTENT_PART = "content.xml";
|
|
8
|
+
const GROUP_TAG = "rpt:group";
|
|
9
|
+
const FUNCTION_TAG = "rpt:function";
|
|
10
|
+
const REPORT_ELEMENT_TAG = "rpt:report-element";
|
|
11
|
+
const REPORT_COMPONENT_TAG = "rpt:report-component";
|
|
12
|
+
const RPT_TAG_PREFIX = "rpt:";
|
|
13
|
+
const FIELD_FORMULA_PREFIX = "field:[";
|
|
14
|
+
const FIELD_FORMULA_SUFFIX = "]";
|
|
15
|
+
function reportAttr(element, name) {
|
|
16
|
+
const raw = require_xml_query.attrValue(element, name);
|
|
17
|
+
return raw === void 0 ? void 0 : require_xml_entities.decodeXmlText(raw);
|
|
18
|
+
}
|
|
19
|
+
function parseOptionalBoolean(raw) {
|
|
20
|
+
return raw === void 0 ? void 0 : raw === "true";
|
|
21
|
+
}
|
|
22
|
+
function boundFieldName(formula) {
|
|
23
|
+
if (formula === void 0 || !formula.startsWith(FIELD_FORMULA_PREFIX) || !formula.endsWith(FIELD_FORMULA_SUFFIX)) return;
|
|
24
|
+
const inner = formula.slice(7, formula.length - 1);
|
|
25
|
+
return inner.length === 0 ? void 0 : inner;
|
|
26
|
+
}
|
|
27
|
+
function elementText(element) {
|
|
28
|
+
let text = "";
|
|
29
|
+
for (const child of element.children) if (child.type === "text") text += require_xml_entities.decodeXmlText(child.value);
|
|
30
|
+
else if (child.type === "element") text += elementText(child);
|
|
31
|
+
return text;
|
|
32
|
+
}
|
|
33
|
+
function reportElementChild(element) {
|
|
34
|
+
return require_xml_query.findChildElement(element.children, REPORT_ELEMENT_TAG);
|
|
35
|
+
}
|
|
36
|
+
function readReportElement(element, reportElement) {
|
|
37
|
+
const control = { tag: element.tag };
|
|
38
|
+
const component = require_xml_query.findChildElement(reportElement.children, REPORT_COMPONENT_TAG);
|
|
39
|
+
const name = component === void 0 ? void 0 : reportAttr(component, "draw:name");
|
|
40
|
+
if (name !== void 0) control.name = name;
|
|
41
|
+
const formula = reportAttr(element, "rpt:formula");
|
|
42
|
+
if (formula !== void 0) control.formula = formula;
|
|
43
|
+
let text = "";
|
|
44
|
+
for (const child of element.children) if (child.type === "text") text += require_xml_entities.decodeXmlText(child.value);
|
|
45
|
+
else if (child.type === "element" && !child.tag.startsWith(RPT_TAG_PREFIX)) text += elementText(child);
|
|
46
|
+
if (text.length > 0) control.text = text;
|
|
47
|
+
const dataField = boundFieldName(formula);
|
|
48
|
+
if (dataField !== void 0) control.dataField = dataField;
|
|
49
|
+
return control;
|
|
50
|
+
}
|
|
51
|
+
function collectControls(nodes, controls) {
|
|
52
|
+
for (const node of nodes) {
|
|
53
|
+
if (node.type !== "element") continue;
|
|
54
|
+
if (node.tag.startsWith(RPT_TAG_PREFIX)) {
|
|
55
|
+
const reportElement = reportElementChild(node);
|
|
56
|
+
if (reportElement !== void 0) {
|
|
57
|
+
controls.push(readReportElement(node, reportElement));
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
collectControls(node.children, controls);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function readBand(element, kind) {
|
|
65
|
+
const elements = [];
|
|
66
|
+
collectControls(element.children, elements);
|
|
67
|
+
const band = {
|
|
68
|
+
kind,
|
|
69
|
+
elements
|
|
70
|
+
};
|
|
71
|
+
const table = require_xml_query.findChildElement(element.children, "table:table");
|
|
72
|
+
const name = table === void 0 ? void 0 : reportAttr(table, "table:name");
|
|
73
|
+
if (name !== void 0) band.name = name;
|
|
74
|
+
return band;
|
|
75
|
+
}
|
|
76
|
+
function readBandIfPresent(container, tag, kind) {
|
|
77
|
+
const element = require_xml_query.findChildElement(container.children, tag);
|
|
78
|
+
return element === void 0 ? void 0 : readBand(element, kind);
|
|
79
|
+
}
|
|
80
|
+
function readFunctions(container) {
|
|
81
|
+
const functions = [];
|
|
82
|
+
for (const child of container.children) {
|
|
83
|
+
if (child.type !== "element" || child.tag !== FUNCTION_TAG) continue;
|
|
84
|
+
const name = reportAttr(child, "rpt:name");
|
|
85
|
+
const formula = reportAttr(child, "rpt:formula");
|
|
86
|
+
if (name === void 0 || formula === void 0) continue;
|
|
87
|
+
functions.push({
|
|
88
|
+
name,
|
|
89
|
+
formula
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return functions;
|
|
93
|
+
}
|
|
94
|
+
function readGroup(element) {
|
|
95
|
+
const group = {
|
|
96
|
+
functions: readFunctions(element),
|
|
97
|
+
groups: []
|
|
98
|
+
};
|
|
99
|
+
const groupExpression = reportAttr(element, "rpt:group-expression");
|
|
100
|
+
if (groupExpression !== void 0) group.groupExpression = groupExpression;
|
|
101
|
+
const sortExpression = reportAttr(element, "rpt:sort-expression");
|
|
102
|
+
if (sortExpression !== void 0) group.sortExpression = sortExpression;
|
|
103
|
+
const sortAscending = parseOptionalBoolean(require_xml_query.attrValue(element, "rpt:sort-ascending"));
|
|
104
|
+
if (sortAscending !== void 0) group.sortAscending = sortAscending;
|
|
105
|
+
const startNewColumn = parseOptionalBoolean(require_xml_query.attrValue(element, "rpt:start-new-column"));
|
|
106
|
+
if (startNewColumn !== void 0) group.startNewColumn = startNewColumn;
|
|
107
|
+
const resetPageNumber = parseOptionalBoolean(require_xml_query.attrValue(element, "rpt:reset-page-number"));
|
|
108
|
+
if (resetPageNumber !== void 0) group.resetPageNumber = resetPageNumber;
|
|
109
|
+
const keepTogether = reportAttr(element, "rpt:keep-together");
|
|
110
|
+
if (keepTogether !== void 0) group.keepTogether = keepTogether;
|
|
111
|
+
const header = readBandIfPresent(element, "rpt:group-header", "group-header");
|
|
112
|
+
if (header !== void 0) group.header = header;
|
|
113
|
+
const footer = readBandIfPresent(element, "rpt:group-footer", "group-footer");
|
|
114
|
+
if (footer !== void 0) group.footer = footer;
|
|
115
|
+
for (const child of element.children) if (child.type === "element" && child.tag === GROUP_TAG) group.groups.push(readGroup(child));
|
|
116
|
+
return group;
|
|
117
|
+
}
|
|
118
|
+
function findDetail(container) {
|
|
119
|
+
const detail = require_xml_query.findChildElement(container.children, "rpt:detail");
|
|
120
|
+
if (detail !== void 0) return readBand(detail, "detail");
|
|
121
|
+
const group = require_xml_query.findChildElement(container.children, GROUP_TAG);
|
|
122
|
+
return group === void 0 ? void 0 : findDetail(group);
|
|
123
|
+
}
|
|
124
|
+
function readOdbReport(pkg, reportName) {
|
|
125
|
+
const component = require_typed_odb_read.resolveOdbComponent(pkg, "report", reportName);
|
|
126
|
+
const contentPart = require_typed_odb_subdocument.subDocumentPackage(pkg, component.href).parts[CONTENT_PART];
|
|
127
|
+
if (contentPart?.kind !== "xml") throw new Error(`readOdbReport: report "${reportName}" sub-document ${component.href}/${CONTENT_PART} is not an XML part`);
|
|
128
|
+
const contentRoot = require_xml_query.rootElement(contentPart.nodes);
|
|
129
|
+
const body = contentRoot === void 0 ? void 0 : require_xml_query.findChildElement(contentRoot.children, "office:body");
|
|
130
|
+
const reportElement = body === void 0 ? void 0 : require_xml_query.findChildElement(body.children, "office:report");
|
|
131
|
+
if (reportElement === void 0) throw new Error(`readOdbReport: report "${reportName}" sub-document ${component.href}/${CONTENT_PART} has no office:body/office:report element`);
|
|
132
|
+
const groups = [];
|
|
133
|
+
for (const child of reportElement.children) if (child.type === "element" && child.tag === GROUP_TAG) groups.push(readGroup(child));
|
|
134
|
+
const report = {
|
|
135
|
+
name: component.name,
|
|
136
|
+
href: component.href,
|
|
137
|
+
groups,
|
|
138
|
+
functions: readFunctions(reportElement)
|
|
139
|
+
};
|
|
140
|
+
const command = reportAttr(reportElement, "rpt:command");
|
|
141
|
+
if (command !== void 0) report.command = command;
|
|
142
|
+
const commandType = reportAttr(reportElement, "rpt:command-type");
|
|
143
|
+
if (commandType !== void 0) report.commandType = commandType;
|
|
144
|
+
const caption = reportAttr(reportElement, "office:caption");
|
|
145
|
+
if (caption !== void 0) report.caption = caption;
|
|
146
|
+
const mimeType = reportAttr(reportElement, "office:mimetype");
|
|
147
|
+
if (mimeType !== void 0) report.mimeType = mimeType;
|
|
148
|
+
const reportHeader = readBandIfPresent(reportElement, "rpt:report-header", "report-header");
|
|
149
|
+
if (reportHeader !== void 0) report.reportHeader = reportHeader;
|
|
150
|
+
const pageHeader = readBandIfPresent(reportElement, "rpt:page-header", "page-header");
|
|
151
|
+
if (pageHeader !== void 0) report.pageHeader = pageHeader;
|
|
152
|
+
const detail = findDetail(reportElement);
|
|
153
|
+
if (detail !== void 0) report.detail = detail;
|
|
154
|
+
const pageFooter = readBandIfPresent(reportElement, "rpt:page-footer", "page-footer");
|
|
155
|
+
if (pageFooter !== void 0) report.pageFooter = pageFooter;
|
|
156
|
+
const reportFooter = readBandIfPresent(reportElement, "rpt:report-footer", "report-footer");
|
|
157
|
+
if (reportFooter !== void 0) report.reportFooter = reportFooter;
|
|
158
|
+
return report;
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
exports.readOdbReport = readOdbReport;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Package } from "../../model/package.cjs";
|
|
2
|
+
//#region src/typed/odb/report.d.ts
|
|
3
|
+
interface OdbReportElement {
|
|
4
|
+
tag: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
text?: string;
|
|
7
|
+
formula?: string;
|
|
8
|
+
dataField?: string;
|
|
9
|
+
}
|
|
10
|
+
interface OdbReportBand {
|
|
11
|
+
kind: 'report-header' | 'page-header' | 'group-header' | 'detail' | 'group-footer' | 'page-footer' | 'report-footer';
|
|
12
|
+
name?: string;
|
|
13
|
+
elements: OdbReportElement[];
|
|
14
|
+
}
|
|
15
|
+
interface OdbReportFunction {
|
|
16
|
+
name: string;
|
|
17
|
+
formula: string;
|
|
18
|
+
}
|
|
19
|
+
interface OdbReportGroup {
|
|
20
|
+
groupExpression?: string;
|
|
21
|
+
sortExpression?: string;
|
|
22
|
+
sortAscending?: boolean;
|
|
23
|
+
startNewColumn?: boolean;
|
|
24
|
+
resetPageNumber?: boolean;
|
|
25
|
+
keepTogether?: string;
|
|
26
|
+
header?: OdbReportBand;
|
|
27
|
+
footer?: OdbReportBand;
|
|
28
|
+
functions: OdbReportFunction[];
|
|
29
|
+
groups: OdbReportGroup[];
|
|
30
|
+
}
|
|
31
|
+
interface OdbReport {
|
|
32
|
+
name: string;
|
|
33
|
+
href: string;
|
|
34
|
+
command?: string;
|
|
35
|
+
commandType?: string;
|
|
36
|
+
caption?: string;
|
|
37
|
+
mimeType?: string;
|
|
38
|
+
reportHeader?: OdbReportBand;
|
|
39
|
+
pageHeader?: OdbReportBand;
|
|
40
|
+
groups: OdbReportGroup[];
|
|
41
|
+
detail?: OdbReportBand;
|
|
42
|
+
pageFooter?: OdbReportBand;
|
|
43
|
+
reportFooter?: OdbReportBand;
|
|
44
|
+
functions: OdbReportFunction[];
|
|
45
|
+
}
|
|
46
|
+
declare function readOdbReport(pkg: Package, reportName: string): OdbReport;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { OdbReport, OdbReportBand, OdbReportElement, OdbReportFunction, OdbReportGroup, readOdbReport };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Package } from "../../model/package.js";
|
|
2
|
+
//#region src/typed/odb/report.d.ts
|
|
3
|
+
interface OdbReportElement {
|
|
4
|
+
tag: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
text?: string;
|
|
7
|
+
formula?: string;
|
|
8
|
+
dataField?: string;
|
|
9
|
+
}
|
|
10
|
+
interface OdbReportBand {
|
|
11
|
+
kind: 'report-header' | 'page-header' | 'group-header' | 'detail' | 'group-footer' | 'page-footer' | 'report-footer';
|
|
12
|
+
name?: string;
|
|
13
|
+
elements: OdbReportElement[];
|
|
14
|
+
}
|
|
15
|
+
interface OdbReportFunction {
|
|
16
|
+
name: string;
|
|
17
|
+
formula: string;
|
|
18
|
+
}
|
|
19
|
+
interface OdbReportGroup {
|
|
20
|
+
groupExpression?: string;
|
|
21
|
+
sortExpression?: string;
|
|
22
|
+
sortAscending?: boolean;
|
|
23
|
+
startNewColumn?: boolean;
|
|
24
|
+
resetPageNumber?: boolean;
|
|
25
|
+
keepTogether?: string;
|
|
26
|
+
header?: OdbReportBand;
|
|
27
|
+
footer?: OdbReportBand;
|
|
28
|
+
functions: OdbReportFunction[];
|
|
29
|
+
groups: OdbReportGroup[];
|
|
30
|
+
}
|
|
31
|
+
interface OdbReport {
|
|
32
|
+
name: string;
|
|
33
|
+
href: string;
|
|
34
|
+
command?: string;
|
|
35
|
+
commandType?: string;
|
|
36
|
+
caption?: string;
|
|
37
|
+
mimeType?: string;
|
|
38
|
+
reportHeader?: OdbReportBand;
|
|
39
|
+
pageHeader?: OdbReportBand;
|
|
40
|
+
groups: OdbReportGroup[];
|
|
41
|
+
detail?: OdbReportBand;
|
|
42
|
+
pageFooter?: OdbReportBand;
|
|
43
|
+
reportFooter?: OdbReportBand;
|
|
44
|
+
functions: OdbReportFunction[];
|
|
45
|
+
}
|
|
46
|
+
declare function readOdbReport(pkg: Package, reportName: string): OdbReport;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { OdbReport, OdbReportBand, OdbReportElement, OdbReportFunction, OdbReportGroup, readOdbReport };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { decodeXmlText } from "../../xml/entities.js";
|
|
2
|
+
import { attrValue, findChildElement, rootElement } from "../../xml/query.js";
|
|
3
|
+
import { resolveOdbComponent } from "./read.js";
|
|
4
|
+
import { subDocumentPackage } from "./subdocument.js";
|
|
5
|
+
//#region src/typed/odb/report.ts
|
|
6
|
+
const CONTENT_PART = "content.xml";
|
|
7
|
+
const GROUP_TAG = "rpt:group";
|
|
8
|
+
const FUNCTION_TAG = "rpt:function";
|
|
9
|
+
const REPORT_ELEMENT_TAG = "rpt:report-element";
|
|
10
|
+
const REPORT_COMPONENT_TAG = "rpt:report-component";
|
|
11
|
+
const RPT_TAG_PREFIX = "rpt:";
|
|
12
|
+
const FIELD_FORMULA_PREFIX = "field:[";
|
|
13
|
+
const FIELD_FORMULA_SUFFIX = "]";
|
|
14
|
+
function reportAttr(element, name) {
|
|
15
|
+
const raw = attrValue(element, name);
|
|
16
|
+
return raw === void 0 ? void 0 : decodeXmlText(raw);
|
|
17
|
+
}
|
|
18
|
+
function parseOptionalBoolean(raw) {
|
|
19
|
+
return raw === void 0 ? void 0 : raw === "true";
|
|
20
|
+
}
|
|
21
|
+
function boundFieldName(formula) {
|
|
22
|
+
if (formula === void 0 || !formula.startsWith(FIELD_FORMULA_PREFIX) || !formula.endsWith(FIELD_FORMULA_SUFFIX)) return;
|
|
23
|
+
const inner = formula.slice(7, formula.length - 1);
|
|
24
|
+
return inner.length === 0 ? void 0 : inner;
|
|
25
|
+
}
|
|
26
|
+
function elementText(element) {
|
|
27
|
+
let text = "";
|
|
28
|
+
for (const child of element.children) if (child.type === "text") text += decodeXmlText(child.value);
|
|
29
|
+
else if (child.type === "element") text += elementText(child);
|
|
30
|
+
return text;
|
|
31
|
+
}
|
|
32
|
+
function reportElementChild(element) {
|
|
33
|
+
return findChildElement(element.children, REPORT_ELEMENT_TAG);
|
|
34
|
+
}
|
|
35
|
+
function readReportElement(element, reportElement) {
|
|
36
|
+
const control = { tag: element.tag };
|
|
37
|
+
const component = findChildElement(reportElement.children, REPORT_COMPONENT_TAG);
|
|
38
|
+
const name = component === void 0 ? void 0 : reportAttr(component, "draw:name");
|
|
39
|
+
if (name !== void 0) control.name = name;
|
|
40
|
+
const formula = reportAttr(element, "rpt:formula");
|
|
41
|
+
if (formula !== void 0) control.formula = formula;
|
|
42
|
+
let text = "";
|
|
43
|
+
for (const child of element.children) if (child.type === "text") text += decodeXmlText(child.value);
|
|
44
|
+
else if (child.type === "element" && !child.tag.startsWith(RPT_TAG_PREFIX)) text += elementText(child);
|
|
45
|
+
if (text.length > 0) control.text = text;
|
|
46
|
+
const dataField = boundFieldName(formula);
|
|
47
|
+
if (dataField !== void 0) control.dataField = dataField;
|
|
48
|
+
return control;
|
|
49
|
+
}
|
|
50
|
+
function collectControls(nodes, controls) {
|
|
51
|
+
for (const node of nodes) {
|
|
52
|
+
if (node.type !== "element") continue;
|
|
53
|
+
if (node.tag.startsWith(RPT_TAG_PREFIX)) {
|
|
54
|
+
const reportElement = reportElementChild(node);
|
|
55
|
+
if (reportElement !== void 0) {
|
|
56
|
+
controls.push(readReportElement(node, reportElement));
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
collectControls(node.children, controls);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function readBand(element, kind) {
|
|
64
|
+
const elements = [];
|
|
65
|
+
collectControls(element.children, elements);
|
|
66
|
+
const band = {
|
|
67
|
+
kind,
|
|
68
|
+
elements
|
|
69
|
+
};
|
|
70
|
+
const table = findChildElement(element.children, "table:table");
|
|
71
|
+
const name = table === void 0 ? void 0 : reportAttr(table, "table:name");
|
|
72
|
+
if (name !== void 0) band.name = name;
|
|
73
|
+
return band;
|
|
74
|
+
}
|
|
75
|
+
function readBandIfPresent(container, tag, kind) {
|
|
76
|
+
const element = findChildElement(container.children, tag);
|
|
77
|
+
return element === void 0 ? void 0 : readBand(element, kind);
|
|
78
|
+
}
|
|
79
|
+
function readFunctions(container) {
|
|
80
|
+
const functions = [];
|
|
81
|
+
for (const child of container.children) {
|
|
82
|
+
if (child.type !== "element" || child.tag !== FUNCTION_TAG) continue;
|
|
83
|
+
const name = reportAttr(child, "rpt:name");
|
|
84
|
+
const formula = reportAttr(child, "rpt:formula");
|
|
85
|
+
if (name === void 0 || formula === void 0) continue;
|
|
86
|
+
functions.push({
|
|
87
|
+
name,
|
|
88
|
+
formula
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return functions;
|
|
92
|
+
}
|
|
93
|
+
function readGroup(element) {
|
|
94
|
+
const group = {
|
|
95
|
+
functions: readFunctions(element),
|
|
96
|
+
groups: []
|
|
97
|
+
};
|
|
98
|
+
const groupExpression = reportAttr(element, "rpt:group-expression");
|
|
99
|
+
if (groupExpression !== void 0) group.groupExpression = groupExpression;
|
|
100
|
+
const sortExpression = reportAttr(element, "rpt:sort-expression");
|
|
101
|
+
if (sortExpression !== void 0) group.sortExpression = sortExpression;
|
|
102
|
+
const sortAscending = parseOptionalBoolean(attrValue(element, "rpt:sort-ascending"));
|
|
103
|
+
if (sortAscending !== void 0) group.sortAscending = sortAscending;
|
|
104
|
+
const startNewColumn = parseOptionalBoolean(attrValue(element, "rpt:start-new-column"));
|
|
105
|
+
if (startNewColumn !== void 0) group.startNewColumn = startNewColumn;
|
|
106
|
+
const resetPageNumber = parseOptionalBoolean(attrValue(element, "rpt:reset-page-number"));
|
|
107
|
+
if (resetPageNumber !== void 0) group.resetPageNumber = resetPageNumber;
|
|
108
|
+
const keepTogether = reportAttr(element, "rpt:keep-together");
|
|
109
|
+
if (keepTogether !== void 0) group.keepTogether = keepTogether;
|
|
110
|
+
const header = readBandIfPresent(element, "rpt:group-header", "group-header");
|
|
111
|
+
if (header !== void 0) group.header = header;
|
|
112
|
+
const footer = readBandIfPresent(element, "rpt:group-footer", "group-footer");
|
|
113
|
+
if (footer !== void 0) group.footer = footer;
|
|
114
|
+
for (const child of element.children) if (child.type === "element" && child.tag === GROUP_TAG) group.groups.push(readGroup(child));
|
|
115
|
+
return group;
|
|
116
|
+
}
|
|
117
|
+
function findDetail(container) {
|
|
118
|
+
const detail = findChildElement(container.children, "rpt:detail");
|
|
119
|
+
if (detail !== void 0) return readBand(detail, "detail");
|
|
120
|
+
const group = findChildElement(container.children, GROUP_TAG);
|
|
121
|
+
return group === void 0 ? void 0 : findDetail(group);
|
|
122
|
+
}
|
|
123
|
+
function readOdbReport(pkg, reportName) {
|
|
124
|
+
const component = resolveOdbComponent(pkg, "report", reportName);
|
|
125
|
+
const contentPart = subDocumentPackage(pkg, component.href).parts[CONTENT_PART];
|
|
126
|
+
if (contentPart?.kind !== "xml") throw new Error(`readOdbReport: report "${reportName}" sub-document ${component.href}/${CONTENT_PART} is not an XML part`);
|
|
127
|
+
const contentRoot = rootElement(contentPart.nodes);
|
|
128
|
+
const body = contentRoot === void 0 ? void 0 : findChildElement(contentRoot.children, "office:body");
|
|
129
|
+
const reportElement = body === void 0 ? void 0 : findChildElement(body.children, "office:report");
|
|
130
|
+
if (reportElement === void 0) throw new Error(`readOdbReport: report "${reportName}" sub-document ${component.href}/${CONTENT_PART} has no office:body/office:report element`);
|
|
131
|
+
const groups = [];
|
|
132
|
+
for (const child of reportElement.children) if (child.type === "element" && child.tag === GROUP_TAG) groups.push(readGroup(child));
|
|
133
|
+
const report = {
|
|
134
|
+
name: component.name,
|
|
135
|
+
href: component.href,
|
|
136
|
+
groups,
|
|
137
|
+
functions: readFunctions(reportElement)
|
|
138
|
+
};
|
|
139
|
+
const command = reportAttr(reportElement, "rpt:command");
|
|
140
|
+
if (command !== void 0) report.command = command;
|
|
141
|
+
const commandType = reportAttr(reportElement, "rpt:command-type");
|
|
142
|
+
if (commandType !== void 0) report.commandType = commandType;
|
|
143
|
+
const caption = reportAttr(reportElement, "office:caption");
|
|
144
|
+
if (caption !== void 0) report.caption = caption;
|
|
145
|
+
const mimeType = reportAttr(reportElement, "office:mimetype");
|
|
146
|
+
if (mimeType !== void 0) report.mimeType = mimeType;
|
|
147
|
+
const reportHeader = readBandIfPresent(reportElement, "rpt:report-header", "report-header");
|
|
148
|
+
if (reportHeader !== void 0) report.reportHeader = reportHeader;
|
|
149
|
+
const pageHeader = readBandIfPresent(reportElement, "rpt:page-header", "page-header");
|
|
150
|
+
if (pageHeader !== void 0) report.pageHeader = pageHeader;
|
|
151
|
+
const detail = findDetail(reportElement);
|
|
152
|
+
if (detail !== void 0) report.detail = detail;
|
|
153
|
+
const pageFooter = readBandIfPresent(reportElement, "rpt:page-footer", "page-footer");
|
|
154
|
+
if (pageFooter !== void 0) report.pageFooter = pageFooter;
|
|
155
|
+
const reportFooter = readBandIfPresent(reportElement, "rpt:report-footer", "report-footer");
|
|
156
|
+
if (reportFooter !== void 0) report.reportFooter = reportFooter;
|
|
157
|
+
return report;
|
|
158
|
+
}
|
|
159
|
+
//#endregion
|
|
160
|
+
export { readOdbReport };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/typed/odb/subdocument.ts
|
|
3
|
+
const CONTENT_PART = "content.xml";
|
|
4
|
+
function subDocumentPackage(pkg, prefix, options = {}) {
|
|
5
|
+
const normalised = prefix.endsWith("/") ? prefix : `${prefix}/`;
|
|
6
|
+
const parts = {};
|
|
7
|
+
for (const [path, part] of Object.entries(pkg.parts)) {
|
|
8
|
+
if (!path.startsWith(normalised)) continue;
|
|
9
|
+
const relative = path.slice(normalised.length);
|
|
10
|
+
if (relative.length > 0) parts[relative] = part;
|
|
11
|
+
}
|
|
12
|
+
if (options.allowMissingContent !== true && parts[CONTENT_PART] === void 0) throw new Error(`subDocumentPackage: package has no ${normalised}${CONTENT_PART} part`);
|
|
13
|
+
return { parts };
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
exports.subDocumentPackage = subDocumentPackage;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Package } from "../../model/package.cjs";
|
|
2
|
+
//#region src/typed/odb/subdocument.d.ts
|
|
3
|
+
interface SubDocumentPackageOptions {
|
|
4
|
+
allowMissingContent?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare function subDocumentPackage(pkg: Package, prefix: string, options?: SubDocumentPackageOptions): Package;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { SubDocumentPackageOptions, subDocumentPackage };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Package } from "../../model/package.js";
|
|
2
|
+
//#region src/typed/odb/subdocument.d.ts
|
|
3
|
+
interface SubDocumentPackageOptions {
|
|
4
|
+
allowMissingContent?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare function subDocumentPackage(pkg: Package, prefix: string, options?: SubDocumentPackageOptions): Package;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { SubDocumentPackageOptions, subDocumentPackage };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/typed/odb/subdocument.ts
|
|
2
|
+
const CONTENT_PART = "content.xml";
|
|
3
|
+
function subDocumentPackage(pkg, prefix, options = {}) {
|
|
4
|
+
const normalised = prefix.endsWith("/") ? prefix : `${prefix}/`;
|
|
5
|
+
const parts = {};
|
|
6
|
+
for (const [path, part] of Object.entries(pkg.parts)) {
|
|
7
|
+
if (!path.startsWith(normalised)) continue;
|
|
8
|
+
const relative = path.slice(normalised.length);
|
|
9
|
+
if (relative.length > 0) parts[relative] = part;
|
|
10
|
+
}
|
|
11
|
+
if (options.allowMissingContent !== true && parts[CONTENT_PART] === void 0) throw new Error(`subDocumentPackage: package has no ${normalised}${CONTENT_PART} part`);
|
|
12
|
+
return { parts };
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { subDocumentPackage };
|