@muhammara/native-core 7.0.0-beta.3 → 7.0.0-rc.1
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 +57 -7
- package/THIRD_PARTY_NOTICES.md +39 -0
- package/index.js +120 -1
- package/lib/PDFRStreamForBuffer.js +52 -7
- package/lib/PDFRStreamForFile.js +56 -7
- package/lib/PDFStreamForResponse.js +22 -5
- package/lib/PDFWStreamForBuffer.js +49 -10
- package/lib/PDFWStreamForFile.js +26 -5
- package/lib/Recipe.js +86 -30
- package/lib/content-stream.js +503 -0
- package/lib/css-colors.js +149 -0
- package/lib/font-text.js +1461 -0
- package/lib/glyph-list.js +1127 -0
- package/lib/recipe/annotation.js +482 -105
- package/lib/recipe/appendPage.js +8 -1
- package/lib/recipe/colors.js +132 -35
- package/lib/recipe/coordinate.js +82 -16
- package/lib/recipe/encrypt.js +32 -13
- package/lib/recipe/font.js +70 -25
- package/lib/recipe/graphics-state.js +3 -2
- package/lib/recipe/htmlToTextObjects.js +154 -21
- package/lib/recipe/image.js +34 -9
- package/lib/recipe/info.js +93 -22
- package/lib/recipe/insertPage.js +12 -3
- package/lib/recipe/overlay.js +4 -2
- package/lib/recipe/page-references.js +436 -0
- package/lib/recipe/page.js +379 -275
- package/lib/recipe/parameters.js +11 -2
- package/lib/recipe/replaceText.js +464 -30
- package/lib/recipe/shapes.js +418 -45
- package/lib/recipe/split.js +6 -2
- package/lib/recipe/table.js +260 -173
- package/lib/recipe/text.helper.js +176 -10
- package/lib/recipe/text.js +420 -115
- package/lib/recipe/utils.js +66 -3
- package/lib/recipe/vector-line.js +101 -39
- package/lib/recipe/vector-polygon.js +18 -6
- package/lib/recipe/vector.helper.js +150 -13
- package/lib/recipe/vector.js +150 -53
- package/lib/recipe/xObjectForm.js +73 -10
- package/lib/recipe-constants.js +373 -0
- package/muhammara.d.ts +3487 -152
- package/package.json +1 -1
package/lib/recipe/split.js
CHANGED
|
@@ -8,8 +8,12 @@ const utils = require("./utils");
|
|
|
8
8
|
* @function
|
|
9
9
|
* @memberof Recipe#
|
|
10
10
|
* @param {string} [outputDir=''] - The path for the output PDFs.
|
|
11
|
-
* @param {string} [prefix] - The output filename prefix. Defaults to the
|
|
12
|
-
*
|
|
11
|
+
* @param {string} [prefix] - The output filename prefix. Defaults to the
|
|
12
|
+
* source filename; pass one for a Buffer source, which has no filename.
|
|
13
|
+
* @returns {Recipe} The recipe instance. Each page is written to
|
|
14
|
+
* `<outputDir>/<prefix>-<pageNumber>.pdf`.
|
|
15
|
+
* @throws {Error} If the source reader was released by endPDF(), or an output
|
|
16
|
+
* file cannot be written.
|
|
13
17
|
*/
|
|
14
18
|
exports.split = function split(outputDir = "", prefix) {
|
|
15
19
|
prefix = prefix || this.filename;
|
package/lib/recipe/table.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
}
|
|
1
|
+
var { htmlToTextObjects } = require("./htmlToTextObjects");
|
|
2
|
+
var { cloneOptions: clone } = require("./utils");
|
|
3
|
+
var { LineCap, TableRowNth } = require("../recipe-constants");
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Convert a table cell style into text options: a copy of the options with
|
|
7
|
+
* the cell entry moved to `textBox`.
|
|
8
|
+
* @private
|
|
9
|
+
* @param {Object} options - The table, header, row or column options.
|
|
10
|
+
* @param {string} [cell='cell'] - The key holding the cell style, "cell" or "hcell".
|
|
11
|
+
* @returns {Object} The text options.
|
|
12
|
+
*/
|
|
5
13
|
function getCellOptions(options, cell = "cell") {
|
|
6
|
-
|
|
14
|
+
var cellOptions = clone(options);
|
|
7
15
|
|
|
8
16
|
if (cellOptions[cell]) {
|
|
9
17
|
// convert cell options to textBox options
|
|
@@ -13,65 +21,141 @@ function getCellOptions(options, cell = "cell") {
|
|
|
13
21
|
return cellOptions;
|
|
14
22
|
}
|
|
15
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Measure the outer box height text() will draw for a cell.
|
|
26
|
+
* @private
|
|
27
|
+
* @param {Recipe} self - The recipe instance.
|
|
28
|
+
* @param {string} text - The cell text or HTML.
|
|
29
|
+
* @param {Object} column - The table layout column: x, y and width.
|
|
30
|
+
* @param {Object} options - The cell text options.
|
|
31
|
+
* @returns {number} The box height, including padding and minimum height.
|
|
32
|
+
* @throws {TypeError} If no page is active.
|
|
33
|
+
*/
|
|
16
34
|
function getCellHeight(self, text, column, options) {
|
|
17
|
-
|
|
18
|
-
|
|
35
|
+
var colOptions = self._merge(options, { textBox: { width: column.width } });
|
|
36
|
+
var originCoord = self._calibrateCoordinate(
|
|
19
37
|
column.x,
|
|
20
38
|
column.y,
|
|
21
39
|
0,
|
|
22
40
|
0,
|
|
23
41
|
self.pageNumber,
|
|
24
42
|
);
|
|
25
|
-
|
|
43
|
+
var pathOptions = self._getPathOptions(
|
|
26
44
|
colOptions,
|
|
27
45
|
originCoord.nx,
|
|
28
46
|
originCoord.ny,
|
|
29
47
|
);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
pathOptions.html = colOptions.html;
|
|
49
|
+
var textObjects = colOptions.html
|
|
50
|
+
? htmlToTextObjects(text, colOptions)
|
|
51
|
+
: self._makeTextObject(text, pathOptions.size, colOptions);
|
|
52
|
+
var textBox = self._makeTextBox(colOptions);
|
|
53
|
+
var { textHeight } = self._layoutText(textObjects, textBox, pathOptions);
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
textBox.height ||
|
|
57
|
+
Math.max(
|
|
58
|
+
textBox.minHeight,
|
|
59
|
+
textHeight + textBox.paddingTop + textBox.paddingBottom,
|
|
60
|
+
)
|
|
61
|
+
);
|
|
35
62
|
}
|
|
36
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Draw the border of a completed table segment without duplicating its
|
|
66
|
+
* bottom edge.
|
|
67
|
+
* @private
|
|
68
|
+
* @param {Recipe} self - The recipe instance.
|
|
69
|
+
* @param {number} x - The segment left.
|
|
70
|
+
* @param {number} y - The segment top.
|
|
71
|
+
* @param {number} width - The segment width.
|
|
72
|
+
* @param {number} height - The segment height; nothing is drawn when 0.
|
|
73
|
+
* @param {number[]} rowLines - The y of every row bottom.
|
|
74
|
+
* @param {Object} options - The table options; `border` enables drawing.
|
|
75
|
+
* @returns {void}
|
|
76
|
+
*/
|
|
37
77
|
function drawTableBorder(self, x, y, width, height, rowLines, options) {
|
|
38
|
-
|
|
39
|
-
|
|
78
|
+
// A segment without rows has nothing to enclose.
|
|
79
|
+
if (!options.border || height <= 0) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
var borderOptions = Object.assign(
|
|
83
|
+
{},
|
|
84
|
+
options.border === true ? {} : options.border,
|
|
85
|
+
// Keep borders from extending outside of the enclosing box.
|
|
86
|
+
{ lineCap: LineCap.BUTT },
|
|
87
|
+
);
|
|
88
|
+
if (!borderOptions.width) {
|
|
89
|
+
borderOptions.width = 0.5;
|
|
90
|
+
}
|
|
40
91
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
92
|
+
self.rectangle(x, y, width, height, borderOptions);
|
|
93
|
+
var columns = self._layouts["_table_"];
|
|
94
|
+
|
|
95
|
+
// Draw verticals
|
|
96
|
+
for (var index = 0; index < columns.length - 1; index++) {
|
|
97
|
+
var column = columns[index];
|
|
98
|
+
self.line(
|
|
99
|
+
[
|
|
100
|
+
[column.x + column.width, y],
|
|
101
|
+
[column.x + column.width, y + height],
|
|
102
|
+
],
|
|
103
|
+
borderOptions,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
// Draw horizontals; the last row line is the rectangle's bottom edge.
|
|
107
|
+
for (var index = 0; index < rowLines.length - 1; index++) {
|
|
108
|
+
var yPos = rowLines[index];
|
|
109
|
+
self.line(
|
|
110
|
+
[
|
|
111
|
+
[x, yPos],
|
|
112
|
+
[x + width, yPos],
|
|
113
|
+
],
|
|
114
|
+
borderOptions,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
44
118
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Resolves the table's data fields: `order` when given, otherwise the names
|
|
121
|
+
* of the configured `columns`, otherwise every field found in any record, in
|
|
122
|
+
* first-seen order.
|
|
123
|
+
* @private
|
|
124
|
+
* @param {Object[]} contents - The table records.
|
|
125
|
+
* @param {Object} options - The table options.
|
|
126
|
+
* @returns {string[]} The field names, in column order.
|
|
127
|
+
*/
|
|
128
|
+
function tableFields(contents, options) {
|
|
129
|
+
if (options.order && options.order.length) {
|
|
130
|
+
return typeof options.order === "string"
|
|
131
|
+
? options.order
|
|
132
|
+
.split(",")
|
|
133
|
+
.map((field) => field.trim())
|
|
134
|
+
.filter(Boolean)
|
|
135
|
+
: options.order.slice();
|
|
136
|
+
}
|
|
137
|
+
if (options.columns && options.columns.length) {
|
|
138
|
+
return options.columns.map((column) => column.name);
|
|
139
|
+
}
|
|
140
|
+
var fields = [];
|
|
141
|
+
for (var record of contents) {
|
|
142
|
+
for (var field of Object.keys(record || {})) {
|
|
143
|
+
if (!fields.includes(field)) {
|
|
144
|
+
fields.push(field);
|
|
145
|
+
}
|
|
69
146
|
}
|
|
70
147
|
}
|
|
148
|
+
return fields;
|
|
71
149
|
}
|
|
72
150
|
|
|
73
151
|
/**
|
|
74
152
|
* Display text data in tabular form
|
|
153
|
+
* Rows and headers use their rendered text-box heights, including padding,
|
|
154
|
+
* minimum heights, fixed heights, and HTML layout. Empty contents or no selected
|
|
155
|
+
* columns leave the Recipe unchanged. Array-form order preserves exact keys.
|
|
156
|
+
* Header text styles are independent of body styles: column header options
|
|
157
|
+
* (or defaults) are overridden by table header options, then alignToData
|
|
158
|
+
* and column hcell box overrides are applied.
|
|
75
159
|
* @name table
|
|
76
160
|
* @function
|
|
77
161
|
* @memberof Recipe#
|
|
@@ -108,107 +192,101 @@ function drawTableBorder(self, x, y, width, height, rowLines, options) {
|
|
|
108
192
|
* @param {function} [options.overflow] - Called when the next table entry is going to expand the table
|
|
109
193
|
* beyond the given height or page boundary. Its parameters are (self, row) where 'self' is the recipe handle so
|
|
110
194
|
* that other recipe interfaces can be called, and the row number of the data which caused the data overflow.
|
|
195
|
+
* The callback's `this` is also the Recipe instance.
|
|
111
196
|
* The return value can be 'true' which indicates that data processing should stop, or 'false' which indicates that
|
|
112
197
|
* the data should continue being processed with the original [x,y] coordinates, or it can be an object containing
|
|
113
198
|
* a 'position' property indicating the [x,y] coordinates where the next table for the remaining data should start.
|
|
114
199
|
* @param {object} [options.row] - text properties to be applied to all cells in a table row.
|
|
115
200
|
* @param {object} [options.row.cell] - All textBox options from the 'text' interface can be used here.
|
|
116
|
-
* @param {
|
|
117
|
-
* 'even' or 'odd' rows.
|
|
201
|
+
* @param {Recipe.TableRowNth} [options.row.nth] - A `Recipe.TableRowNth` value, indicating that the
|
|
202
|
+
* properties should be applied only to 'even' or 'odd' rows.
|
|
118
203
|
* @returns {Recipe} The recipe instance.
|
|
204
|
+
* @throws {TypeError} If no page is active.
|
|
205
|
+
* @throws {RangeError} If the overflow callback continues into an area too small
|
|
206
|
+
* for the pending row and its repeated header. Return true to stop, or provide
|
|
207
|
+
* enough space; rows are not split and the callback is called once per overflow.
|
|
208
|
+
* @throws {Error} If the overflow callback continues after ending the page
|
|
209
|
+
* without starting another one.
|
|
119
210
|
*/
|
|
120
211
|
exports.table = function table(x, y, contents, options = {}) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
let order = options.order || [];
|
|
124
|
-
let columns = [];
|
|
125
|
-
|
|
126
|
-
if (order.length !== 0) {
|
|
127
|
-
if (typeof options.order === "string") {
|
|
128
|
-
order = options.order.split(",");
|
|
129
|
-
}
|
|
130
|
-
} else {
|
|
131
|
-
if (!options.columns || options.columns.length > fields.length) {
|
|
132
|
-
order = fields; // all fields emitted, columns in order of first record
|
|
133
|
-
} else {
|
|
134
|
-
// columns in order found in options
|
|
135
|
-
for (const column of options.columns) {
|
|
136
|
-
order.push(column.name);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
212
|
+
if (!Array.isArray(contents) || contents.length === 0) {
|
|
213
|
+
return this;
|
|
139
214
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
found = true;
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (!found) {
|
|
155
|
-
columns.push({ text: field, name: field });
|
|
156
|
-
}
|
|
157
|
-
}
|
|
215
|
+
var columns = tableFields(contents, options).map((field) => {
|
|
216
|
+
var column =
|
|
217
|
+
options.columns &&
|
|
218
|
+
options.columns.find((definition) => definition.name === field);
|
|
219
|
+
return column || { text: field, name: field };
|
|
220
|
+
});
|
|
221
|
+
if (columns.length === 0) {
|
|
222
|
+
return this;
|
|
158
223
|
}
|
|
159
224
|
this.layout("_table_", x, y, 0, 0, { columns: columns, reset: true });
|
|
160
225
|
|
|
161
|
-
|
|
226
|
+
var tableWidth = this._layouts["_table_"].reduce((width, column) => {
|
|
162
227
|
width += column.width;
|
|
163
228
|
return width;
|
|
164
229
|
}, 0);
|
|
165
230
|
|
|
166
|
-
let tableHeight = 0;
|
|
167
|
-
let headerHeight = 0;
|
|
168
|
-
let rowLines = [];
|
|
169
|
-
let currentY = y;
|
|
170
231
|
this._previousTextObjects = [];
|
|
171
|
-
|
|
172
|
-
|
|
232
|
+
var nth;
|
|
233
|
+
var rowOptions = {};
|
|
234
|
+
|
|
235
|
+
/** Resolves header styles identically for measurement and drawing. */
|
|
236
|
+
var headerOptions = (column) => {
|
|
237
|
+
var colOptions = clone(column.options.header);
|
|
238
|
+
if (typeof options.header === "object") {
|
|
239
|
+
colOptions = this._merge(colOptions, getCellOptions(options.header));
|
|
240
|
+
}
|
|
241
|
+
// Have header alignment match data alignment?
|
|
242
|
+
if (options.header.alignToData && column.options.textBox.textAlign) {
|
|
243
|
+
colOptions.textBox.textAlign = column.options.textBox.textAlign;
|
|
244
|
+
}
|
|
245
|
+
// Is there a specific header cell override in this column?
|
|
246
|
+
if (column.options.hcell) {
|
|
247
|
+
var cellOptions = getCellOptions(column.options, "hcell");
|
|
248
|
+
colOptions.textBox = this._merge(
|
|
249
|
+
colOptions.textBox,
|
|
250
|
+
clone(cellOptions.textBox),
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
return colOptions;
|
|
254
|
+
};
|
|
173
255
|
|
|
256
|
+
var headerHeight = 0;
|
|
174
257
|
if (options.header) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const cellHeight = getCellHeight(this, column.text, column, colOptions);
|
|
258
|
+
for (var column of this._layouts["_table_"]) {
|
|
259
|
+
var cellHeight = getCellHeight(
|
|
260
|
+
this,
|
|
261
|
+
column.text,
|
|
262
|
+
column,
|
|
263
|
+
headerOptions(column),
|
|
264
|
+
);
|
|
184
265
|
headerHeight = Math.max(headerHeight, cellHeight);
|
|
185
266
|
}
|
|
186
267
|
}
|
|
187
268
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
269
|
+
/** Recomputes bounds for each continuation position and page. */
|
|
270
|
+
var segmentBottom = (top) => {
|
|
271
|
+
var bottom = options.height ? top + options.height : 0;
|
|
272
|
+
var pageBottom =
|
|
192
273
|
this.pageInfo(this.pageNumber).height - this._margin.bottom;
|
|
193
|
-
if (
|
|
194
|
-
|
|
274
|
+
if (bottom === 0 || bottom > pageBottom) {
|
|
275
|
+
bottom = pageBottom;
|
|
195
276
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (options.border) {
|
|
199
|
-
options.border.lineCap = "butt"; // keep borders from extending outside of enclosing box.
|
|
200
|
-
}
|
|
277
|
+
return bottom;
|
|
278
|
+
};
|
|
201
279
|
|
|
202
280
|
if (options.row) {
|
|
203
281
|
rowOptions = getCellOptions(options.row);
|
|
204
282
|
|
|
205
283
|
switch (options.row.nth) {
|
|
206
|
-
case
|
|
284
|
+
case TableRowNth.EVEN:
|
|
207
285
|
nth = (row) => {
|
|
208
286
|
return row % 2 === 0;
|
|
209
287
|
};
|
|
210
288
|
break;
|
|
211
|
-
case
|
|
289
|
+
case TableRowNth.ODD:
|
|
212
290
|
nth = (row) => {
|
|
213
291
|
return row % 2 !== 0;
|
|
214
292
|
};
|
|
@@ -221,46 +299,78 @@ exports.table = function table(x, y, contents, options = {}) {
|
|
|
221
299
|
}
|
|
222
300
|
}
|
|
223
301
|
|
|
224
|
-
|
|
225
|
-
|
|
302
|
+
var tableBottom = options.overflow ? segmentBottom(y) : 0;
|
|
303
|
+
var tableHeight = 0;
|
|
304
|
+
var rowLines = [];
|
|
305
|
+
var currentY = y;
|
|
306
|
+
var firstTime = true;
|
|
307
|
+
var row = 0;
|
|
226
308
|
|
|
227
|
-
for (
|
|
228
|
-
let rowHeight = 0;
|
|
309
|
+
for (var record of contents) {
|
|
229
310
|
row++;
|
|
230
311
|
|
|
231
|
-
//
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
312
|
+
// Resolve every cell once: the renderer runs once per cell, and its
|
|
313
|
+
// options size the row as well as style the drawn text.
|
|
314
|
+
var cells = this._layouts["_table_"].map((column) => {
|
|
315
|
+
var field = column.field;
|
|
316
|
+
var value = Object.prototype.hasOwnProperty.call(record, field)
|
|
317
|
+
? record[field]
|
|
318
|
+
: undefined;
|
|
319
|
+
var text = value === undefined || value === null ? "" : value;
|
|
320
|
+
var colOptions = clone(options);
|
|
321
|
+
// The table callback is not a text-flow overflow callback.
|
|
322
|
+
delete colOptions.overflow;
|
|
323
|
+
colOptions = this._merge(colOptions, clone(column.options));
|
|
324
|
+
if (nth && nth(row)) {
|
|
325
|
+
colOptions = this._merge(colOptions, clone(rowOptions));
|
|
326
|
+
}
|
|
327
|
+
if (column.options.renderer) {
|
|
328
|
+
var renderOptions = column.options.renderer(text, record, field, row);
|
|
329
|
+
if (renderOptions) {
|
|
330
|
+
colOptions = this._merge(colOptions, renderOptions);
|
|
240
331
|
}
|
|
241
|
-
const cellHeight = getCellHeight(this, text, column, colOptions);
|
|
242
|
-
rowHeight = Math.max(rowHeight, cellHeight);
|
|
243
332
|
}
|
|
333
|
+
return { column, text: String(text), options: colOptions };
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
var rowHeight = 0;
|
|
337
|
+
for (var cell of cells) {
|
|
338
|
+
var cellHeight = getCellHeight(
|
|
339
|
+
this,
|
|
340
|
+
cell.text,
|
|
341
|
+
cell.column,
|
|
342
|
+
cell.options,
|
|
343
|
+
);
|
|
344
|
+
rowHeight = Math.max(rowHeight, cellHeight);
|
|
244
345
|
}
|
|
245
346
|
|
|
246
347
|
// When table gets 'full', let user know when overflow callback provided.
|
|
247
348
|
// They can choose to bail out of table filling loop, or keep on going with
|
|
248
349
|
// appropriate variables reset to initial values. This gives the user the
|
|
249
350
|
// opportunity to change to a new page to continue table production with
|
|
250
|
-
// remaining rows of data.
|
|
251
|
-
|
|
351
|
+
// remaining rows of data. A continuation reserves room for its repeated
|
|
352
|
+
// header as well as the row.
|
|
353
|
+
var needed = rowHeight + (firstTime && options.header ? headerHeight : 0);
|
|
354
|
+
if (options.overflow && currentY + needed > tableBottom) {
|
|
252
355
|
drawTableBorder(this, x, y, tableWidth, tableHeight, rowLines, options);
|
|
253
356
|
|
|
254
|
-
|
|
357
|
+
var orders = options.overflow.call(this, this, row);
|
|
255
358
|
|
|
256
359
|
if (orders === true) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
360
|
+
// stop processing table data
|
|
361
|
+
tableHeight = 0;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
if (!this.page) {
|
|
365
|
+
throw new Error(
|
|
366
|
+
"Recipe.table: the overflow callback must leave an active page to continue on.",
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
if (orders && orders.position) {
|
|
260
370
|
[x, y] = orders.position;
|
|
261
|
-
|
|
371
|
+
var xx = x;
|
|
262
372
|
// Make sure x position adjusted in all columns
|
|
263
|
-
for (
|
|
373
|
+
for (var column of this._layouts["_table_"]) {
|
|
264
374
|
column.x = xx;
|
|
265
375
|
xx += column.width;
|
|
266
376
|
}
|
|
@@ -270,32 +380,18 @@ exports.table = function table(x, y, contents, options = {}) {
|
|
|
270
380
|
currentY = y;
|
|
271
381
|
tableHeight = 0;
|
|
272
382
|
rowLines = [];
|
|
383
|
+
tableBottom = segmentBottom(y);
|
|
384
|
+
if (currentY + rowHeight + headerHeight > tableBottom) {
|
|
385
|
+
throw new RangeError(
|
|
386
|
+
`Recipe.table: row ${row} and its header do not fit in the continuation area.`,
|
|
387
|
+
);
|
|
388
|
+
}
|
|
273
389
|
}
|
|
274
390
|
|
|
275
391
|
if (firstTime && options.header) {
|
|
276
392
|
// Display table header
|
|
277
|
-
for (
|
|
278
|
-
|
|
279
|
-
if (typeof options.header === "object") {
|
|
280
|
-
const cellOptions = getCellOptions(options.header);
|
|
281
|
-
colOptions = this._merge(colOptions, clone(cellOptions));
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Have header alignment match data alignment?
|
|
285
|
-
if (options.header.alignToData && column.options.textBox.textAlign) {
|
|
286
|
-
colOptions.textBox.textAlign = column.options.textBox.textAlign;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Is there a specific header cell override in this column?
|
|
290
|
-
if (column.options.hcell) {
|
|
291
|
-
const cellOptions = getCellOptions(column.options, "hcell");
|
|
292
|
-
colOptions.textBox = this._merge(
|
|
293
|
-
colOptions.textBox,
|
|
294
|
-
clone(cellOptions.textBox),
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
colOptions = this._merge(colOptions, {
|
|
393
|
+
for (var column of this._layouts["_table_"]) {
|
|
394
|
+
var colOptions = this._merge(headerOptions(column), {
|
|
299
395
|
textBox: { minHeight: headerHeight, width: column.width },
|
|
300
396
|
});
|
|
301
397
|
this.text(column.text, column.x, currentY, colOptions);
|
|
@@ -309,27 +405,11 @@ exports.table = function table(x, y, contents, options = {}) {
|
|
|
309
405
|
firstTime = false;
|
|
310
406
|
|
|
311
407
|
// Now write out table cells for current record
|
|
312
|
-
for (
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (text === undefined) {
|
|
316
|
-
text = "";
|
|
317
|
-
}
|
|
318
|
-
let colOptions = clone(options);
|
|
319
|
-
colOptions = this._merge(colOptions, clone(column.options));
|
|
320
|
-
if (nth && nth(row)) {
|
|
321
|
-
colOptions = this._merge(colOptions, clone(rowOptions));
|
|
322
|
-
}
|
|
323
|
-
if (column.options.renderer) {
|
|
324
|
-
let renderOptions = column.options.renderer(text, record, field, row);
|
|
325
|
-
if (renderOptions) {
|
|
326
|
-
colOptions = this._merge(colOptions, renderOptions);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
colOptions = this._merge(colOptions, {
|
|
330
|
-
textBox: { minHeight: rowHeight, width: column.width },
|
|
408
|
+
for (var cell of cells) {
|
|
409
|
+
var colOptions = this._merge(cell.options, {
|
|
410
|
+
textBox: { minHeight: rowHeight, width: cell.column.width },
|
|
331
411
|
});
|
|
332
|
-
this.text(text, column.x, currentY, colOptions);
|
|
412
|
+
this.text(cell.text, cell.column.x, currentY, colOptions);
|
|
333
413
|
}
|
|
334
414
|
|
|
335
415
|
currentY += rowHeight;
|
|
@@ -339,5 +419,12 @@ exports.table = function table(x, y, contents, options = {}) {
|
|
|
339
419
|
|
|
340
420
|
drawTableBorder(this, x, y, tableWidth, tableHeight, rowLines, options);
|
|
341
421
|
|
|
422
|
+
// Leave the text cursor at the table's left edge, below its last segment.
|
|
423
|
+
this.x = x;
|
|
424
|
+
this.y = currentY;
|
|
425
|
+
this.box = { x, y: currentY };
|
|
426
|
+
this._flow = false;
|
|
427
|
+
this._previousTextObjects = [];
|
|
428
|
+
|
|
342
429
|
return this;
|
|
343
430
|
};
|