@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.
Files changed (43) hide show
  1. package/README.md +57 -7
  2. package/THIRD_PARTY_NOTICES.md +39 -0
  3. package/index.js +120 -1
  4. package/lib/PDFRStreamForBuffer.js +52 -7
  5. package/lib/PDFRStreamForFile.js +56 -7
  6. package/lib/PDFStreamForResponse.js +22 -5
  7. package/lib/PDFWStreamForBuffer.js +49 -10
  8. package/lib/PDFWStreamForFile.js +26 -5
  9. package/lib/Recipe.js +86 -30
  10. package/lib/content-stream.js +503 -0
  11. package/lib/css-colors.js +149 -0
  12. package/lib/font-text.js +1461 -0
  13. package/lib/glyph-list.js +1127 -0
  14. package/lib/recipe/annotation.js +482 -105
  15. package/lib/recipe/appendPage.js +8 -1
  16. package/lib/recipe/colors.js +132 -35
  17. package/lib/recipe/coordinate.js +82 -16
  18. package/lib/recipe/encrypt.js +32 -13
  19. package/lib/recipe/font.js +70 -25
  20. package/lib/recipe/graphics-state.js +3 -2
  21. package/lib/recipe/htmlToTextObjects.js +154 -21
  22. package/lib/recipe/image.js +34 -9
  23. package/lib/recipe/info.js +93 -22
  24. package/lib/recipe/insertPage.js +12 -3
  25. package/lib/recipe/overlay.js +4 -2
  26. package/lib/recipe/page-references.js +436 -0
  27. package/lib/recipe/page.js +379 -275
  28. package/lib/recipe/parameters.js +11 -2
  29. package/lib/recipe/replaceText.js +464 -30
  30. package/lib/recipe/shapes.js +418 -45
  31. package/lib/recipe/split.js +6 -2
  32. package/lib/recipe/table.js +260 -173
  33. package/lib/recipe/text.helper.js +176 -10
  34. package/lib/recipe/text.js +420 -115
  35. package/lib/recipe/utils.js +66 -3
  36. package/lib/recipe/vector-line.js +101 -39
  37. package/lib/recipe/vector-polygon.js +18 -6
  38. package/lib/recipe/vector.helper.js +150 -13
  39. package/lib/recipe/vector.js +150 -53
  40. package/lib/recipe/xObjectForm.js +73 -10
  41. package/lib/recipe-constants.js +373 -0
  42. package/muhammara.d.ts +3487 -152
  43. package/package.json +1 -1
@@ -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 source filename.
12
- * @returns {Recipe} The recipe instance.
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;
@@ -1,9 +1,17 @@
1
- function clone(object) {
2
- return JSON.parse(JSON.stringify(object));
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
- const cellOptions = clone(options);
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
- let colOptions = self._merge(options, { textBox: { width: column.width } });
18
- const originCoord = self._calibrateCoordinate(
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
- const pathOptions = self._getPathOptions(
43
+ var pathOptions = self._getPathOptions(
26
44
  colOptions,
27
45
  originCoord.nx,
28
46
  originCoord.ny,
29
47
  );
30
- const textObjects = self._makeTextObject(text, pathOptions.size, colOptions);
31
- const textBox = self._makeTextBox(colOptions);
32
- const { textHeight } = self._layoutText(textObjects, textBox, pathOptions);
33
-
34
- return textHeight;
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
- if (options.border) {
39
- let borderOptions = options.border === true ? {} : options.border;
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
- if (!borderOptions.width) {
42
- borderOptions = Object.assign({}, borderOptions, { width: 0.5 });
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
- self.rectangle(x, y, width, height, borderOptions);
46
- const columns = self._layouts["_table_"];
47
-
48
- // Draw verticles
49
- for (let index = 0; index < columns.length - 1; index++) {
50
- const column = columns[index];
51
- self.line(
52
- [
53
- [column.x + column.width, y],
54
- [column.x + column.width, y + height],
55
- ],
56
- borderOptions,
57
- );
58
- }
59
- // Draw horizontals
60
- for (let index = 0; index < rowLines.length - 1; index++) {
61
- const yPos = rowLines[index];
62
- self.line(
63
- [
64
- [x, yPos],
65
- [x + width, yPos],
66
- ],
67
- borderOptions,
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 {string} [options.row.nth] - 'even|odd', indicating that the properties should be applied only to
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
- let tableBottom = options.height ? y + options.height : 0;
122
- let fields = Object.keys(contents[0]);
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
- for (const field of order) {
142
- if (fields.includes(field)) {
143
- let found = false;
144
- if (options.columns) {
145
- for (const column of options.columns) {
146
- if (column.name && column.name === field) {
147
- columns.push(column);
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
- const tableWidth = this._layouts["_table_"].reduce((width, column) => {
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
- let nth;
172
- let rowOptions = {};
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
- // Wade through columns to determine minimum header height
176
- for (const column of this._layouts["_table_"]) {
177
- let colOptions = clone(column.options.header);
178
- if (typeof options.header === "object") {
179
- const cellOptions = getCellOptions(options.header);
180
- colOptions = this._merge(colOptions, cellOptions);
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
- if (options.overflow) {
189
- this._tableFullNotifier = options.overflow;
190
-
191
- const pageBottom =
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 (tableBottom === 0 || tableBottom > pageBottom) {
194
- tableBottom = pageBottom;
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 "even":
284
+ case TableRowNth.EVEN:
207
285
  nth = (row) => {
208
286
  return row % 2 === 0;
209
287
  };
210
288
  break;
211
- case "odd":
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
- let firstTime = true;
225
- let row = 0;
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 (const record of contents) {
228
- let rowHeight = 0;
309
+ for (var record of contents) {
229
310
  row++;
230
311
 
231
- // Wade through row data to determine row height
232
- for (const column of this._layouts["_table_"]) {
233
- const field = column.field;
234
- const text = record[field];
235
- if (text !== undefined) {
236
- let colOptions = clone(options);
237
- colOptions = this._merge(colOptions, clone(column.options));
238
- if (nth && nth(row)) {
239
- colOptions = this._merge(colOptions, clone(rowOptions));
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
- if (currentY + rowHeight > tableBottom && this._tableFullNotifier) {
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
- const orders = this._tableFullNotifier(this, row);
357
+ var orders = options.overflow.call(this, this, row);
255
358
 
256
359
  if (orders === true) {
257
- return this;
258
- } // stop processing table data
259
- if (orders.position) {
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
- let xx = x;
371
+ var xx = x;
262
372
  // Make sure x position adjusted in all columns
263
- for (const column of this._layouts["_table_"]) {
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 (const column of this._layouts["_table_"]) {
278
- let colOptions = clone(column.options.header);
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 (const column of this._layouts["_table_"]) {
313
- const field = column.field;
314
- let text = record[field];
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
  };