@report-designer/core 0.1.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/dist/aggregate-engine/aggregate-runtime.d.ts +14 -0
- package/dist/aggregate-engine/aggregate-runtime.d.ts.map +1 -0
- package/dist/aggregate-engine/aggregate-runtime.js +98 -0
- package/dist/aggregate-engine/aggregate-runtime.js.map +1 -0
- package/dist/aggregate-engine/aggregate-types.d.ts +14 -0
- package/dist/aggregate-engine/aggregate-types.d.ts.map +1 -0
- package/dist/aggregate-engine/aggregate-types.js +2 -0
- package/dist/aggregate-engine/aggregate-types.js.map +1 -0
- package/dist/aggregate-engine/index.d.ts +3 -0
- package/dist/aggregate-engine/index.d.ts.map +1 -0
- package/dist/aggregate-engine/index.js +3 -0
- package/dist/aggregate-engine/index.js.map +1 -0
- package/dist/band-planner/band-plan.d.ts +46 -0
- package/dist/band-planner/band-plan.d.ts.map +1 -0
- package/dist/band-planner/band-plan.js +2 -0
- package/dist/band-planner/band-plan.js.map +1 -0
- package/dist/band-planner/build-band-plan.d.ts +4 -0
- package/dist/band-planner/build-band-plan.d.ts.map +1 -0
- package/dist/band-planner/build-band-plan.js +84 -0
- package/dist/band-planner/build-band-plan.js.map +1 -0
- package/dist/band-planner/execute-band-plan.d.ts +8 -0
- package/dist/band-planner/execute-band-plan.d.ts.map +1 -0
- package/dist/band-planner/execute-band-plan.js +280 -0
- package/dist/band-planner/execute-band-plan.js.map +1 -0
- package/dist/band-planner/index.d.ts +4 -0
- package/dist/band-planner/index.d.ts.map +1 -0
- package/dist/band-planner/index.js +4 -0
- package/dist/band-planner/index.js.map +1 -0
- package/dist/chart/chart-capabilities.d.ts +40 -0
- package/dist/chart/chart-capabilities.d.ts.map +1 -0
- package/dist/chart/chart-capabilities.js +62 -0
- package/dist/chart/chart-capabilities.js.map +1 -0
- package/dist/chart/index.d.ts +2 -0
- package/dist/chart/index.d.ts.map +1 -0
- package/dist/chart/index.js +2 -0
- package/dist/chart/index.js.map +1 -0
- package/dist/command-engine/index.d.ts +49 -0
- package/dist/command-engine/index.d.ts.map +1 -0
- package/dist/command-engine/index.js +537 -0
- package/dist/command-engine/index.js.map +1 -0
- package/dist/conditional-format/index.d.ts +17 -0
- package/dist/conditional-format/index.d.ts.map +1 -0
- package/dist/conditional-format/index.js +171 -0
- package/dist/conditional-format/index.js.map +1 -0
- package/dist/data-dictionary/index.d.ts +3 -0
- package/dist/data-dictionary/index.d.ts.map +1 -0
- package/dist/data-dictionary/index.js +3 -0
- package/dist/data-dictionary/index.js.map +1 -0
- package/dist/data-dictionary/json-dictionary.d.ts +6 -0
- package/dist/data-dictionary/json-dictionary.d.ts.map +1 -0
- package/dist/data-dictionary/json-dictionary.js +195 -0
- package/dist/data-dictionary/json-dictionary.js.map +1 -0
- package/dist/data-dictionary/json-path.d.ts +9 -0
- package/dist/data-dictionary/json-path.d.ts.map +1 -0
- package/dist/data-dictionary/json-path.js +34 -0
- package/dist/data-dictionary/json-path.js.map +1 -0
- package/dist/event-engine/event-component-handles.d.ts +84 -0
- package/dist/event-engine/event-component-handles.d.ts.map +1 -0
- package/dist/event-engine/event-component-handles.js +276 -0
- package/dist/event-engine/event-component-handles.js.map +1 -0
- package/dist/event-engine/event-context.d.ts +23 -0
- package/dist/event-engine/event-context.d.ts.map +1 -0
- package/dist/event-engine/event-context.js +184 -0
- package/dist/event-engine/event-context.js.map +1 -0
- package/dist/event-engine/event-editor-contract.d.ts +39 -0
- package/dist/event-engine/event-editor-contract.d.ts.map +1 -0
- package/dist/event-engine/event-editor-contract.js +442 -0
- package/dist/event-engine/event-editor-contract.js.map +1 -0
- package/dist/event-engine/event-editor-data-contract.d.ts +10 -0
- package/dist/event-engine/event-editor-data-contract.d.ts.map +1 -0
- package/dist/event-engine/event-editor-data-contract.js +85 -0
- package/dist/event-engine/event-editor-data-contract.js.map +1 -0
- package/dist/event-engine/event-log.d.ts +3 -0
- package/dist/event-engine/event-log.d.ts.map +1 -0
- package/dist/event-engine/event-log.js +37 -0
- package/dist/event-engine/event-log.js.map +1 -0
- package/dist/event-engine/event-runner.d.ts +7 -0
- package/dist/event-engine/event-runner.d.ts.map +1 -0
- package/dist/event-engine/event-runner.js +265 -0
- package/dist/event-engine/event-runner.js.map +1 -0
- package/dist/event-engine/event-template.d.ts +14 -0
- package/dist/event-engine/event-template.d.ts.map +1 -0
- package/dist/event-engine/event-template.js +183 -0
- package/dist/event-engine/event-template.js.map +1 -0
- package/dist/event-engine/index.d.ts +9 -0
- package/dist/event-engine/index.d.ts.map +1 -0
- package/dist/event-engine/index.js +9 -0
- package/dist/event-engine/index.js.map +1 -0
- package/dist/event-engine/types.d.ts +129 -0
- package/dist/event-engine/types.d.ts.map +1 -0
- package/dist/event-engine/types.js +2 -0
- package/dist/event-engine/types.js.map +1 -0
- package/dist/expression-engine/ast.d.ts +35 -0
- package/dist/expression-engine/ast.d.ts.map +1 -0
- package/dist/expression-engine/ast.js +9 -0
- package/dist/expression-engine/ast.js.map +1 -0
- package/dist/expression-engine/chinese-money.d.ts +2 -0
- package/dist/expression-engine/chinese-money.d.ts.map +1 -0
- package/dist/expression-engine/chinese-money.js +90 -0
- package/dist/expression-engine/chinese-money.js.map +1 -0
- package/dist/expression-engine/evaluator.d.ts +26 -0
- package/dist/expression-engine/evaluator.d.ts.map +1 -0
- package/dist/expression-engine/evaluator.js +406 -0
- package/dist/expression-engine/evaluator.js.map +1 -0
- package/dist/expression-engine/index.d.ts +7 -0
- package/dist/expression-engine/index.d.ts.map +1 -0
- package/dist/expression-engine/index.js +5 -0
- package/dist/expression-engine/index.js.map +1 -0
- package/dist/expression-engine/lexer.d.ts +33 -0
- package/dist/expression-engine/lexer.d.ts.map +1 -0
- package/dist/expression-engine/lexer.js +169 -0
- package/dist/expression-engine/lexer.js.map +1 -0
- package/dist/expression-engine/parser.d.ts +8 -0
- package/dist/expression-engine/parser.d.ts.map +1 -0
- package/dist/expression-engine/parser.js +223 -0
- package/dist/expression-engine/parser.js.map +1 -0
- package/dist/expression-engine/report-functions.d.ts +3 -0
- package/dist/expression-engine/report-functions.d.ts.map +1 -0
- package/dist/expression-engine/report-functions.js +41 -0
- package/dist/expression-engine/report-functions.js.map +1 -0
- package/dist/fonts/index.d.ts +14 -0
- package/dist/fonts/index.d.ts.map +1 -0
- package/dist/fonts/index.js +73 -0
- package/dist/fonts/index.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/layout-engine/index.d.ts +3 -0
- package/dist/layout-engine/index.d.ts.map +1 -0
- package/dist/layout-engine/index.js +3 -0
- package/dist/layout-engine/index.js.map +1 -0
- package/dist/layout-engine/layout-band.d.ts +40 -0
- package/dist/layout-engine/layout-band.d.ts.map +1 -0
- package/dist/layout-engine/layout-band.js +1243 -0
- package/dist/layout-engine/layout-band.js.map +1 -0
- package/dist/layout-engine/measure.d.ts +10 -0
- package/dist/layout-engine/measure.d.ts.map +1 -0
- package/dist/layout-engine/measure.js +37 -0
- package/dist/layout-engine/measure.js.map +1 -0
- package/dist/pagination/index.d.ts +38 -0
- package/dist/pagination/index.d.ts.map +1 -0
- package/dist/pagination/index.js +114 -0
- package/dist/pagination/index.js.map +1 -0
- package/dist/pagination/page-number-pass.d.ts +3 -0
- package/dist/pagination/page-number-pass.d.ts.map +1 -0
- package/dist/pagination/page-number-pass.js +18 -0
- package/dist/pagination/page-number-pass.js.map +1 -0
- package/dist/pagination/paginate.d.ts +24 -0
- package/dist/pagination/paginate.d.ts.map +1 -0
- package/dist/pagination/paginate.js +904 -0
- package/dist/pagination/paginate.js.map +1 -0
- package/dist/render-document/index.d.ts +2 -0
- package/dist/render-document/index.d.ts.map +1 -0
- package/dist/render-document/index.js +2 -0
- package/dist/render-document/index.js.map +1 -0
- package/dist/render-document/types.d.ts +159 -0
- package/dist/render-document/types.d.ts.map +1 -0
- package/dist/render-document/types.js +2 -0
- package/dist/render-document/types.js.map +1 -0
- package/dist/render-engine/index.d.ts +65 -0
- package/dist/render-engine/index.d.ts.map +1 -0
- package/dist/render-engine/index.js +188 -0
- package/dist/render-engine/index.js.map +1 -0
- package/dist/rich-text/index.d.ts +2 -0
- package/dist/rich-text/index.d.ts.map +1 -0
- package/dist/rich-text/index.js +84 -0
- package/dist/rich-text/index.js.map +1 -0
- package/dist/table/table-structure.d.ts +10 -0
- package/dist/table/table-structure.d.ts.map +1 -0
- package/dist/table/table-structure.js +196 -0
- package/dist/table/table-structure.js.map +1 -0
- package/dist/template-model/index.d.ts +5 -0
- package/dist/template-model/index.d.ts.map +1 -0
- package/dist/template-model/index.js +5 -0
- package/dist/template-model/index.js.map +1 -0
- package/dist/template-model/normalize-template.d.ts +3 -0
- package/dist/template-model/normalize-template.d.ts.map +1 -0
- package/dist/template-model/normalize-template.js +167 -0
- package/dist/template-model/normalize-template.js.map +1 -0
- package/dist/template-model/schema.d.ts +10 -0
- package/dist/template-model/schema.d.ts.map +1 -0
- package/dist/template-model/schema.js +134 -0
- package/dist/template-model/schema.js.map +1 -0
- package/dist/template-model/template.d.ts +5 -0
- package/dist/template-model/template.d.ts.map +1 -0
- package/dist/template-model/template.js +194 -0
- package/dist/template-model/template.js.map +1 -0
- package/dist/template-model/types.d.ts +641 -0
- package/dist/template-model/types.d.ts.map +1 -0
- package/dist/template-model/types.js +36 -0
- package/dist/template-model/types.js.map +1 -0
- package/dist/text-format/index.d.ts +5 -0
- package/dist/text-format/index.d.ts.map +1 -0
- package/dist/text-format/index.js +311 -0
- package/dist/text-format/index.js.map +1 -0
- package/dist/text-style/index.d.ts +3 -0
- package/dist/text-style/index.d.ts.map +1 -0
- package/dist/text-style/index.js +2 -0
- package/dist/text-style/index.js.map +1 -0
- package/dist/text-style/resolve-text-style.d.ts +58 -0
- package/dist/text-style/resolve-text-style.d.ts.map +1 -0
- package/dist/text-style/resolve-text-style.js +155 -0
- package/dist/text-style/resolve-text-style.js.map +1 -0
- package/package.json +38 -0
|
@@ -0,0 +1,904 @@
|
|
|
1
|
+
import { buildBandPlan, executeBandPlan } from '../band-planner';
|
|
2
|
+
import { expandJsonDataBySources, mergeInferredDataSources } from '../data-dictionary';
|
|
3
|
+
import { createLayoutEventState, layoutBand } from '../layout-engine/layout-band';
|
|
4
|
+
import { isRepeatOnEveryPageBandType } from '../template-model/types';
|
|
5
|
+
import { normalizeTemplate } from '../template-model';
|
|
6
|
+
import { evalExpression } from '../expression-engine/evaluator';
|
|
7
|
+
import { applyPageNumberPass } from './page-number-pass';
|
|
8
|
+
import { cloneReportTemplate, createEventContext, createEventLogCollector, createEventRuntimeState, runEventScript, } from '../event-engine';
|
|
9
|
+
const DEFAULT_MAX_SUBREPORT_DEPTH = 3;
|
|
10
|
+
function createRenderEventRuntime(template, data, options) {
|
|
11
|
+
const mode = options.mode ?? options.eventRuntime?.mode ?? 'preview';
|
|
12
|
+
if (options.eventRuntime) {
|
|
13
|
+
return {
|
|
14
|
+
...options.eventRuntime,
|
|
15
|
+
mode,
|
|
16
|
+
report: template,
|
|
17
|
+
data,
|
|
18
|
+
parameters: options.parameters ?? options.eventRuntime.parameters,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
mode,
|
|
23
|
+
report: template,
|
|
24
|
+
data,
|
|
25
|
+
parameters: options.parameters,
|
|
26
|
+
variables: {},
|
|
27
|
+
state: {},
|
|
28
|
+
log: createEventLogCollector({ ownerType: 'report', ownerId: template.id, eventName: '' }),
|
|
29
|
+
runtime: createEventRuntimeState(),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function runReportEvent(template, eventName, eventRuntime) {
|
|
33
|
+
const execution = { canceled: false, hidden: false, hasValue: false };
|
|
34
|
+
const target = { ownerType: 'report', ownerId: template.id, eventName };
|
|
35
|
+
const ctx = createEventContext({
|
|
36
|
+
mode: eventRuntime.mode,
|
|
37
|
+
report: template,
|
|
38
|
+
data: eventRuntime.data,
|
|
39
|
+
parameters: eventRuntime.parameters,
|
|
40
|
+
variables: eventRuntime.variables,
|
|
41
|
+
state: eventRuntime.state,
|
|
42
|
+
log: eventRuntime.log,
|
|
43
|
+
target,
|
|
44
|
+
runtime: eventRuntime.runtime,
|
|
45
|
+
execution,
|
|
46
|
+
});
|
|
47
|
+
runEventScript({
|
|
48
|
+
event: template.events?.[eventName],
|
|
49
|
+
ctx,
|
|
50
|
+
target,
|
|
51
|
+
eventLogs: eventRuntime.log,
|
|
52
|
+
runtimeState: eventRuntime.runtime,
|
|
53
|
+
});
|
|
54
|
+
return execution;
|
|
55
|
+
}
|
|
56
|
+
function runPageEvent(page, eventName, eventRuntime) {
|
|
57
|
+
const execution = { canceled: false, hidden: false, hasValue: false };
|
|
58
|
+
if (!eventRuntime) {
|
|
59
|
+
return execution;
|
|
60
|
+
}
|
|
61
|
+
const target = { ownerType: 'page', ownerId: page.id, eventName };
|
|
62
|
+
const ctx = createEventContext({
|
|
63
|
+
mode: eventRuntime.mode,
|
|
64
|
+
report: eventRuntime.report,
|
|
65
|
+
page,
|
|
66
|
+
data: eventRuntime.data,
|
|
67
|
+
parameters: eventRuntime.parameters,
|
|
68
|
+
variables: eventRuntime.variables,
|
|
69
|
+
state: eventRuntime.state,
|
|
70
|
+
log: eventRuntime.log,
|
|
71
|
+
target,
|
|
72
|
+
runtime: eventRuntime.runtime,
|
|
73
|
+
execution,
|
|
74
|
+
});
|
|
75
|
+
runEventScript({
|
|
76
|
+
event: page.events?.[eventName],
|
|
77
|
+
ctx,
|
|
78
|
+
target,
|
|
79
|
+
eventLogs: eventRuntime.log,
|
|
80
|
+
runtimeState: eventRuntime.runtime,
|
|
81
|
+
});
|
|
82
|
+
return execution;
|
|
83
|
+
}
|
|
84
|
+
export function renderReport(template, data = {}, options = {}) {
|
|
85
|
+
return renderReportInternal(template, data, options);
|
|
86
|
+
}
|
|
87
|
+
function renderReportInternal(template, data, options) {
|
|
88
|
+
const normalizedTemplate = normalizeTemplate(mergeInferredDataSources(cloneReportTemplate(template), data));
|
|
89
|
+
const expandedData = expandJsonDataBySources(data, normalizedTemplate.dataSources);
|
|
90
|
+
const eventRuntime = createRenderEventRuntime(normalizedTemplate, expandedData, options);
|
|
91
|
+
if (!options.suppressEvents) {
|
|
92
|
+
const modeEvent = eventRuntime.mode === 'preview' ? 'beforePreview' : 'beforePrint';
|
|
93
|
+
const modeExecution = runReportEvent(normalizedTemplate, modeEvent, eventRuntime);
|
|
94
|
+
if (modeExecution.canceled) {
|
|
95
|
+
return { pages: [], fonts: normalizedTemplate.fonts, eventLogs: eventRuntime.log.entries };
|
|
96
|
+
}
|
|
97
|
+
for (const eventName of ['beforeRender', 'beforeData']) {
|
|
98
|
+
const execution = runReportEvent(normalizedTemplate, eventName, eventRuntime);
|
|
99
|
+
if (execution.canceled) {
|
|
100
|
+
return { pages: [], fonts: normalizedTemplate.fonts, eventLogs: eventRuntime.log.entries };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const templatePage = normalizedTemplate.pages[0];
|
|
105
|
+
const plan = buildBandPlan(normalizedTemplate);
|
|
106
|
+
const logicalItems = executeBandPlan(plan, expandedData, {
|
|
107
|
+
expressionVariables: options.expressionVariables,
|
|
108
|
+
expressionFunctions: options.expressionFunctions,
|
|
109
|
+
});
|
|
110
|
+
if (!options.suppressEvents) {
|
|
111
|
+
runReportEvent(normalizedTemplate, 'afterData', eventRuntime);
|
|
112
|
+
}
|
|
113
|
+
const pages = paginate(templatePage, plan.pageBands, logicalItems, expandedData, normalizedTemplate.styles, normalizedTemplate.conditionalFormats, {
|
|
114
|
+
...options,
|
|
115
|
+
eventRuntime: options.suppressEvents ? undefined : eventRuntime,
|
|
116
|
+
});
|
|
117
|
+
const document = applyPageNumberPass({ pages, fonts: normalizedTemplate.fonts, eventLogs: eventRuntime.log.entries });
|
|
118
|
+
if (!options.suppressEvents) {
|
|
119
|
+
runReportEvent(normalizedTemplate, 'afterRender', eventRuntime);
|
|
120
|
+
}
|
|
121
|
+
document.eventLogs = eventRuntime.log.entries;
|
|
122
|
+
return document;
|
|
123
|
+
}
|
|
124
|
+
export function paginate(templatePage, pageBands, logicalItems, rowsByBand = {}, styles = [], conditionalFormats = [], options = {}) {
|
|
125
|
+
const pages = [];
|
|
126
|
+
const printableX = templatePage.margins.left;
|
|
127
|
+
const printableWidth = templatePage.width - templatePage.margins.left - templatePage.margins.right;
|
|
128
|
+
const footerHeight = pageBands.pageFooter.reduce((sum, band) => sum + band.height, 0);
|
|
129
|
+
const pageBottomY = templatePage.height - templatePage.margins.bottom - footerHeight;
|
|
130
|
+
const lastPageBandIds = collectLastPageBandIds(templatePage);
|
|
131
|
+
const repeatedGroups = [];
|
|
132
|
+
const pageRows = new WeakMap();
|
|
133
|
+
let activeSectionRepeatBands = [];
|
|
134
|
+
let currentPage;
|
|
135
|
+
let cursorY = 0;
|
|
136
|
+
let columnFlow;
|
|
137
|
+
let pendingColumnHeaders = [];
|
|
138
|
+
const newPage = () => {
|
|
139
|
+
currentPage = {
|
|
140
|
+
id: `${templatePage.id}-${pages.length + 1}`,
|
|
141
|
+
pageNumber: pages.length + 1,
|
|
142
|
+
totalPages: 0,
|
|
143
|
+
width: templatePage.width,
|
|
144
|
+
height: templatePage.height,
|
|
145
|
+
backgroundColor: templatePage.backgroundColor,
|
|
146
|
+
watermark: clonePageWatermark(templatePage.watermark),
|
|
147
|
+
pageBorder: clonePageBorder(templatePage.pageBorder),
|
|
148
|
+
items: [],
|
|
149
|
+
};
|
|
150
|
+
runPageEvent(templatePage, 'beforePrint', options.eventRuntime);
|
|
151
|
+
pages.push(currentPage);
|
|
152
|
+
pageRows.set(currentPage, {});
|
|
153
|
+
cursorY = templatePage.margins.top;
|
|
154
|
+
for (const header of pageBands.pageHeader) {
|
|
155
|
+
placeBand(header, createEmptyContext(options, rowsByBand), true);
|
|
156
|
+
}
|
|
157
|
+
for (const groupHeader of repeatedGroups) {
|
|
158
|
+
placeBand(groupHeader, createEmptyContext(options, rowsByBand), true);
|
|
159
|
+
}
|
|
160
|
+
for (const sectionBand of activeSectionRepeatBands) {
|
|
161
|
+
if (columnFlow && sectionBand.type === 'columnHeader') {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
placeBand(sectionBand, createEmptyContext(options, rowsByBand), true);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
const ensurePage = () => {
|
|
168
|
+
if (!currentPage) {
|
|
169
|
+
newPage();
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const placeBand = (band, context, force = false) => {
|
|
173
|
+
ensurePage();
|
|
174
|
+
const runtimeContext = withRuntimeContext(context, options);
|
|
175
|
+
const eventBand = prepareBandInstance(band, runtimeContext, options, templatePage);
|
|
176
|
+
if (!eventBand) {
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
const layoutContext = withParameters(runtimeContext, options.parameters);
|
|
180
|
+
const behavior = getBandBehavior(eventBand);
|
|
181
|
+
if (!shouldPrintBand(behavior, currentPage.pageNumber, layoutContext, rowsByBand)) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
let layoutState = createLayoutState(options);
|
|
185
|
+
const currentPageRows = currentPage ? pageRows.get(currentPage) ?? {} : {};
|
|
186
|
+
let preview = layoutBand(eventBand, { x: printableX, y: cursorY, width: printableWidth, context: layoutContext, rowsByBand, pageRowsByBand: currentPageRows, styles, conditionalFormats, renderSubreport: createSubreportRenderer(rowsByBand, options, false), eventRuntime: withEventPage(options.eventRuntime, templatePage), eventState: layoutState, eventMode: 'measure' });
|
|
187
|
+
if (behavior.printIfEmpty === false && preview.components.length === 0) {
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
190
|
+
const breakIfLessThan = behavior.breakIfLessThan ?? 0;
|
|
191
|
+
if (!force && breakIfLessThan > 0 && pageBottomY - cursorY < breakIfLessThan && currentPage.items.length > 0) {
|
|
192
|
+
newPage();
|
|
193
|
+
preview = layoutBand(eventBand, { x: printableX, y: cursorY, width: printableWidth, context: layoutContext, rowsByBand, pageRowsByBand: pageRows.get(currentPage) ?? {}, styles, conditionalFormats, renderSubreport: createSubreportRenderer(rowsByBand, options, false), eventRuntime: withEventPage(options.eventRuntime, templatePage), eventState: layoutState, eventMode: 'measure' });
|
|
194
|
+
}
|
|
195
|
+
if (!force && cursorY + preview.height > pageBottomY && currentPage.items.length > 0) {
|
|
196
|
+
newPage();
|
|
197
|
+
preview = layoutBand(eventBand, { x: printableX, y: cursorY, width: printableWidth, context: layoutContext, rowsByBand, pageRowsByBand: pageRows.get(currentPage) ?? {}, styles, conditionalFormats, renderSubreport: createSubreportRenderer(rowsByBand, options, false), eventRuntime: withEventPage(options.eventRuntime, templatePage), eventState: layoutState, eventMode: 'measure' });
|
|
198
|
+
}
|
|
199
|
+
const splitTable = !force ? splitTableBand(eventBand, preview, cursorY, pageBottomY, templatePage.margins.top) : undefined;
|
|
200
|
+
if (splitTable) {
|
|
201
|
+
let placedBox;
|
|
202
|
+
for (const chunk of splitTable.chunks) {
|
|
203
|
+
if (cursorY + chunk.height > pageBottomY && currentPage.items.length > 0) {
|
|
204
|
+
newPage();
|
|
205
|
+
}
|
|
206
|
+
const box = createTableChunkBandBox(preview, chunk, cursorY);
|
|
207
|
+
currentPage.items.push(box);
|
|
208
|
+
collectPageRow(pageRows.get(currentPage), eventBand, runtimeContext);
|
|
209
|
+
cursorY += box.height;
|
|
210
|
+
placedBox = box;
|
|
211
|
+
}
|
|
212
|
+
finishBandInstance(eventBand, runtimeContext, options, templatePage);
|
|
213
|
+
return placedBox;
|
|
214
|
+
}
|
|
215
|
+
const targetY = behavior.printAtBottom ? pageBottomY - preview.height : cursorY;
|
|
216
|
+
const box = layoutBand(eventBand, { x: printableX, y: targetY, width: printableWidth, context: layoutContext, rowsByBand, pageRowsByBand: pageRows.get(currentPage) ?? {}, styles, conditionalFormats, renderSubreport: createSubreportRenderer(rowsByBand, options, true), eventRuntime: withEventPage(options.eventRuntime, templatePage), eventState: layoutState, eventMode: 'render' });
|
|
217
|
+
currentPage.items.push(box);
|
|
218
|
+
collectPageRow(pageRows.get(currentPage), eventBand, runtimeContext);
|
|
219
|
+
cursorY = behavior.printAtBottom ? pageBottomY : cursorY + box.height;
|
|
220
|
+
finishBandInstance(eventBand, runtimeContext, options, templatePage);
|
|
221
|
+
return box;
|
|
222
|
+
};
|
|
223
|
+
const measureBandAt = (band, context, x, y, width) => {
|
|
224
|
+
const layoutContext = withParameters(withRuntimeContext(context, options), options.parameters);
|
|
225
|
+
return layoutBand(band, {
|
|
226
|
+
x,
|
|
227
|
+
y,
|
|
228
|
+
width,
|
|
229
|
+
context: layoutContext,
|
|
230
|
+
rowsByBand,
|
|
231
|
+
pageRowsByBand: currentPage ? pageRows.get(currentPage) ?? {} : {},
|
|
232
|
+
styles,
|
|
233
|
+
conditionalFormats,
|
|
234
|
+
renderSubreport: createSubreportRenderer(rowsByBand, options, false),
|
|
235
|
+
eventRuntime: undefined,
|
|
236
|
+
eventMode: 'measure',
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
const placeBandAt = (band, context, x, y, width) => {
|
|
240
|
+
ensurePage();
|
|
241
|
+
const runtimeContext = withRuntimeContext(context, options);
|
|
242
|
+
const eventBand = prepareBandInstance(band, runtimeContext, options, templatePage);
|
|
243
|
+
if (!eventBand) {
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
const layoutContext = withParameters(runtimeContext, options.parameters);
|
|
247
|
+
const behavior = getBandBehavior(eventBand);
|
|
248
|
+
if (!shouldPrintBand(behavior, currentPage.pageNumber, layoutContext, rowsByBand)) {
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
const layoutState = createLayoutState(options);
|
|
252
|
+
const preview = layoutBand(eventBand, { x, y, width, context: layoutContext, rowsByBand, pageRowsByBand: pageRows.get(currentPage) ?? {}, styles, conditionalFormats, renderSubreport: createSubreportRenderer(rowsByBand, options, false), eventRuntime: withEventPage(options.eventRuntime, templatePage), eventState: layoutState, eventMode: 'measure' });
|
|
253
|
+
if (behavior.printIfEmpty === false && preview.components.length === 0) {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
const box = layoutBand(eventBand, { x, y, width, context: layoutContext, rowsByBand, pageRowsByBand: pageRows.get(currentPage) ?? {}, styles, conditionalFormats, renderSubreport: createSubreportRenderer(rowsByBand, options, true), eventRuntime: withEventPage(options.eventRuntime, templatePage), eventState: layoutState, eventMode: 'render' });
|
|
257
|
+
currentPage.items.push(box);
|
|
258
|
+
collectPageRow(pageRows.get(currentPage), eventBand, runtimeContext);
|
|
259
|
+
finishBandInstance(eventBand, runtimeContext, options, templatePage);
|
|
260
|
+
return box;
|
|
261
|
+
};
|
|
262
|
+
const renderColumnHeaders = (flow) => {
|
|
263
|
+
if (flow.columnHeaders.length === 0) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
let headerY = cursorY;
|
|
267
|
+
for (const item of flow.columnHeaders) {
|
|
268
|
+
let headerHeight = 0;
|
|
269
|
+
for (let columnIndex = 0; columnIndex < flow.layout.count; columnIndex += 1) {
|
|
270
|
+
const x = printableX + columnIndex * (flow.layout.width + flow.layout.gap);
|
|
271
|
+
const box = placeBandAt(item.band, item.context, x, headerY, flow.layout.width);
|
|
272
|
+
headerHeight = Math.max(headerHeight, box?.height ?? item.band.height);
|
|
273
|
+
}
|
|
274
|
+
headerY += headerHeight;
|
|
275
|
+
}
|
|
276
|
+
flow.columnCursors = Array.from({ length: flow.layout.count }, () => headerY);
|
|
277
|
+
flow.rowY = headerY;
|
|
278
|
+
flow.rowHeight = 0;
|
|
279
|
+
cursorY = headerY;
|
|
280
|
+
};
|
|
281
|
+
const resetColumnFlowForPage = (flow) => {
|
|
282
|
+
flow.columnIndex = 0;
|
|
283
|
+
flow.columnCursors = Array.from({ length: flow.layout.count }, () => cursorY);
|
|
284
|
+
flow.rowY = cursorY;
|
|
285
|
+
flow.rowHeight = 0;
|
|
286
|
+
renderColumnHeaders(flow);
|
|
287
|
+
};
|
|
288
|
+
const ensureColumnFlow = (layout, columnHeaders, dataBandId) => {
|
|
289
|
+
if (!columnFlow || columnFlow.dataBandId !== dataBandId || !sameColumnLayout(columnFlow.layout, layout)) {
|
|
290
|
+
columnFlow = {
|
|
291
|
+
dataBandId,
|
|
292
|
+
layout,
|
|
293
|
+
columnHeaders,
|
|
294
|
+
columnIndex: 0,
|
|
295
|
+
columnCursors: Array.from({ length: layout.count }, () => cursorY),
|
|
296
|
+
rowY: cursorY,
|
|
297
|
+
rowHeight: 0,
|
|
298
|
+
};
|
|
299
|
+
renderColumnHeaders(columnFlow);
|
|
300
|
+
}
|
|
301
|
+
return columnFlow;
|
|
302
|
+
};
|
|
303
|
+
const finishColumnFlow = () => {
|
|
304
|
+
if (!columnFlow) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
cursorY = columnFlow.layout.direction === 'acrossThenDown'
|
|
308
|
+
? columnFlow.rowY + (columnFlow.columnIndex > 0 ? columnFlow.rowHeight : 0)
|
|
309
|
+
: Math.max(...columnFlow.columnCursors);
|
|
310
|
+
columnFlow = undefined;
|
|
311
|
+
};
|
|
312
|
+
const placeColumnDataBand = (band, context, layout, columnHeaders) => {
|
|
313
|
+
ensurePage();
|
|
314
|
+
const flow = ensureColumnFlow(layout, columnHeaders, band.id);
|
|
315
|
+
if (layout.direction === 'acrossThenDown') {
|
|
316
|
+
const columnX = printableX + flow.columnIndex * (layout.width + layout.gap);
|
|
317
|
+
const preview = measureBandAt(band, context, columnX, flow.rowY, layout.width);
|
|
318
|
+
if (flow.columnIndex === 0 && flow.rowY + preview.height > pageBottomY && currentPage.items.length > 0) {
|
|
319
|
+
newPage();
|
|
320
|
+
resetColumnFlowForPage(flow);
|
|
321
|
+
}
|
|
322
|
+
const x = printableX + flow.columnIndex * (layout.width + layout.gap);
|
|
323
|
+
const box = placeBandAt(band, context, x, flow.rowY, layout.width);
|
|
324
|
+
const placedHeight = box?.height ?? preview.height;
|
|
325
|
+
flow.rowHeight = Math.max(flow.rowHeight, placedHeight);
|
|
326
|
+
if (flow.columnIndex >= layout.count - 1) {
|
|
327
|
+
flow.rowY += flow.rowHeight;
|
|
328
|
+
flow.rowHeight = 0;
|
|
329
|
+
flow.columnIndex = 0;
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
flow.columnIndex += 1;
|
|
333
|
+
}
|
|
334
|
+
cursorY = flow.rowY + (flow.columnIndex > 0 ? flow.rowHeight : 0);
|
|
335
|
+
return box;
|
|
336
|
+
}
|
|
337
|
+
let columnY = flow.columnCursors[flow.columnIndex];
|
|
338
|
+
let columnX = printableX + flow.columnIndex * (layout.width + layout.gap);
|
|
339
|
+
let preview = measureBandAt(band, context, columnX, columnY, layout.width);
|
|
340
|
+
if (columnY + preview.height > pageBottomY && currentPage.items.length > 0) {
|
|
341
|
+
if (flow.columnIndex < layout.count - 1) {
|
|
342
|
+
flow.columnIndex += 1;
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
newPage();
|
|
346
|
+
resetColumnFlowForPage(flow);
|
|
347
|
+
}
|
|
348
|
+
columnY = flow.columnCursors[flow.columnIndex];
|
|
349
|
+
columnX = printableX + flow.columnIndex * (layout.width + layout.gap);
|
|
350
|
+
preview = measureBandAt(band, context, columnX, columnY, layout.width);
|
|
351
|
+
}
|
|
352
|
+
const box = placeBandAt(band, context, columnX, columnY, layout.width);
|
|
353
|
+
flow.columnCursors[flow.columnIndex] += box?.height ?? preview.height;
|
|
354
|
+
cursorY = Math.max(...flow.columnCursors);
|
|
355
|
+
return box;
|
|
356
|
+
};
|
|
357
|
+
const placeColumnFooterBand = (item) => {
|
|
358
|
+
if (!columnFlow) {
|
|
359
|
+
placeBand(item.band, item.context);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const footerY = columnFlow.layout.direction === 'acrossThenDown'
|
|
363
|
+
? columnFlow.rowY + (columnFlow.columnIndex > 0 ? columnFlow.rowHeight : 0)
|
|
364
|
+
: Math.max(...columnFlow.columnCursors);
|
|
365
|
+
let footerHeight = 0;
|
|
366
|
+
for (let columnIndex = 0; columnIndex < columnFlow.layout.count; columnIndex += 1) {
|
|
367
|
+
const x = printableX + columnIndex * (columnFlow.layout.width + columnFlow.layout.gap);
|
|
368
|
+
const box = placeBandAt(item.band, item.context, x, footerY, columnFlow.layout.width);
|
|
369
|
+
footerHeight = Math.max(footerHeight, box?.height ?? item.band.height);
|
|
370
|
+
}
|
|
371
|
+
cursorY = footerY + footerHeight;
|
|
372
|
+
columnFlow = undefined;
|
|
373
|
+
};
|
|
374
|
+
const flushPendingColumnHeaders = () => {
|
|
375
|
+
for (const item of pendingColumnHeaders) {
|
|
376
|
+
placeBand(item.band, item.context);
|
|
377
|
+
}
|
|
378
|
+
pendingColumnHeaders = [];
|
|
379
|
+
};
|
|
380
|
+
for (const item of logicalItems) {
|
|
381
|
+
if (item.kind === 'pageBreak') {
|
|
382
|
+
newPage();
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
if (item.band.type === 'columnHeader') {
|
|
386
|
+
pendingColumnHeaders.push(item);
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (item.band.type === 'groupHeader' && getBandBehavior(item.band).printOnAllPages) {
|
|
390
|
+
if (!repeatedGroups.some((band) => band.id === item.band.id)) {
|
|
391
|
+
repeatedGroups.push(item.band);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (item.band.type === 'groupFooter') {
|
|
395
|
+
const estimated = item.band.height;
|
|
396
|
+
if (cursorY + estimated > pageBottomY && currentPage?.items.length) {
|
|
397
|
+
newPage();
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (item.repeatOnPageBreakBefore) {
|
|
401
|
+
activeSectionRepeatBands = item.repeatOnPageBreakBefore;
|
|
402
|
+
}
|
|
403
|
+
if (item.band.type === 'columnFooter' && columnFlow) {
|
|
404
|
+
placeColumnFooterBand(item);
|
|
405
|
+
activeSectionRepeatBands = [];
|
|
406
|
+
pendingColumnHeaders = [];
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
if (['footer', 'columnFooter', 'reportSummary'].includes(item.band.type)) {
|
|
410
|
+
flushPendingColumnHeaders();
|
|
411
|
+
finishColumnFlow();
|
|
412
|
+
activeSectionRepeatBands = [];
|
|
413
|
+
}
|
|
414
|
+
const columnLayout = resolveDataBandColumnLayout(item.band, printableWidth);
|
|
415
|
+
if (columnLayout) {
|
|
416
|
+
placeColumnDataBand(item.band, item.context, columnLayout, pendingColumnHeaders);
|
|
417
|
+
pendingColumnHeaders = [];
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
flushPendingColumnHeaders();
|
|
421
|
+
finishColumnFlow();
|
|
422
|
+
placeBand(item.band, item.context);
|
|
423
|
+
}
|
|
424
|
+
if (item.band.type === 'groupFooter') {
|
|
425
|
+
const index = repeatedGroups.length - 1;
|
|
426
|
+
if (index >= 0) {
|
|
427
|
+
repeatedGroups.splice(index, 1);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
flushPendingColumnHeaders();
|
|
432
|
+
finishColumnFlow();
|
|
433
|
+
if (!currentPage) {
|
|
434
|
+
newPage();
|
|
435
|
+
}
|
|
436
|
+
for (const page of pages) {
|
|
437
|
+
for (const overlay of pageBands.overlay) {
|
|
438
|
+
const box = renderFixedBand(overlay, createEmptyContext(options, rowsByBand), templatePage.margins.top, pageRows.get(page) ?? {}, templatePage, page.pageNumber, printableX, printableWidth, rowsByBand, styles, conditionalFormats, options);
|
|
439
|
+
if (box) {
|
|
440
|
+
page.items.unshift(box);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
let footerY = templatePage.height - templatePage.margins.bottom - footerHeight;
|
|
444
|
+
for (const footer of pageBands.pageFooter) {
|
|
445
|
+
const box = renderFixedBand(footer, createEmptyContext(options, rowsByBand), footerY, pageRows.get(page) ?? {}, templatePage, page.pageNumber, printableX, printableWidth, rowsByBand, styles, conditionalFormats, options);
|
|
446
|
+
if (box) {
|
|
447
|
+
page.items.push(box);
|
|
448
|
+
footerY += box.height;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
runPageEvent(templatePage, 'afterPrint', options.eventRuntime);
|
|
452
|
+
}
|
|
453
|
+
removeNonFinalLastPageBands(pages, lastPageBandIds);
|
|
454
|
+
return pages;
|
|
455
|
+
}
|
|
456
|
+
function createLayoutState(options) {
|
|
457
|
+
return options.eventRuntime ? createLayoutEventState() : undefined;
|
|
458
|
+
}
|
|
459
|
+
function resolveDataBandColumnLayout(band, availableWidth) {
|
|
460
|
+
if (band.type !== 'data') {
|
|
461
|
+
return undefined;
|
|
462
|
+
}
|
|
463
|
+
const columns = band.dataBand?.columns;
|
|
464
|
+
const count = Math.max(1, Math.floor(columns?.count ?? 1));
|
|
465
|
+
if (count <= 1) {
|
|
466
|
+
return undefined;
|
|
467
|
+
}
|
|
468
|
+
const gap = Math.max(0, columns?.gap ?? 0);
|
|
469
|
+
const width = Math.max(1, (availableWidth - gap * (count - 1)) / count);
|
|
470
|
+
return {
|
|
471
|
+
count,
|
|
472
|
+
gap,
|
|
473
|
+
width,
|
|
474
|
+
direction: columns?.direction ?? 'downThenAcross',
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function sameColumnLayout(a, b) {
|
|
478
|
+
return a.count === b.count
|
|
479
|
+
&& a.gap === b.gap
|
|
480
|
+
&& a.width === b.width
|
|
481
|
+
&& a.direction === b.direction;
|
|
482
|
+
}
|
|
483
|
+
function clonePageWatermark(watermark) {
|
|
484
|
+
return watermark ? { ...watermark } : undefined;
|
|
485
|
+
}
|
|
486
|
+
function clonePageBorder(pageBorder) {
|
|
487
|
+
return pageBorder
|
|
488
|
+
? { ...pageBorder, sides: { ...pageBorder.sides } }
|
|
489
|
+
: undefined;
|
|
490
|
+
}
|
|
491
|
+
function collectLastPageBandIds(page) {
|
|
492
|
+
return new Set(page.bands.filter((band) => getBandBehavior(band).printOn === 'lastPage').map((band) => band.id));
|
|
493
|
+
}
|
|
494
|
+
function removeNonFinalLastPageBands(pages, lastPageBandIds) {
|
|
495
|
+
if (lastPageBandIds.size === 0)
|
|
496
|
+
return;
|
|
497
|
+
const finalPage = pages.at(-1);
|
|
498
|
+
for (const page of pages) {
|
|
499
|
+
if (page === finalPage)
|
|
500
|
+
continue;
|
|
501
|
+
page.items = page.items.filter((item) => !lastPageBandIds.has(item.bandId));
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
function renderFixedBand(band, context, y, pageRowsByBand, templatePage, pageNumber, x, width, rowsByBand, styles, conditionalFormats, options) {
|
|
505
|
+
const runtimeContext = withRuntimeContext(context, options);
|
|
506
|
+
const eventBand = prepareBandInstance(band, runtimeContext, options, templatePage);
|
|
507
|
+
if (!eventBand) {
|
|
508
|
+
return undefined;
|
|
509
|
+
}
|
|
510
|
+
const layoutContext = withParameters(runtimeContext, options.parameters);
|
|
511
|
+
const behavior = getBandBehavior(eventBand);
|
|
512
|
+
if (!shouldPrintBand(behavior, pageNumber, layoutContext, rowsByBand)) {
|
|
513
|
+
return undefined;
|
|
514
|
+
}
|
|
515
|
+
const box = layoutBand(eventBand, {
|
|
516
|
+
x,
|
|
517
|
+
y,
|
|
518
|
+
width,
|
|
519
|
+
context: layoutContext,
|
|
520
|
+
rowsByBand,
|
|
521
|
+
pageRowsByBand,
|
|
522
|
+
styles,
|
|
523
|
+
conditionalFormats,
|
|
524
|
+
renderSubreport: createSubreportRenderer(rowsByBand, options, true),
|
|
525
|
+
eventRuntime: withEventPage(options.eventRuntime, templatePage),
|
|
526
|
+
});
|
|
527
|
+
if (behavior.printIfEmpty === false && box.components.length === 0) {
|
|
528
|
+
return undefined;
|
|
529
|
+
}
|
|
530
|
+
finishBandInstance(eventBand, runtimeContext, options, templatePage);
|
|
531
|
+
return box;
|
|
532
|
+
}
|
|
533
|
+
function splitTableBand(band, box, cursorY, pageBottomY, pageTopY) {
|
|
534
|
+
if (box.components.length !== 1)
|
|
535
|
+
return undefined;
|
|
536
|
+
const component = box.components[0];
|
|
537
|
+
if (component.type !== 'table' || !('rows' in component) || !('columns' in component))
|
|
538
|
+
return undefined;
|
|
539
|
+
const templateTable = band.components.find(item => item.id === component.id && item.type === 'table');
|
|
540
|
+
if (templateTable?.canBreak === false)
|
|
541
|
+
return undefined;
|
|
542
|
+
const table = component;
|
|
543
|
+
const rows = table.rows ?? [];
|
|
544
|
+
const tableTopOffset = Math.max(0, table.y - box.y);
|
|
545
|
+
const bodyRows = rows;
|
|
546
|
+
if (bodyRows.length === 0)
|
|
547
|
+
return undefined;
|
|
548
|
+
const availableFirstPage = pageBottomY - cursorY - tableTopOffset;
|
|
549
|
+
const totalTableHeight = rowsHeight(rows);
|
|
550
|
+
if (availableFirstPage >= totalTableHeight)
|
|
551
|
+
return undefined;
|
|
552
|
+
const chunks = [];
|
|
553
|
+
let remainingBodyRows = bodyRows;
|
|
554
|
+
let availableHeight = availableFirstPage;
|
|
555
|
+
while (remainingBodyRows.length > 0) {
|
|
556
|
+
const chunk = takeTableRowsForPage(table, remainingBodyRows, availableHeight);
|
|
557
|
+
if (chunk.bodyRows.length === 0) {
|
|
558
|
+
chunk.bodyRows = [remainingBodyRows[0]];
|
|
559
|
+
}
|
|
560
|
+
const chunkRows = normalizeTableChunkRows(chunk.bodyRows);
|
|
561
|
+
const tableHeight = rowsHeight(chunkRows);
|
|
562
|
+
chunks.push({
|
|
563
|
+
table: {
|
|
564
|
+
...table,
|
|
565
|
+
y: tableTopOffset,
|
|
566
|
+
height: tableHeight,
|
|
567
|
+
rows: chunkRows,
|
|
568
|
+
},
|
|
569
|
+
height: tableTopOffset + tableHeight,
|
|
570
|
+
tableHeight,
|
|
571
|
+
tableTopOffset,
|
|
572
|
+
});
|
|
573
|
+
remainingBodyRows = remainingBodyRows.slice(chunk.bodyRows.length);
|
|
574
|
+
availableHeight = pageBottomY - pageTopY - tableTopOffset;
|
|
575
|
+
}
|
|
576
|
+
return chunks.length > 1 ? { chunks } : undefined;
|
|
577
|
+
}
|
|
578
|
+
function takeTableRowsForPage(table, bodyRows, availableHeight) {
|
|
579
|
+
const bodyBudget = Math.max(0, availableHeight);
|
|
580
|
+
const selected = [];
|
|
581
|
+
let used = 0;
|
|
582
|
+
for (const row of bodyRows) {
|
|
583
|
+
const rowHeight = renderTableRowHeight(row);
|
|
584
|
+
if (selected.length > 0 && used + rowHeight > bodyBudget)
|
|
585
|
+
break;
|
|
586
|
+
if (selected.length === 0 && rowHeight > Math.max(0, availableHeight))
|
|
587
|
+
break;
|
|
588
|
+
selected.push(row);
|
|
589
|
+
used += rowHeight;
|
|
590
|
+
}
|
|
591
|
+
if (selected.length === 0 && bodyRows.length > 0 && renderTableRowHeight(bodyRows[0]) <= table.height) {
|
|
592
|
+
selected.push(bodyRows[0]);
|
|
593
|
+
}
|
|
594
|
+
return { bodyRows: selected };
|
|
595
|
+
}
|
|
596
|
+
function createTableChunkBandBox(source, chunk, y) {
|
|
597
|
+
return {
|
|
598
|
+
...source,
|
|
599
|
+
id: `${source.id}-table-chunk-${y}`,
|
|
600
|
+
y,
|
|
601
|
+
height: chunk.height,
|
|
602
|
+
components: [{
|
|
603
|
+
...chunk.table,
|
|
604
|
+
y: y + chunk.tableTopOffset,
|
|
605
|
+
height: chunk.tableHeight,
|
|
606
|
+
}],
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
function normalizeTableChunkRows(rows) {
|
|
610
|
+
return rows.map((row, rowIndex) => row.map(cell => ({
|
|
611
|
+
...cell,
|
|
612
|
+
row: rowIndex,
|
|
613
|
+
})));
|
|
614
|
+
}
|
|
615
|
+
function rowsHeight(rows) {
|
|
616
|
+
return rows.reduce((sum, row) => sum + renderTableRowHeight(row), 0);
|
|
617
|
+
}
|
|
618
|
+
function renderTableRowHeight(row) {
|
|
619
|
+
return row[0]?.height ?? 0;
|
|
620
|
+
}
|
|
621
|
+
function prepareBandInstance(band, context, options, page) {
|
|
622
|
+
const eventBand = cloneBand(band);
|
|
623
|
+
if (!options.eventRuntime) {
|
|
624
|
+
return eventBand;
|
|
625
|
+
}
|
|
626
|
+
const execution = { canceled: false, hidden: false, hasValue: false };
|
|
627
|
+
if (isDataRowBand(eventBand, context)) {
|
|
628
|
+
runBandEvent(eventBand, context, 'beforeRow', options.eventRuntime, page, execution);
|
|
629
|
+
}
|
|
630
|
+
runBandEvent(eventBand, context, 'beforePrint', options.eventRuntime, page, execution);
|
|
631
|
+
return execution.hidden || execution.canceled ? undefined : eventBand;
|
|
632
|
+
}
|
|
633
|
+
function finishBandInstance(band, context, options, page) {
|
|
634
|
+
if (!options.eventRuntime) {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
const execution = { canceled: false, hidden: false, hasValue: false };
|
|
638
|
+
runBandEvent(band, context, 'afterPrint', options.eventRuntime, page, execution);
|
|
639
|
+
if (isDataRowBand(band, context)) {
|
|
640
|
+
runBandEvent(band, context, 'afterRow', options.eventRuntime, page, execution);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
function runBandEvent(band, context, eventName, eventRuntime, page, execution) {
|
|
644
|
+
const event = band.events?.[eventName];
|
|
645
|
+
if (!event) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
const target = { ownerType: 'band', ownerId: band.id, eventName };
|
|
649
|
+
const ctx = createEventContext({
|
|
650
|
+
mode: eventRuntime.mode,
|
|
651
|
+
report: eventRuntime.report,
|
|
652
|
+
page,
|
|
653
|
+
band,
|
|
654
|
+
row: context.row,
|
|
655
|
+
rowIndex: context.rowIndex,
|
|
656
|
+
dataSourceId: context.dataSourceId,
|
|
657
|
+
data: eventRuntime.data,
|
|
658
|
+
parameters: context.parameters ?? eventRuntime.parameters,
|
|
659
|
+
variables: eventRuntime.variables,
|
|
660
|
+
state: eventRuntime.state,
|
|
661
|
+
log: eventRuntime.log,
|
|
662
|
+
target,
|
|
663
|
+
runtime: eventRuntime.runtime,
|
|
664
|
+
execution,
|
|
665
|
+
});
|
|
666
|
+
runEventScript({
|
|
667
|
+
event,
|
|
668
|
+
ctx,
|
|
669
|
+
target,
|
|
670
|
+
eventLogs: eventRuntime.log,
|
|
671
|
+
runtimeState: eventRuntime.runtime,
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
function isDataRowBand(band, context) {
|
|
675
|
+
return Boolean(context.row
|
|
676
|
+
&& context.dataSourceId
|
|
677
|
+
&& (band.type === 'data' || band.type === 'hierarchicalData'));
|
|
678
|
+
}
|
|
679
|
+
function cloneBand(band) {
|
|
680
|
+
return JSON.parse(JSON.stringify(band));
|
|
681
|
+
}
|
|
682
|
+
function withEventPage(eventRuntime, page) {
|
|
683
|
+
return eventRuntime ? { ...eventRuntime, page } : undefined;
|
|
684
|
+
}
|
|
685
|
+
function createSubreportRenderer(rowsByBand, options, enableEvents) {
|
|
686
|
+
return (component, x, y, context) => {
|
|
687
|
+
const template = options.subreports?.[component.templateUrl];
|
|
688
|
+
const maxDepth = options.maxSubreportDepth ?? DEFAULT_MAX_SUBREPORT_DEPTH;
|
|
689
|
+
const depth = options.subreportDepth ?? 0;
|
|
690
|
+
const parameters = resolveSubreportParameters(component, context, rowsByBand);
|
|
691
|
+
if (!template) {
|
|
692
|
+
return {
|
|
693
|
+
missing: true,
|
|
694
|
+
children: [createSubreportPlaceholder(component, x, y, `Missing subreport: ${component.templateUrl}`)],
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
if (depth >= maxDepth) {
|
|
698
|
+
return {
|
|
699
|
+
missing: true,
|
|
700
|
+
children: [createSubreportPlaceholder(component, x, y, `Max subreport depth exceeded: ${component.templateUrl}`)],
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
const subreportRowsByBand = { ...rowsByBand, Parameters: [parameters] };
|
|
704
|
+
const document = renderReportInternal(template, subreportRowsByBand, {
|
|
705
|
+
...options,
|
|
706
|
+
parameters,
|
|
707
|
+
subreportDepth: depth + 1,
|
|
708
|
+
suppressEvents: options.suppressEvents || !enableEvents,
|
|
709
|
+
});
|
|
710
|
+
const children = flattenSubreportPages(document.pages, x, y);
|
|
711
|
+
return {
|
|
712
|
+
missing: false,
|
|
713
|
+
children,
|
|
714
|
+
height: measureChildrenHeight(children, y, component.height),
|
|
715
|
+
};
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
function createSubreportPlaceholder(component, x, y, content) {
|
|
719
|
+
return {
|
|
720
|
+
id: `${component.id}-placeholder`,
|
|
721
|
+
type: 'text',
|
|
722
|
+
x,
|
|
723
|
+
y,
|
|
724
|
+
width: component.width,
|
|
725
|
+
height: component.height,
|
|
726
|
+
content,
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
function offsetRenderComponent(component, dx, dy) {
|
|
730
|
+
if ('children' in component) {
|
|
731
|
+
return {
|
|
732
|
+
...component,
|
|
733
|
+
x: component.x + dx,
|
|
734
|
+
y: component.y + dy,
|
|
735
|
+
children: component.children.map(child => offsetRenderComponent(child, dx, dy)),
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
return {
|
|
739
|
+
...component,
|
|
740
|
+
x: component.x + dx,
|
|
741
|
+
y: component.y + dy,
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
function flattenSubreportPages(pages, x, y) {
|
|
745
|
+
let pageOffsetY = 0;
|
|
746
|
+
const children = [];
|
|
747
|
+
for (const page of pages) {
|
|
748
|
+
for (const item of page.items) {
|
|
749
|
+
children.push(...item.components.map(child => offsetRenderComponent(child, x, y + pageOffsetY)));
|
|
750
|
+
}
|
|
751
|
+
pageOffsetY += page.height;
|
|
752
|
+
}
|
|
753
|
+
return children;
|
|
754
|
+
}
|
|
755
|
+
function measureChildrenHeight(children, y, fallback) {
|
|
756
|
+
if (children.length === 0)
|
|
757
|
+
return fallback;
|
|
758
|
+
return Math.max(fallback, ...children.map(child => child.y + child.height - y));
|
|
759
|
+
}
|
|
760
|
+
function resolveSubreportParameters(component, context, rowsByBand) {
|
|
761
|
+
return Object.fromEntries(Object.entries(component.parameters ?? {}).map(([key, expression]) => [
|
|
762
|
+
key,
|
|
763
|
+
resolveTemplateValue(expression, context, rowsByBand),
|
|
764
|
+
]));
|
|
765
|
+
}
|
|
766
|
+
function resolveTemplateValue(value, context, rowsByBand) {
|
|
767
|
+
if (!value.includes('{') && !value.includes('(') && !value.includes('=') && !value.startsWith('"')) {
|
|
768
|
+
return value;
|
|
769
|
+
}
|
|
770
|
+
const placeholderPattern = /\{([^{}]+)\}/g;
|
|
771
|
+
const isSinglePlaceholder = value.trim().match(/^\{([^{}]+)\}$/);
|
|
772
|
+
if (!isSinglePlaceholder) {
|
|
773
|
+
try {
|
|
774
|
+
return evalExpression(value, (source, field) => resolveField(context, source, field), context.rowIndex, expressionVariables(context, { rowsByBand }), undefined, context.expressionFunctions);
|
|
775
|
+
}
|
|
776
|
+
catch {
|
|
777
|
+
// Fall through to mixed literal placeholder replacement.
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
if (!isSinglePlaceholder && placeholderPattern.test(value)) {
|
|
781
|
+
return value.replace(/\{([^{}]+)\}/g, (match, expressionBody) => {
|
|
782
|
+
try {
|
|
783
|
+
const result = evalExpression(`{${expressionBody}}`, (source, field) => resolveField(context, source, field), context.rowIndex, expressionVariables(context), undefined, context.expressionFunctions);
|
|
784
|
+
return result == null ? '' : String(result);
|
|
785
|
+
}
|
|
786
|
+
catch {
|
|
787
|
+
return match;
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
try {
|
|
792
|
+
return evalExpression(value, (source, field) => resolveField(context, source, field), context.rowIndex, expressionVariables(context, { rowsByBand }), undefined, context.expressionFunctions);
|
|
793
|
+
}
|
|
794
|
+
catch {
|
|
795
|
+
return value;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
function resolveField(context, source, field) {
|
|
799
|
+
if (['Parameter', 'Parameters', 'Params'].includes(source)) {
|
|
800
|
+
return context.parameters?.[field];
|
|
801
|
+
}
|
|
802
|
+
if (!source && context.parameters && field in context.parameters) {
|
|
803
|
+
return context.parameters[field];
|
|
804
|
+
}
|
|
805
|
+
if (!source && context.expressionVariables && field in context.expressionVariables) {
|
|
806
|
+
return context.expressionVariables[field];
|
|
807
|
+
}
|
|
808
|
+
const row = context.row ?? {};
|
|
809
|
+
const scoped = source ? row[source] : undefined;
|
|
810
|
+
if (scoped && typeof scoped === 'object' && !Array.isArray(scoped)) {
|
|
811
|
+
return scoped[field];
|
|
812
|
+
}
|
|
813
|
+
return row[field] ?? row[`${source}.${field}`] ?? context.groupValues[field];
|
|
814
|
+
}
|
|
815
|
+
function collectPageRow(pageRows, band, context) {
|
|
816
|
+
if (band.type !== 'data' && band.type !== 'hierarchicalData')
|
|
817
|
+
return;
|
|
818
|
+
if (!context.dataSourceId || !context.row)
|
|
819
|
+
return;
|
|
820
|
+
if (!pageRows[context.dataSourceId]) {
|
|
821
|
+
pageRows[context.dataSourceId] = [];
|
|
822
|
+
}
|
|
823
|
+
pageRows[context.dataSourceId].push(context.row);
|
|
824
|
+
}
|
|
825
|
+
function createEmptyContext(options, rowsByBand = {}) {
|
|
826
|
+
const rootRow = rowsByBand.root?.[0];
|
|
827
|
+
return {
|
|
828
|
+
row: rootRow,
|
|
829
|
+
rowIndex: 0,
|
|
830
|
+
dataSourceId: rootRow ? 'root' : undefined,
|
|
831
|
+
groupValues: {},
|
|
832
|
+
parameters: options.parameters,
|
|
833
|
+
expressionVariables: options.expressionVariables,
|
|
834
|
+
expressionFunctions: options.expressionFunctions,
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
function withParameters(context, parameters) {
|
|
838
|
+
return {
|
|
839
|
+
...context,
|
|
840
|
+
parameters: context.parameters ?? parameters,
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
function withRuntimeContext(context, options) {
|
|
844
|
+
return {
|
|
845
|
+
...context,
|
|
846
|
+
expressionVariables: context.expressionVariables ?? options.expressionVariables,
|
|
847
|
+
expressionFunctions: context.expressionFunctions ?? options.expressionFunctions,
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
function expressionVariables(context, extra = {}) {
|
|
851
|
+
return {
|
|
852
|
+
row: context.row,
|
|
853
|
+
groupValues: context.groupValues,
|
|
854
|
+
parameters: context.parameters,
|
|
855
|
+
...extra,
|
|
856
|
+
...(context.expressionVariables ?? {}),
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
function getBandBehavior(band) {
|
|
860
|
+
return band.behavior ?? {
|
|
861
|
+
enabled: true,
|
|
862
|
+
printOn: 'allPages',
|
|
863
|
+
printIfEmpty: true,
|
|
864
|
+
printOnAllPages: isRepeatOnEveryPageBandType(band.type),
|
|
865
|
+
keepTogether: false,
|
|
866
|
+
canBreak: band.type === 'data',
|
|
867
|
+
printAtBottom: band.type === 'pageFooter',
|
|
868
|
+
autoGrow: true,
|
|
869
|
+
autoShrink: false,
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
function shouldPrintBand(behavior, pageNumber, context, rowsByBand) {
|
|
873
|
+
if (behavior.enabled === false)
|
|
874
|
+
return false;
|
|
875
|
+
if (behavior.visibleExpression && !resolveTemplateBoolean(behavior.visibleExpression, context, rowsByBand))
|
|
876
|
+
return false;
|
|
877
|
+
switch (behavior.printOn) {
|
|
878
|
+
case 'firstPage':
|
|
879
|
+
return pageNumber === 1;
|
|
880
|
+
case 'exceptFirstPage':
|
|
881
|
+
return pageNumber > 1;
|
|
882
|
+
case 'oddPages':
|
|
883
|
+
return pageNumber % 2 === 1;
|
|
884
|
+
case 'evenPages':
|
|
885
|
+
return pageNumber % 2 === 0;
|
|
886
|
+
case 'lastPage':
|
|
887
|
+
return true;
|
|
888
|
+
case 'allPages':
|
|
889
|
+
default:
|
|
890
|
+
return true;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
function resolveTemplateBoolean(value, context, rowsByBand) {
|
|
894
|
+
const resolved = resolveTemplateValue(value, context, rowsByBand);
|
|
895
|
+
if (typeof resolved === 'boolean')
|
|
896
|
+
return resolved;
|
|
897
|
+
if (typeof resolved === 'number')
|
|
898
|
+
return resolved !== 0;
|
|
899
|
+
const text = String(resolved ?? '').trim().toLowerCase();
|
|
900
|
+
if (['false', '0', 'no', 'off', ''].includes(text))
|
|
901
|
+
return false;
|
|
902
|
+
return true;
|
|
903
|
+
}
|
|
904
|
+
//# sourceMappingURL=paginate.js.map
|