pdfkit 0.16.0 → 0.17.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/js/pdfkit.es.js CHANGED
@@ -8,24 +8,16 @@ import LineBreaker from 'linebreak';
8
8
  import exif from 'jpeg-exif';
9
9
  import PNG from 'png-js';
10
10
 
11
- /*
12
- PDFAbstractReference - abstract class for PDF reference
13
- */
14
-
15
11
  class PDFAbstractReference {
16
12
  toString() {
17
13
  throw new Error('Must be implemented by subclasses');
18
14
  }
19
15
  }
20
16
 
21
- /*
22
- PDFTree - abstract base class for name and number tree objects
23
- */
24
17
  class PDFTree {
25
18
  constructor() {
26
19
  let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
27
20
  this._items = {};
28
- // disable /Limits output for this tree
29
21
  this.limits = typeof options.limits === 'boolean' ? options.limits : true;
30
22
  }
31
23
  add(key, val) {
@@ -35,7 +27,6 @@ class PDFTree {
35
27
  return this._items[key];
36
28
  }
37
29
  toString() {
38
- // Needs to be sorted by key
39
30
  const sortedKeys = Object.keys(this._items).sort((a, b) => this._compareKeys(a, b));
40
31
  const out = ['<<'];
41
32
  if (this.limits && sortedKeys.length > 1) {
@@ -51,15 +42,13 @@ class PDFTree {
51
42
  out.push('>>');
52
43
  return out.join('\n');
53
44
  }
54
- _compareKeys(/*a, b*/
55
- ) {
45
+ _compareKeys() {
56
46
  throw new Error('Must be implemented by subclasses');
57
47
  }
58
48
  _keysName() {
59
49
  throw new Error('Must be implemented by subclasses');
60
50
  }
61
- _dataForKey(/*k*/
62
- ) {
51
+ _dataForKey() {
63
52
  throw new Error('Must be implemented by subclasses');
64
53
  }
65
54
  }
@@ -84,10 +73,6 @@ class SpotColor {
84
73
  }
85
74
  }
86
75
 
87
- /*
88
- PDFObject - converts JavaScript types into their corresponding PDF types.
89
- By Devon Govett
90
- */
91
76
  const pad = (str, length) => (Array(length + 1).join('0') + str).slice(-length);
92
77
  const escapableRe = /[\n\r\t\b\f()\\]/g;
93
78
  const escapable = {
@@ -100,8 +85,6 @@ const escapable = {
100
85
  '(': '\\(',
101
86
  ')': '\\)'
102
87
  };
103
-
104
- // Convert little endian UTF-16 to big endian
105
88
  const swapBytes = function (buff) {
106
89
  const l = buff.length;
107
90
  if (l & 0x01) {
@@ -118,14 +101,10 @@ const swapBytes = function (buff) {
118
101
  class PDFObject {
119
102
  static convert(object) {
120
103
  let encryptFn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
121
- // String literals are converted to the PDF name type
122
104
  if (typeof object === 'string') {
123
105
  return `/${object}`;
124
-
125
- // String objects are converted to PDF strings (UTF-16)
126
106
  } else if (object instanceof String) {
127
107
  let string = object;
128
- // Detect if this is a unicode string
129
108
  let isUnicode = false;
130
109
  for (let i = 0, end = string.length; i < end; i++) {
131
110
  if (string.charCodeAt(i) > 0x7f) {
@@ -133,39 +112,27 @@ class PDFObject {
133
112
  break;
134
113
  }
135
114
  }
136
-
137
- // If so, encode it as big endian UTF-16
138
115
  let stringBuffer;
139
116
  if (isUnicode) {
140
117
  stringBuffer = swapBytes(Buffer.from(`\ufeff${string}`, 'utf16le'));
141
118
  } else {
142
119
  stringBuffer = Buffer.from(string.valueOf(), 'ascii');
143
120
  }
144
-
145
- // Encrypt the string when necessary
146
121
  if (encryptFn) {
147
122
  string = encryptFn(stringBuffer).toString('binary');
148
123
  } else {
149
124
  string = stringBuffer.toString('binary');
150
125
  }
151
-
152
- // Escape characters as required by the spec
153
126
  string = string.replace(escapableRe, c => escapable[c]);
154
127
  return `(${string})`;
155
-
156
- // Buffers are converted to PDF hex strings
157
128
  } else if (Buffer.isBuffer(object)) {
158
129
  return `<${object.toString('hex')}>`;
159
130
  } else if (object instanceof PDFAbstractReference || object instanceof PDFTree || object instanceof SpotColor) {
160
131
  return object.toString();
161
132
  } else if (object instanceof Date) {
162
133
  let string = `D:${pad(object.getUTCFullYear(), 4)}` + pad(object.getUTCMonth() + 1, 2) + pad(object.getUTCDate(), 2) + pad(object.getUTCHours(), 2) + pad(object.getUTCMinutes(), 2) + pad(object.getUTCSeconds(), 2) + 'Z';
163
-
164
- // Encrypt the string when necessary
165
134
  if (encryptFn) {
166
135
  string = encryptFn(Buffer.from(string, 'ascii')).toString('binary');
167
-
168
- // Escape characters as required by the spec
169
136
  string = string.replace(escapableRe, c => escapable[c]);
170
137
  }
171
138
  return `(${string})`;
@@ -194,10 +161,6 @@ class PDFObject {
194
161
  }
195
162
  }
196
163
 
197
- /*
198
- PDFReference - represents a reference to another object in the PDF object heirarchy
199
- By Devon Govett
200
- */
201
164
  class PDFReference extends PDFAbstractReference {
202
165
  constructor(document, id) {
203
166
  let data = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
@@ -221,14 +184,14 @@ class PDFReference extends PDFAbstractReference {
221
184
  this.buffer.push(chunk);
222
185
  this.data.Length += chunk.length;
223
186
  if (this.compress) {
224
- return this.data.Filter = 'FlateDecode';
187
+ this.data.Filter = 'FlateDecode';
225
188
  }
226
189
  }
227
190
  end(chunk) {
228
191
  if (chunk) {
229
192
  this.write(chunk);
230
193
  }
231
- return this.finalize();
194
+ this.finalize();
232
195
  }
233
196
  finalize() {
234
197
  this.offset = this.document._offset;
@@ -248,7 +211,7 @@ class PDFReference extends PDFAbstractReference {
248
211
  if (this.buffer.length) {
249
212
  this.document._write('stream');
250
213
  this.document._write(this.buffer);
251
- this.buffer = []; // free up memory
214
+ this.buffer = [];
252
215
  this.document._write('\nendstream');
253
216
  }
254
217
  this.document._write('endobj');
@@ -259,10 +222,81 @@ class PDFReference extends PDFAbstractReference {
259
222
  }
260
223
  }
261
224
 
262
- /*
263
- PDFPage - represents a single page in the PDF document
264
- By Devon Govett
265
- */
225
+ const fArray = new Float32Array(1);
226
+ const uArray = new Uint32Array(fArray.buffer);
227
+ function PDFNumber(n) {
228
+ const rounded = Math.fround(n);
229
+ if (rounded <= n) return rounded;
230
+ fArray[0] = n;
231
+ if (n <= 0) {
232
+ uArray[0] += 1;
233
+ } else {
234
+ uArray[0] -= 1;
235
+ }
236
+ return fArray[0];
237
+ }
238
+ function normalizeSides(sides) {
239
+ let defaultDefinition = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
240
+ let transformer = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : v => v;
241
+ if (sides == null || typeof sides === 'object' && Object.keys(sides).length === 0) {
242
+ sides = defaultDefinition;
243
+ }
244
+ if (sides == null || typeof sides !== 'object') {
245
+ sides = {
246
+ top: sides,
247
+ right: sides,
248
+ bottom: sides,
249
+ left: sides
250
+ };
251
+ } else if (Array.isArray(sides)) {
252
+ if (sides.length === 2) {
253
+ sides = {
254
+ vertical: sides[0],
255
+ horizontal: sides[1]
256
+ };
257
+ } else {
258
+ sides = {
259
+ top: sides[0],
260
+ right: sides[1],
261
+ bottom: sides[2],
262
+ left: sides[3]
263
+ };
264
+ }
265
+ }
266
+ if ('vertical' in sides || 'horizontal' in sides) {
267
+ sides = {
268
+ top: sides.vertical,
269
+ right: sides.horizontal,
270
+ bottom: sides.vertical,
271
+ left: sides.horizontal
272
+ };
273
+ }
274
+ return {
275
+ top: transformer(sides.top),
276
+ right: transformer(sides.right),
277
+ bottom: transformer(sides.bottom),
278
+ left: transformer(sides.left)
279
+ };
280
+ }
281
+ const MM_TO_CM = 1 / 10;
282
+ const CM_TO_IN = 1 / 2.54;
283
+ const PX_TO_IN = 1 / 96;
284
+ const IN_TO_PT = 72;
285
+ const PC_TO_PT = 12;
286
+ function cosine(a) {
287
+ if (a === 0) return 1;
288
+ if (a === 90) return 0;
289
+ if (a === 180) return -1;
290
+ if (a === 270) return 0;
291
+ return Math.cos(a * Math.PI / 180);
292
+ }
293
+ function sine(a) {
294
+ if (a === 0) return 0;
295
+ if (a === 90) return 1;
296
+ if (a === 180) return 0;
297
+ if (a === 270) return -1;
298
+ return Math.sin(a * Math.PI / 180);
299
+ }
266
300
 
267
301
  const DEFAULT_MARGINS = {
268
302
  top: 72,
@@ -326,35 +360,19 @@ class PDFPage {
326
360
  constructor(document) {
327
361
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
328
362
  this.document = document;
363
+ this._options = options;
329
364
  this.size = options.size || 'letter';
330
365
  this.layout = options.layout || 'portrait';
331
-
332
- // process margins
333
- if (typeof options.margin === 'number') {
334
- this.margins = {
335
- top: options.margin,
336
- left: options.margin,
337
- bottom: options.margin,
338
- right: options.margin
339
- };
340
-
341
- // default to 1 inch margins
342
- } else {
343
- this.margins = options.margins || DEFAULT_MARGINS;
344
- }
345
-
346
- // calculate page dimensions
347
366
  const dimensions = Array.isArray(this.size) ? this.size : SIZES[this.size.toUpperCase()];
348
367
  this.width = dimensions[this.layout === 'portrait' ? 0 : 1];
349
368
  this.height = dimensions[this.layout === 'portrait' ? 1 : 0];
350
369
  this.content = this.document.ref();
351
-
352
- // Initialize the Font, XObject, and ExtGState dictionaries
370
+ if (options.font) document.font(options.font, options.fontFamily);
371
+ if (options.fontSize) document.fontSize(options.fontSize);
372
+ this.margins = normalizeSides(options.margin ?? options.margins, DEFAULT_MARGINS, x => document.sizeToPoint(x, 0, this));
353
373
  this.resources = this.document.ref({
354
374
  ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI']
355
375
  });
356
-
357
- // The page dictionary
358
376
  this.dictionary = this.document.ref({
359
377
  Type: 'Page',
360
378
  Parent: this.document._root.data.Pages,
@@ -364,8 +382,6 @@ class PDFPage {
364
382
  });
365
383
  this.markings = [];
366
384
  }
367
-
368
- // Lazily create these objects
369
385
  get fonts() {
370
386
  const data = this.resources.data;
371
387
  return data.Font != null ? data.Font : data.Font = {};
@@ -394,14 +410,18 @@ class PDFPage {
394
410
  const data = this.dictionary.data;
395
411
  return data.StructParents != null ? data.StructParents : data.StructParents = this.document.createStructParentTreeNextKey();
396
412
  }
413
+ get contentWidth() {
414
+ return this.width - this.margins.left - this.margins.right;
415
+ }
416
+ get contentHeight() {
417
+ return this.height - this.margins.top - this.margins.bottom;
418
+ }
397
419
  maxY() {
398
420
  return this.height - this.margins.bottom;
399
421
  }
400
422
  write(chunk) {
401
423
  return this.content.write(chunk);
402
424
  }
403
-
404
- // Set tab order if document is tagged for accessibility.
405
425
  _setTabOrder() {
406
426
  if (!this.dictionary.Tabs && this.document.hasMarkInfoDictionary()) {
407
427
  this.dictionary.data.Tabs = 'S';
@@ -419,191 +439,57 @@ class PDFPage {
419
439
  }
420
440
  }
421
441
 
422
- /*
423
- PDFNameTree - represents a name tree object
424
- */
425
442
  class PDFNameTree extends PDFTree {
426
443
  _compareKeys(a, b) {
427
444
  return a.localeCompare(b);
428
445
  }
429
446
  _keysName() {
430
- return "Names";
447
+ return 'Names';
431
448
  }
432
449
  _dataForKey(k) {
433
450
  return new String(k);
434
451
  }
435
452
  }
436
453
 
437
- /**
438
- * Check if value is in a range group.
439
- * @param {number} value
440
- * @param {number[]} rangeGroup
441
- * @returns {boolean}
442
- */
443
454
  function inRange(value, rangeGroup) {
444
455
  if (value < rangeGroup[0]) return false;
445
456
  let startRange = 0;
446
457
  let endRange = rangeGroup.length / 2;
447
458
  while (startRange <= endRange) {
448
459
  const middleRange = Math.floor((startRange + endRange) / 2);
449
-
450
- // actual array index
451
460
  const arrayIndex = middleRange * 2;
452
-
453
- // Check if value is in range pointed by actual index
454
461
  if (value >= rangeGroup[arrayIndex] && value <= rangeGroup[arrayIndex + 1]) {
455
462
  return true;
456
463
  }
457
464
  if (value > rangeGroup[arrayIndex + 1]) {
458
- // Search Right Side Of Array
459
465
  startRange = middleRange + 1;
460
466
  } else {
461
- // Search Left Side Of Array
462
467
  endRange = middleRange - 1;
463
468
  }
464
469
  }
465
470
  return false;
466
471
  }
467
472
 
468
- // prettier-ignore-start
469
- /**
470
- * A.1 Unassigned code points in Unicode 3.2
471
- * @link https://tools.ietf.org/html/rfc3454#appendix-A.1
472
- */
473
473
  const unassigned_code_points = [0x0221, 0x0221, 0x0234, 0x024f, 0x02ae, 0x02af, 0x02ef, 0x02ff, 0x0350, 0x035f, 0x0370, 0x0373, 0x0376, 0x0379, 0x037b, 0x037d, 0x037f, 0x0383, 0x038b, 0x038b, 0x038d, 0x038d, 0x03a2, 0x03a2, 0x03cf, 0x03cf, 0x03f7, 0x03ff, 0x0487, 0x0487, 0x04cf, 0x04cf, 0x04f6, 0x04f7, 0x04fa, 0x04ff, 0x0510, 0x0530, 0x0557, 0x0558, 0x0560, 0x0560, 0x0588, 0x0588, 0x058b, 0x0590, 0x05a2, 0x05a2, 0x05ba, 0x05ba, 0x05c5, 0x05cf, 0x05eb, 0x05ef, 0x05f5, 0x060b, 0x060d, 0x061a, 0x061c, 0x061e, 0x0620, 0x0620, 0x063b, 0x063f, 0x0656, 0x065f, 0x06ee, 0x06ef, 0x06ff, 0x06ff, 0x070e, 0x070e, 0x072d, 0x072f, 0x074b, 0x077f, 0x07b2, 0x0900, 0x0904, 0x0904, 0x093a, 0x093b, 0x094e, 0x094f, 0x0955, 0x0957, 0x0971, 0x0980, 0x0984, 0x0984, 0x098d, 0x098e, 0x0991, 0x0992, 0x09a9, 0x09a9, 0x09b1, 0x09b1, 0x09b3, 0x09b5, 0x09ba, 0x09bb, 0x09bd, 0x09bd, 0x09c5, 0x09c6, 0x09c9, 0x09ca, 0x09ce, 0x09d6, 0x09d8, 0x09db, 0x09de, 0x09de, 0x09e4, 0x09e5, 0x09fb, 0x0a01, 0x0a03, 0x0a04, 0x0a0b, 0x0a0e, 0x0a11, 0x0a12, 0x0a29, 0x0a29, 0x0a31, 0x0a31, 0x0a34, 0x0a34, 0x0a37, 0x0a37, 0x0a3a, 0x0a3b, 0x0a3d, 0x0a3d, 0x0a43, 0x0a46, 0x0a49, 0x0a4a, 0x0a4e, 0x0a58, 0x0a5d, 0x0a5d, 0x0a5f, 0x0a65, 0x0a75, 0x0a80, 0x0a84, 0x0a84, 0x0a8c, 0x0a8c, 0x0a8e, 0x0a8e, 0x0a92, 0x0a92, 0x0aa9, 0x0aa9, 0x0ab1, 0x0ab1, 0x0ab4, 0x0ab4, 0x0aba, 0x0abb, 0x0ac6, 0x0ac6, 0x0aca, 0x0aca, 0x0ace, 0x0acf, 0x0ad1, 0x0adf, 0x0ae1, 0x0ae5, 0x0af0, 0x0b00, 0x0b04, 0x0b04, 0x0b0d, 0x0b0e, 0x0b11, 0x0b12, 0x0b29, 0x0b29, 0x0b31, 0x0b31, 0x0b34, 0x0b35, 0x0b3a, 0x0b3b, 0x0b44, 0x0b46, 0x0b49, 0x0b4a, 0x0b4e, 0x0b55, 0x0b58, 0x0b5b, 0x0b5e, 0x0b5e, 0x0b62, 0x0b65, 0x0b71, 0x0b81, 0x0b84, 0x0b84, 0x0b8b, 0x0b8d, 0x0b91, 0x0b91, 0x0b96, 0x0b98, 0x0b9b, 0x0b9b, 0x0b9d, 0x0b9d, 0x0ba0, 0x0ba2, 0x0ba5, 0x0ba7, 0x0bab, 0x0bad, 0x0bb6, 0x0bb6, 0x0bba, 0x0bbd, 0x0bc3, 0x0bc5, 0x0bc9, 0x0bc9, 0x0bce, 0x0bd6, 0x0bd8, 0x0be6, 0x0bf3, 0x0c00, 0x0c04, 0x0c04, 0x0c0d, 0x0c0d, 0x0c11, 0x0c11, 0x0c29, 0x0c29, 0x0c34, 0x0c34, 0x0c3a, 0x0c3d, 0x0c45, 0x0c45, 0x0c49, 0x0c49, 0x0c4e, 0x0c54, 0x0c57, 0x0c5f, 0x0c62, 0x0c65, 0x0c70, 0x0c81, 0x0c84, 0x0c84, 0x0c8d, 0x0c8d, 0x0c91, 0x0c91, 0x0ca9, 0x0ca9, 0x0cb4, 0x0cb4, 0x0cba, 0x0cbd, 0x0cc5, 0x0cc5, 0x0cc9, 0x0cc9, 0x0cce, 0x0cd4, 0x0cd7, 0x0cdd, 0x0cdf, 0x0cdf, 0x0ce2, 0x0ce5, 0x0cf0, 0x0d01, 0x0d04, 0x0d04, 0x0d0d, 0x0d0d, 0x0d11, 0x0d11, 0x0d29, 0x0d29, 0x0d3a, 0x0d3d, 0x0d44, 0x0d45, 0x0d49, 0x0d49, 0x0d4e, 0x0d56, 0x0d58, 0x0d5f, 0x0d62, 0x0d65, 0x0d70, 0x0d81, 0x0d84, 0x0d84, 0x0d97, 0x0d99, 0x0db2, 0x0db2, 0x0dbc, 0x0dbc, 0x0dbe, 0x0dbf, 0x0dc7, 0x0dc9, 0x0dcb, 0x0dce, 0x0dd5, 0x0dd5, 0x0dd7, 0x0dd7, 0x0de0, 0x0df1, 0x0df5, 0x0e00, 0x0e3b, 0x0e3e, 0x0e5c, 0x0e80, 0x0e83, 0x0e83, 0x0e85, 0x0e86, 0x0e89, 0x0e89, 0x0e8b, 0x0e8c, 0x0e8e, 0x0e93, 0x0e98, 0x0e98, 0x0ea0, 0x0ea0, 0x0ea4, 0x0ea4, 0x0ea6, 0x0ea6, 0x0ea8, 0x0ea9, 0x0eac, 0x0eac, 0x0eba, 0x0eba, 0x0ebe, 0x0ebf, 0x0ec5, 0x0ec5, 0x0ec7, 0x0ec7, 0x0ece, 0x0ecf, 0x0eda, 0x0edb, 0x0ede, 0x0eff, 0x0f48, 0x0f48, 0x0f6b, 0x0f70, 0x0f8c, 0x0f8f, 0x0f98, 0x0f98, 0x0fbd, 0x0fbd, 0x0fcd, 0x0fce, 0x0fd0, 0x0fff, 0x1022, 0x1022, 0x1028, 0x1028, 0x102b, 0x102b, 0x1033, 0x1035, 0x103a, 0x103f, 0x105a, 0x109f, 0x10c6, 0x10cf, 0x10f9, 0x10fa, 0x10fc, 0x10ff, 0x115a, 0x115e, 0x11a3, 0x11a7, 0x11fa, 0x11ff, 0x1207, 0x1207, 0x1247, 0x1247, 0x1249, 0x1249, 0x124e, 0x124f, 0x1257, 0x1257, 0x1259, 0x1259, 0x125e, 0x125f, 0x1287, 0x1287, 0x1289, 0x1289, 0x128e, 0x128f, 0x12af, 0x12af, 0x12b1, 0x12b1, 0x12b6, 0x12b7, 0x12bf, 0x12bf, 0x12c1, 0x12c1, 0x12c6, 0x12c7, 0x12cf, 0x12cf, 0x12d7, 0x12d7, 0x12ef, 0x12ef, 0x130f, 0x130f, 0x1311, 0x1311, 0x1316, 0x1317, 0x131f, 0x131f, 0x1347, 0x1347, 0x135b, 0x1360, 0x137d, 0x139f, 0x13f5, 0x1400, 0x1677, 0x167f, 0x169d, 0x169f, 0x16f1, 0x16ff, 0x170d, 0x170d, 0x1715, 0x171f, 0x1737, 0x173f, 0x1754, 0x175f, 0x176d, 0x176d, 0x1771, 0x1771, 0x1774, 0x177f, 0x17dd, 0x17df, 0x17ea, 0x17ff, 0x180f, 0x180f, 0x181a, 0x181f, 0x1878, 0x187f, 0x18aa, 0x1dff, 0x1e9c, 0x1e9f, 0x1efa, 0x1eff, 0x1f16, 0x1f17, 0x1f1e, 0x1f1f, 0x1f46, 0x1f47, 0x1f4e, 0x1f4f, 0x1f58, 0x1f58, 0x1f5a, 0x1f5a, 0x1f5c, 0x1f5c, 0x1f5e, 0x1f5e, 0x1f7e, 0x1f7f, 0x1fb5, 0x1fb5, 0x1fc5, 0x1fc5, 0x1fd4, 0x1fd5, 0x1fdc, 0x1fdc, 0x1ff0, 0x1ff1, 0x1ff5, 0x1ff5, 0x1fff, 0x1fff, 0x2053, 0x2056, 0x2058, 0x205e, 0x2064, 0x2069, 0x2072, 0x2073, 0x208f, 0x209f, 0x20b2, 0x20cf, 0x20eb, 0x20ff, 0x213b, 0x213c, 0x214c, 0x2152, 0x2184, 0x218f, 0x23cf, 0x23ff, 0x2427, 0x243f, 0x244b, 0x245f, 0x24ff, 0x24ff, 0x2614, 0x2615, 0x2618, 0x2618, 0x267e, 0x267f, 0x268a, 0x2700, 0x2705, 0x2705, 0x270a, 0x270b, 0x2728, 0x2728, 0x274c, 0x274c, 0x274e, 0x274e, 0x2753, 0x2755, 0x2757, 0x2757, 0x275f, 0x2760, 0x2795, 0x2797, 0x27b0, 0x27b0, 0x27bf, 0x27cf, 0x27ec, 0x27ef, 0x2b00, 0x2e7f, 0x2e9a, 0x2e9a, 0x2ef4, 0x2eff, 0x2fd6, 0x2fef, 0x2ffc, 0x2fff, 0x3040, 0x3040, 0x3097, 0x3098, 0x3100, 0x3104, 0x312d, 0x3130, 0x318f, 0x318f, 0x31b8, 0x31ef, 0x321d, 0x321f, 0x3244, 0x3250, 0x327c, 0x327e, 0x32cc, 0x32cf, 0x32ff, 0x32ff, 0x3377, 0x337a, 0x33de, 0x33df, 0x33ff, 0x33ff, 0x4db6, 0x4dff, 0x9fa6, 0x9fff, 0xa48d, 0xa48f, 0xa4c7, 0xabff, 0xd7a4, 0xd7ff, 0xfa2e, 0xfa2f, 0xfa6b, 0xfaff, 0xfb07, 0xfb12, 0xfb18, 0xfb1c, 0xfb37, 0xfb37, 0xfb3d, 0xfb3d, 0xfb3f, 0xfb3f, 0xfb42, 0xfb42, 0xfb45, 0xfb45, 0xfbb2, 0xfbd2, 0xfd40, 0xfd4f, 0xfd90, 0xfd91, 0xfdc8, 0xfdcf, 0xfdfd, 0xfdff, 0xfe10, 0xfe1f, 0xfe24, 0xfe2f, 0xfe47, 0xfe48, 0xfe53, 0xfe53, 0xfe67, 0xfe67, 0xfe6c, 0xfe6f, 0xfe75, 0xfe75, 0xfefd, 0xfefe, 0xff00, 0xff00, 0xffbf, 0xffc1, 0xffc8, 0xffc9, 0xffd0, 0xffd1, 0xffd8, 0xffd9, 0xffdd, 0xffdf, 0xffe7, 0xffe7, 0xffef, 0xfff8, 0x10000, 0x102ff, 0x1031f, 0x1031f, 0x10324, 0x1032f, 0x1034b, 0x103ff, 0x10426, 0x10427, 0x1044e, 0x1cfff, 0x1d0f6, 0x1d0ff, 0x1d127, 0x1d129, 0x1d1de, 0x1d3ff, 0x1d455, 0x1d455, 0x1d49d, 0x1d49d, 0x1d4a0, 0x1d4a1, 0x1d4a3, 0x1d4a4, 0x1d4a7, 0x1d4a8, 0x1d4ad, 0x1d4ad, 0x1d4ba, 0x1d4ba, 0x1d4bc, 0x1d4bc, 0x1d4c1, 0x1d4c1, 0x1d4c4, 0x1d4c4, 0x1d506, 0x1d506, 0x1d50b, 0x1d50c, 0x1d515, 0x1d515, 0x1d51d, 0x1d51d, 0x1d53a, 0x1d53a, 0x1d53f, 0x1d53f, 0x1d545, 0x1d545, 0x1d547, 0x1d549, 0x1d551, 0x1d551, 0x1d6a4, 0x1d6a7, 0x1d7ca, 0x1d7cd, 0x1d800, 0x1fffd, 0x2a6d7, 0x2f7ff, 0x2fa1e, 0x2fffd, 0x30000, 0x3fffd, 0x40000, 0x4fffd, 0x50000, 0x5fffd, 0x60000, 0x6fffd, 0x70000, 0x7fffd, 0x80000, 0x8fffd, 0x90000, 0x9fffd, 0xa0000, 0xafffd, 0xb0000, 0xbfffd, 0xc0000, 0xcfffd, 0xd0000, 0xdfffd, 0xe0000, 0xe0000, 0xe0002, 0xe001f, 0xe0080, 0xefffd];
474
- // prettier-ignore-end
475
-
476
474
  const isUnassignedCodePoint = character => inRange(character, unassigned_code_points);
477
-
478
- // prettier-ignore-start
479
- /**
480
- * B.1 Commonly mapped to nothing
481
- * @link https://tools.ietf.org/html/rfc3454#appendix-B.1
482
- */
483
475
  const commonly_mapped_to_nothing = [0x00ad, 0x00ad, 0x034f, 0x034f, 0x1806, 0x1806, 0x180b, 0x180b, 0x180c, 0x180c, 0x180d, 0x180d, 0x200b, 0x200b, 0x200c, 0x200c, 0x200d, 0x200d, 0x2060, 0x2060, 0xfe00, 0xfe00, 0xfe01, 0xfe01, 0xfe02, 0xfe02, 0xfe03, 0xfe03, 0xfe04, 0xfe04, 0xfe05, 0xfe05, 0xfe06, 0xfe06, 0xfe07, 0xfe07, 0xfe08, 0xfe08, 0xfe09, 0xfe09, 0xfe0a, 0xfe0a, 0xfe0b, 0xfe0b, 0xfe0c, 0xfe0c, 0xfe0d, 0xfe0d, 0xfe0e, 0xfe0e, 0xfe0f, 0xfe0f, 0xfeff, 0xfeff];
484
- // prettier-ignore-end
485
-
486
476
  const isCommonlyMappedToNothing = character => inRange(character, commonly_mapped_to_nothing);
487
-
488
- // prettier-ignore-start
489
- /**
490
- * C.1.2 Non-ASCII space characters
491
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.1.2
492
- */
493
- const non_ASCII_space_characters = [0x00a0, 0x00a0 /* NO-BREAK SPACE */, 0x1680, 0x1680 /* OGHAM SPACE MARK */, 0x2000, 0x2000 /* EN QUAD */, 0x2001, 0x2001 /* EM QUAD */, 0x2002, 0x2002 /* EN SPACE */, 0x2003, 0x2003 /* EM SPACE */, 0x2004, 0x2004 /* THREE-PER-EM SPACE */, 0x2005, 0x2005 /* FOUR-PER-EM SPACE */, 0x2006, 0x2006 /* SIX-PER-EM SPACE */, 0x2007, 0x2007 /* FIGURE SPACE */, 0x2008, 0x2008 /* PUNCTUATION SPACE */, 0x2009, 0x2009 /* THIN SPACE */, 0x200a, 0x200a /* HAIR SPACE */, 0x200b, 0x200b /* ZERO WIDTH SPACE */, 0x202f, 0x202f /* NARROW NO-BREAK SPACE */, 0x205f, 0x205f /* MEDIUM MATHEMATICAL SPACE */, 0x3000, 0x3000 /* IDEOGRAPHIC SPACE */];
494
- // prettier-ignore-end
495
-
477
+ const non_ASCII_space_characters = [0x00a0, 0x00a0, 0x1680, 0x1680, 0x2000, 0x2000, 0x2001, 0x2001, 0x2002, 0x2002, 0x2003, 0x2003, 0x2004, 0x2004, 0x2005, 0x2005, 0x2006, 0x2006, 0x2007, 0x2007, 0x2008, 0x2008, 0x2009, 0x2009, 0x200a, 0x200a, 0x200b, 0x200b, 0x202f, 0x202f, 0x205f, 0x205f, 0x3000, 0x3000];
496
478
  const isNonASCIISpaceCharacter = character => inRange(character, non_ASCII_space_characters);
497
-
498
- // prettier-ignore-start
499
- const non_ASCII_controls_characters = [
500
- /**
501
- * C.2.2 Non-ASCII control characters
502
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.2.2
503
- */
504
- 0x0080, 0x009f /* [CONTROL CHARACTERS] */, 0x06dd, 0x06dd /* ARABIC END OF AYAH */, 0x070f, 0x070f /* SYRIAC ABBREVIATION MARK */, 0x180e, 0x180e /* MONGOLIAN VOWEL SEPARATOR */, 0x200c, 0x200c /* ZERO WIDTH NON-JOINER */, 0x200d, 0x200d /* ZERO WIDTH JOINER */, 0x2028, 0x2028 /* LINE SEPARATOR */, 0x2029, 0x2029 /* PARAGRAPH SEPARATOR */, 0x2060, 0x2060 /* WORD JOINER */, 0x2061, 0x2061 /* FUNCTION APPLICATION */, 0x2062, 0x2062 /* INVISIBLE TIMES */, 0x2063, 0x2063 /* INVISIBLE SEPARATOR */, 0x206a, 0x206f /* [CONTROL CHARACTERS] */, 0xfeff, 0xfeff /* ZERO WIDTH NO-BREAK SPACE */, 0xfff9, 0xfffc /* [CONTROL CHARACTERS] */, 0x1d173, 0x1d17a /* [MUSICAL CONTROL CHARACTERS] */];
505
- const non_character_codepoints = [
506
- /**
507
- * C.4 Non-character code points
508
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.4
509
- */
510
- 0xfdd0, 0xfdef /* [NONCHARACTER CODE POINTS] */, 0xfffe, 0xffff /* [NONCHARACTER CODE POINTS] */, 0x1fffe, 0x1ffff /* [NONCHARACTER CODE POINTS] */, 0x2fffe, 0x2ffff /* [NONCHARACTER CODE POINTS] */, 0x3fffe, 0x3ffff /* [NONCHARACTER CODE POINTS] */, 0x4fffe, 0x4ffff /* [NONCHARACTER CODE POINTS] */, 0x5fffe, 0x5ffff /* [NONCHARACTER CODE POINTS] */, 0x6fffe, 0x6ffff /* [NONCHARACTER CODE POINTS] */, 0x7fffe, 0x7ffff /* [NONCHARACTER CODE POINTS] */, 0x8fffe, 0x8ffff /* [NONCHARACTER CODE POINTS] */, 0x9fffe, 0x9ffff /* [NONCHARACTER CODE POINTS] */, 0xafffe, 0xaffff /* [NONCHARACTER CODE POINTS] */, 0xbfffe, 0xbffff /* [NONCHARACTER CODE POINTS] */, 0xcfffe, 0xcffff /* [NONCHARACTER CODE POINTS] */, 0xdfffe, 0xdffff /* [NONCHARACTER CODE POINTS] */, 0xefffe, 0xeffff /* [NONCHARACTER CODE POINTS] */, 0x10fffe, 0x10ffff /* [NONCHARACTER CODE POINTS] */];
511
-
512
- /**
513
- * 2.3. Prohibited Output
514
- */
515
- const prohibited_characters = [
516
- /**
517
- * C.2.1 ASCII control characters
518
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.2.1
519
- */
520
- 0, 0x001f /* [CONTROL CHARACTERS] */, 0x007f, 0x007f /* DELETE */,
521
- /**
522
- * C.8 Change display properties or are deprecated
523
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.8
524
- */
525
- 0x0340, 0x0340 /* COMBINING GRAVE TONE MARK */, 0x0341, 0x0341 /* COMBINING ACUTE TONE MARK */, 0x200e, 0x200e /* LEFT-TO-RIGHT MARK */, 0x200f, 0x200f /* RIGHT-TO-LEFT MARK */, 0x202a, 0x202a /* LEFT-TO-RIGHT EMBEDDING */, 0x202b, 0x202b /* RIGHT-TO-LEFT EMBEDDING */, 0x202c, 0x202c /* POP DIRECTIONAL FORMATTING */, 0x202d, 0x202d /* LEFT-TO-RIGHT OVERRIDE */, 0x202e, 0x202e /* RIGHT-TO-LEFT OVERRIDE */, 0x206a, 0x206a /* INHIBIT SYMMETRIC SWAPPING */, 0x206b, 0x206b /* ACTIVATE SYMMETRIC SWAPPING */, 0x206c, 0x206c /* INHIBIT ARABIC FORM SHAPING */, 0x206d, 0x206d /* ACTIVATE ARABIC FORM SHAPING */, 0x206e, 0x206e /* NATIONAL DIGIT SHAPES */, 0x206f, 0x206f /* NOMINAL DIGIT SHAPES */,
526
- /**
527
- * C.7 Inappropriate for canonical representation
528
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.7
529
- */
530
- 0x2ff0, 0x2ffb /* [IDEOGRAPHIC DESCRIPTION CHARACTERS] */,
531
- /**
532
- * C.5 Surrogate codes
533
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.5
534
- */
535
- 0xd800, 0xdfff,
536
- /**
537
- * C.3 Private use
538
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.3
539
- */
540
- 0xe000, 0xf8ff /* [PRIVATE USE, PLANE 0] */,
541
- /**
542
- * C.6 Inappropriate for plain text
543
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.6
544
- */
545
- 0xfff9, 0xfff9 /* INTERLINEAR ANNOTATION ANCHOR */, 0xfffa, 0xfffa /* INTERLINEAR ANNOTATION SEPARATOR */, 0xfffb, 0xfffb /* INTERLINEAR ANNOTATION TERMINATOR */, 0xfffc, 0xfffc /* OBJECT REPLACEMENT CHARACTER */, 0xfffd, 0xfffd /* REPLACEMENT CHARACTER */,
546
- /**
547
- * C.9 Tagging characters
548
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.9
549
- */
550
- 0xe0001, 0xe0001 /* LANGUAGE TAG */, 0xe0020, 0xe007f /* [TAGGING CHARACTERS] */,
551
- /**
552
- * C.3 Private use
553
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.3
554
- */
555
-
556
- 0xf0000, 0xffffd /* [PRIVATE USE, PLANE 15] */, 0x100000, 0x10fffd /* [PRIVATE USE, PLANE 16] */];
557
- // prettier-ignore-end
558
-
479
+ const non_ASCII_controls_characters = [0x0080, 0x009f, 0x06dd, 0x06dd, 0x070f, 0x070f, 0x180e, 0x180e, 0x200c, 0x200c, 0x200d, 0x200d, 0x2028, 0x2028, 0x2029, 0x2029, 0x2060, 0x2060, 0x2061, 0x2061, 0x2062, 0x2062, 0x2063, 0x2063, 0x206a, 0x206f, 0xfeff, 0xfeff, 0xfff9, 0xfffc, 0x1d173, 0x1d17a];
480
+ const non_character_codepoints = [0xfdd0, 0xfdef, 0xfffe, 0xffff, 0x1fffe, 0x1ffff, 0x2fffe, 0x2ffff, 0x3fffe, 0x3ffff, 0x4fffe, 0x4ffff, 0x5fffe, 0x5ffff, 0x6fffe, 0x6ffff, 0x7fffe, 0x7ffff, 0x8fffe, 0x8ffff, 0x9fffe, 0x9ffff, 0xafffe, 0xaffff, 0xbfffe, 0xbffff, 0xcfffe, 0xcffff, 0xdfffe, 0xdffff, 0xefffe, 0xeffff, 0x10fffe, 0x10ffff];
481
+ const prohibited_characters = [0, 0x001f, 0x007f, 0x007f, 0x0340, 0x0340, 0x0341, 0x0341, 0x200e, 0x200e, 0x200f, 0x200f, 0x202a, 0x202a, 0x202b, 0x202b, 0x202c, 0x202c, 0x202d, 0x202d, 0x202e, 0x202e, 0x206a, 0x206a, 0x206b, 0x206b, 0x206c, 0x206c, 0x206d, 0x206d, 0x206e, 0x206e, 0x206f, 0x206f, 0x2ff0, 0x2ffb, 0xd800, 0xdfff, 0xe000, 0xf8ff, 0xfff9, 0xfff9, 0xfffa, 0xfffa, 0xfffb, 0xfffb, 0xfffc, 0xfffc, 0xfffd, 0xfffd, 0xe0001, 0xe0001, 0xe0020, 0xe007f, 0xf0000, 0xffffd, 0x100000, 0x10fffd];
559
482
  const isProhibitedCharacter = character => inRange(character, non_ASCII_space_characters) || inRange(character, prohibited_characters) || inRange(character, non_ASCII_controls_characters) || inRange(character, non_character_codepoints);
560
-
561
- // prettier-ignore-start
562
- /**
563
- * D.1 Characters with bidirectional property "R" or "AL"
564
- * @link https://tools.ietf.org/html/rfc3454#appendix-D.1
565
- */
566
483
  const bidirectional_r_al = [0x05be, 0x05be, 0x05c0, 0x05c0, 0x05c3, 0x05c3, 0x05d0, 0x05ea, 0x05f0, 0x05f4, 0x061b, 0x061b, 0x061f, 0x061f, 0x0621, 0x063a, 0x0640, 0x064a, 0x066d, 0x066f, 0x0671, 0x06d5, 0x06dd, 0x06dd, 0x06e5, 0x06e6, 0x06fa, 0x06fe, 0x0700, 0x070d, 0x0710, 0x0710, 0x0712, 0x072c, 0x0780, 0x07a5, 0x07b1, 0x07b1, 0x200f, 0x200f, 0xfb1d, 0xfb1d, 0xfb1f, 0xfb28, 0xfb2a, 0xfb36, 0xfb38, 0xfb3c, 0xfb3e, 0xfb3e, 0xfb40, 0xfb41, 0xfb43, 0xfb44, 0xfb46, 0xfbb1, 0xfbd3, 0xfd3d, 0xfd50, 0xfd8f, 0xfd92, 0xfdc7, 0xfdf0, 0xfdfc, 0xfe70, 0xfe74, 0xfe76, 0xfefc];
567
- // prettier-ignore-end
568
-
569
484
  const isBidirectionalRAL = character => inRange(character, bidirectional_r_al);
570
-
571
- // prettier-ignore-start
572
- /**
573
- * D.2 Characters with bidirectional property "L"
574
- * @link https://tools.ietf.org/html/rfc3454#appendix-D.2
575
- */
576
485
  const bidirectional_l = [0x0041, 0x005a, 0x0061, 0x007a, 0x00aa, 0x00aa, 0x00b5, 0x00b5, 0x00ba, 0x00ba, 0x00c0, 0x00d6, 0x00d8, 0x00f6, 0x00f8, 0x0220, 0x0222, 0x0233, 0x0250, 0x02ad, 0x02b0, 0x02b8, 0x02bb, 0x02c1, 0x02d0, 0x02d1, 0x02e0, 0x02e4, 0x02ee, 0x02ee, 0x037a, 0x037a, 0x0386, 0x0386, 0x0388, 0x038a, 0x038c, 0x038c, 0x038e, 0x03a1, 0x03a3, 0x03ce, 0x03d0, 0x03f5, 0x0400, 0x0482, 0x048a, 0x04ce, 0x04d0, 0x04f5, 0x04f8, 0x04f9, 0x0500, 0x050f, 0x0531, 0x0556, 0x0559, 0x055f, 0x0561, 0x0587, 0x0589, 0x0589, 0x0903, 0x0903, 0x0905, 0x0939, 0x093d, 0x0940, 0x0949, 0x094c, 0x0950, 0x0950, 0x0958, 0x0961, 0x0964, 0x0970, 0x0982, 0x0983, 0x0985, 0x098c, 0x098f, 0x0990, 0x0993, 0x09a8, 0x09aa, 0x09b0, 0x09b2, 0x09b2, 0x09b6, 0x09b9, 0x09be, 0x09c0, 0x09c7, 0x09c8, 0x09cb, 0x09cc, 0x09d7, 0x09d7, 0x09dc, 0x09dd, 0x09df, 0x09e1, 0x09e6, 0x09f1, 0x09f4, 0x09fa, 0x0a05, 0x0a0a, 0x0a0f, 0x0a10, 0x0a13, 0x0a28, 0x0a2a, 0x0a30, 0x0a32, 0x0a33, 0x0a35, 0x0a36, 0x0a38, 0x0a39, 0x0a3e, 0x0a40, 0x0a59, 0x0a5c, 0x0a5e, 0x0a5e, 0x0a66, 0x0a6f, 0x0a72, 0x0a74, 0x0a83, 0x0a83, 0x0a85, 0x0a8b, 0x0a8d, 0x0a8d, 0x0a8f, 0x0a91, 0x0a93, 0x0aa8, 0x0aaa, 0x0ab0, 0x0ab2, 0x0ab3, 0x0ab5, 0x0ab9, 0x0abd, 0x0ac0, 0x0ac9, 0x0ac9, 0x0acb, 0x0acc, 0x0ad0, 0x0ad0, 0x0ae0, 0x0ae0, 0x0ae6, 0x0aef, 0x0b02, 0x0b03, 0x0b05, 0x0b0c, 0x0b0f, 0x0b10, 0x0b13, 0x0b28, 0x0b2a, 0x0b30, 0x0b32, 0x0b33, 0x0b36, 0x0b39, 0x0b3d, 0x0b3e, 0x0b40, 0x0b40, 0x0b47, 0x0b48, 0x0b4b, 0x0b4c, 0x0b57, 0x0b57, 0x0b5c, 0x0b5d, 0x0b5f, 0x0b61, 0x0b66, 0x0b70, 0x0b83, 0x0b83, 0x0b85, 0x0b8a, 0x0b8e, 0x0b90, 0x0b92, 0x0b95, 0x0b99, 0x0b9a, 0x0b9c, 0x0b9c, 0x0b9e, 0x0b9f, 0x0ba3, 0x0ba4, 0x0ba8, 0x0baa, 0x0bae, 0x0bb5, 0x0bb7, 0x0bb9, 0x0bbe, 0x0bbf, 0x0bc1, 0x0bc2, 0x0bc6, 0x0bc8, 0x0bca, 0x0bcc, 0x0bd7, 0x0bd7, 0x0be7, 0x0bf2, 0x0c01, 0x0c03, 0x0c05, 0x0c0c, 0x0c0e, 0x0c10, 0x0c12, 0x0c28, 0x0c2a, 0x0c33, 0x0c35, 0x0c39, 0x0c41, 0x0c44, 0x0c60, 0x0c61, 0x0c66, 0x0c6f, 0x0c82, 0x0c83, 0x0c85, 0x0c8c, 0x0c8e, 0x0c90, 0x0c92, 0x0ca8, 0x0caa, 0x0cb3, 0x0cb5, 0x0cb9, 0x0cbe, 0x0cbe, 0x0cc0, 0x0cc4, 0x0cc7, 0x0cc8, 0x0cca, 0x0ccb, 0x0cd5, 0x0cd6, 0x0cde, 0x0cde, 0x0ce0, 0x0ce1, 0x0ce6, 0x0cef, 0x0d02, 0x0d03, 0x0d05, 0x0d0c, 0x0d0e, 0x0d10, 0x0d12, 0x0d28, 0x0d2a, 0x0d39, 0x0d3e, 0x0d40, 0x0d46, 0x0d48, 0x0d4a, 0x0d4c, 0x0d57, 0x0d57, 0x0d60, 0x0d61, 0x0d66, 0x0d6f, 0x0d82, 0x0d83, 0x0d85, 0x0d96, 0x0d9a, 0x0db1, 0x0db3, 0x0dbb, 0x0dbd, 0x0dbd, 0x0dc0, 0x0dc6, 0x0dcf, 0x0dd1, 0x0dd8, 0x0ddf, 0x0df2, 0x0df4, 0x0e01, 0x0e30, 0x0e32, 0x0e33, 0x0e40, 0x0e46, 0x0e4f, 0x0e5b, 0x0e81, 0x0e82, 0x0e84, 0x0e84, 0x0e87, 0x0e88, 0x0e8a, 0x0e8a, 0x0e8d, 0x0e8d, 0x0e94, 0x0e97, 0x0e99, 0x0e9f, 0x0ea1, 0x0ea3, 0x0ea5, 0x0ea5, 0x0ea7, 0x0ea7, 0x0eaa, 0x0eab, 0x0ead, 0x0eb0, 0x0eb2, 0x0eb3, 0x0ebd, 0x0ebd, 0x0ec0, 0x0ec4, 0x0ec6, 0x0ec6, 0x0ed0, 0x0ed9, 0x0edc, 0x0edd, 0x0f00, 0x0f17, 0x0f1a, 0x0f34, 0x0f36, 0x0f36, 0x0f38, 0x0f38, 0x0f3e, 0x0f47, 0x0f49, 0x0f6a, 0x0f7f, 0x0f7f, 0x0f85, 0x0f85, 0x0f88, 0x0f8b, 0x0fbe, 0x0fc5, 0x0fc7, 0x0fcc, 0x0fcf, 0x0fcf, 0x1000, 0x1021, 0x1023, 0x1027, 0x1029, 0x102a, 0x102c, 0x102c, 0x1031, 0x1031, 0x1038, 0x1038, 0x1040, 0x1057, 0x10a0, 0x10c5, 0x10d0, 0x10f8, 0x10fb, 0x10fb, 0x1100, 0x1159, 0x115f, 0x11a2, 0x11a8, 0x11f9, 0x1200, 0x1206, 0x1208, 0x1246, 0x1248, 0x1248, 0x124a, 0x124d, 0x1250, 0x1256, 0x1258, 0x1258, 0x125a, 0x125d, 0x1260, 0x1286, 0x1288, 0x1288, 0x128a, 0x128d, 0x1290, 0x12ae, 0x12b0, 0x12b0, 0x12b2, 0x12b5, 0x12b8, 0x12be, 0x12c0, 0x12c0, 0x12c2, 0x12c5, 0x12c8, 0x12ce, 0x12d0, 0x12d6, 0x12d8, 0x12ee, 0x12f0, 0x130e, 0x1310, 0x1310, 0x1312, 0x1315, 0x1318, 0x131e, 0x1320, 0x1346, 0x1348, 0x135a, 0x1361, 0x137c, 0x13a0, 0x13f4, 0x1401, 0x1676, 0x1681, 0x169a, 0x16a0, 0x16f0, 0x1700, 0x170c, 0x170e, 0x1711, 0x1720, 0x1731, 0x1735, 0x1736, 0x1740, 0x1751, 0x1760, 0x176c, 0x176e, 0x1770, 0x1780, 0x17b6, 0x17be, 0x17c5, 0x17c7, 0x17c8, 0x17d4, 0x17da, 0x17dc, 0x17dc, 0x17e0, 0x17e9, 0x1810, 0x1819, 0x1820, 0x1877, 0x1880, 0x18a8, 0x1e00, 0x1e9b, 0x1ea0, 0x1ef9, 0x1f00, 0x1f15, 0x1f18, 0x1f1d, 0x1f20, 0x1f45, 0x1f48, 0x1f4d, 0x1f50, 0x1f57, 0x1f59, 0x1f59, 0x1f5b, 0x1f5b, 0x1f5d, 0x1f5d, 0x1f5f, 0x1f7d, 0x1f80, 0x1fb4, 0x1fb6, 0x1fbc, 0x1fbe, 0x1fbe, 0x1fc2, 0x1fc4, 0x1fc6, 0x1fcc, 0x1fd0, 0x1fd3, 0x1fd6, 0x1fdb, 0x1fe0, 0x1fec, 0x1ff2, 0x1ff4, 0x1ff6, 0x1ffc, 0x200e, 0x200e, 0x2071, 0x2071, 0x207f, 0x207f, 0x2102, 0x2102, 0x2107, 0x2107, 0x210a, 0x2113, 0x2115, 0x2115, 0x2119, 0x211d, 0x2124, 0x2124, 0x2126, 0x2126, 0x2128, 0x2128, 0x212a, 0x212d, 0x212f, 0x2131, 0x2133, 0x2139, 0x213d, 0x213f, 0x2145, 0x2149, 0x2160, 0x2183, 0x2336, 0x237a, 0x2395, 0x2395, 0x249c, 0x24e9, 0x3005, 0x3007, 0x3021, 0x3029, 0x3031, 0x3035, 0x3038, 0x303c, 0x3041, 0x3096, 0x309d, 0x309f, 0x30a1, 0x30fa, 0x30fc, 0x30ff, 0x3105, 0x312c, 0x3131, 0x318e, 0x3190, 0x31b7, 0x31f0, 0x321c, 0x3220, 0x3243, 0x3260, 0x327b, 0x327f, 0x32b0, 0x32c0, 0x32cb, 0x32d0, 0x32fe, 0x3300, 0x3376, 0x337b, 0x33dd, 0x33e0, 0x33fe, 0x3400, 0x4db5, 0x4e00, 0x9fa5, 0xa000, 0xa48c, 0xac00, 0xd7a3, 0xd800, 0xfa2d, 0xfa30, 0xfa6a, 0xfb00, 0xfb06, 0xfb13, 0xfb17, 0xff21, 0xff3a, 0xff41, 0xff5a, 0xff66, 0xffbe, 0xffc2, 0xffc7, 0xffca, 0xffcf, 0xffd2, 0xffd7, 0xffda, 0xffdc, 0x10300, 0x1031e, 0x10320, 0x10323, 0x10330, 0x1034a, 0x10400, 0x10425, 0x10428, 0x1044d, 0x1d000, 0x1d0f5, 0x1d100, 0x1d126, 0x1d12a, 0x1d166, 0x1d16a, 0x1d172, 0x1d183, 0x1d184, 0x1d18c, 0x1d1a9, 0x1d1ae, 0x1d1dd, 0x1d400, 0x1d454, 0x1d456, 0x1d49c, 0x1d49e, 0x1d49f, 0x1d4a2, 0x1d4a2, 0x1d4a5, 0x1d4a6, 0x1d4a9, 0x1d4ac, 0x1d4ae, 0x1d4b9, 0x1d4bb, 0x1d4bb, 0x1d4bd, 0x1d4c0, 0x1d4c2, 0x1d4c3, 0x1d4c5, 0x1d505, 0x1d507, 0x1d50a, 0x1d50d, 0x1d514, 0x1d516, 0x1d51c, 0x1d51e, 0x1d539, 0x1d53b, 0x1d53e, 0x1d540, 0x1d544, 0x1d546, 0x1d546, 0x1d54a, 0x1d550, 0x1d552, 0x1d6a3, 0x1d6a8, 0x1d7c9, 0x20000, 0x2a6d6, 0x2f800, 0x2fa1d, 0xf0000, 0xffffd, 0x100000, 0x10fffd];
577
- // prettier-ignore-end
578
-
579
486
  const isBidirectionalL = character => inRange(character, bidirectional_l);
580
487
 
581
- // 2.1. Mapping
582
-
583
- /**
584
- * non-ASCII space characters [StringPrep, C.1.2] that can be
585
- * mapped to SPACE (U+0020)
586
- */
587
488
  const mapping2space = isNonASCIISpaceCharacter;
588
-
589
- /**
590
- * the "commonly mapped to nothing" characters [StringPrep, B.1]
591
- * that can be mapped to nothing.
592
- */
593
489
  const mapping2nothing = isCommonlyMappedToNothing;
594
-
595
- // utils
596
490
  const getCodePoint = character => character.codePointAt(0);
597
491
  const first = x => x[0];
598
492
  const last = x => x[x.length - 1];
599
-
600
- /**
601
- * Convert provided string into an array of Unicode Code Points.
602
- * Based on https://stackoverflow.com/a/21409165/1556249
603
- * and https://www.npmjs.com/package/code-point-at.
604
- * @param {string} input
605
- * @returns {number[]}
606
- */
607
493
  function toCodePoints(input) {
608
494
  const codepoints = [];
609
495
  const size = input.length;
@@ -621,14 +507,6 @@ function toCodePoints(input) {
621
507
  }
622
508
  return codepoints;
623
509
  }
624
-
625
- /**
626
- * SASLprep.
627
- * @param {string} input
628
- * @param {Object} opts
629
- * @param {boolean} opts.allowUnassigned
630
- * @returns {string}
631
- */
632
510
  function saslprep(input) {
633
511
  let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
634
512
  if (typeof input !== 'string') {
@@ -637,49 +515,24 @@ function saslprep(input) {
637
515
  if (input.length === 0) {
638
516
  return '';
639
517
  }
640
-
641
- // 1. Map
642
- const mapped_input = toCodePoints(input)
643
- // 1.1 mapping to space
644
- .map(character => mapping2space(character) ? 0x20 : character)
645
- // 1.2 mapping to nothing
646
- .filter(character => !mapping2nothing(character));
647
-
648
- // 2. Normalize
518
+ const mapped_input = toCodePoints(input).map(character => mapping2space(character) ? 0x20 : character).filter(character => !mapping2nothing(character));
649
519
  const normalized_input = String.fromCodePoint.apply(null, mapped_input).normalize('NFKC');
650
520
  const normalized_map = toCodePoints(normalized_input);
651
-
652
- // 3. Prohibit
653
521
  const hasProhibited = normalized_map.some(isProhibitedCharacter);
654
522
  if (hasProhibited) {
655
523
  throw new Error('Prohibited character, see https://tools.ietf.org/html/rfc4013#section-2.3');
656
524
  }
657
-
658
- // Unassigned Code Points
659
525
  if (opts.allowUnassigned !== true) {
660
526
  const hasUnassigned = normalized_map.some(isUnassignedCodePoint);
661
527
  if (hasUnassigned) {
662
528
  throw new Error('Unassigned code point, see https://tools.ietf.org/html/rfc4013#section-2.5');
663
529
  }
664
530
  }
665
-
666
- // 4. check bidi
667
-
668
531
  const hasBidiRAL = normalized_map.some(isBidirectionalRAL);
669
532
  const hasBidiL = normalized_map.some(isBidirectionalL);
670
-
671
- // 4.1 If a string contains any RandALCat character, the string MUST NOT
672
- // contain any LCat character.
673
533
  if (hasBidiRAL && hasBidiL) {
674
534
  throw new Error('String must not contain RandALCat and LCat at the same time,' + ' see https://tools.ietf.org/html/rfc3454#section-6');
675
535
  }
676
-
677
- /**
678
- * 4.2 If a string contains any RandALCat character, a RandALCat
679
- * character MUST be the first character of the string, and a
680
- * RandALCat character MUST be the last character of the string.
681
- */
682
-
683
536
  const isFirstBidiRAL = isBidirectionalRAL(getCodePoint(first(normalized_input)));
684
537
  const isLastBidiRAL = isBidirectionalRAL(getCodePoint(last(normalized_input)));
685
538
  if (hasBidiRAL && !(isFirstBidiRAL && isLastBidiRAL)) {
@@ -688,16 +541,11 @@ function saslprep(input) {
688
541
  return normalized_input;
689
542
  }
690
543
 
691
- /*
692
- PDFSecurity - represents PDF security settings
693
- By Yang Liu <hi@zesik.com>
694
- */
695
544
  class PDFSecurity {
696
545
  static generateFileID() {
697
546
  let info = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
698
547
  let infoStr = `${info.CreationDate.getTime()}\n`;
699
548
  for (let key in info) {
700
- // eslint-disable-next-line no-prototype-builtins
701
549
  if (!info.hasOwnProperty(key)) {
702
550
  continue;
703
551
  }
@@ -1077,8 +925,6 @@ class PDFGradient$1 {
1077
925
  }
1078
926
  this.embedded = true;
1079
927
  this.matrix = m;
1080
-
1081
- // if the last stop comes before 100%, add a copy at 100%
1082
928
  const last = this.stops[stopsLength - 1];
1083
929
  if (last[0] < 1) {
1084
930
  this.stops.push([1, last[1], last[2]]);
@@ -1101,14 +947,11 @@ class PDFGradient$1 {
1101
947
  stops.push(fn);
1102
948
  fn.end();
1103
949
  }
1104
-
1105
- // if there are only two stops, we don't need a stitching function
1106
950
  if (stopsLength === 1) {
1107
951
  fn = stops[0];
1108
952
  } else {
1109
953
  fn = this.doc.ref({
1110
954
  FunctionType: 3,
1111
- // stitching function
1112
955
  Domain: [0, 1],
1113
956
  Functions: stops,
1114
957
  Bounds: bounds,
@@ -1189,7 +1032,6 @@ class PDFGradient$1 {
1189
1032
  return pattern;
1190
1033
  }
1191
1034
  apply(stroke) {
1192
- // apply gradient transform to existing document ctm
1193
1035
  const [m0, m1, m2, m3, m4, m5] = this.doc._ctm;
1194
1036
  const [m11, m12, m21, m22, dx, dy] = this.transform;
1195
1037
  const m = [m0 * m11 + m2 * m12, m1 * m11 + m3 * m12, m0 * m21 + m2 * m22, m1 * m21 + m3 * m22, m0 * dx + m2 * dy + m4, m1 * dx + m3 * dy + m5];
@@ -1252,10 +1094,6 @@ var Gradient = {
1252
1094
  PDFRadialGradient: PDFRadialGradient$1
1253
1095
  };
1254
1096
 
1255
- /*
1256
- PDF tiling pattern support. Uncolored only.
1257
- */
1258
-
1259
1097
  const underlyingColorSpaces = ['DeviceCMYK', 'DeviceRGB'];
1260
1098
  class PDFTilingPattern$1 {
1261
1099
  constructor(doc, bBox, xStep, yStep, stream) {
@@ -1266,23 +1104,16 @@ class PDFTilingPattern$1 {
1266
1104
  this.stream = stream;
1267
1105
  }
1268
1106
  createPattern() {
1269
- // no resources needed for our current usage
1270
- // required entry
1271
1107
  const resources = this.doc.ref();
1272
1108
  resources.end();
1273
- // apply default transform matrix (flipped in the default doc._ctm)
1274
- // see document.js & gradient.js
1275
1109
  const [m0, m1, m2, m3, m4, m5] = this.doc._ctm;
1276
1110
  const [m11, m12, m21, m22, dx, dy] = [1, 0, 0, 1, 0, 0];
1277
1111
  const m = [m0 * m11 + m2 * m12, m1 * m11 + m3 * m12, m0 * m21 + m2 * m22, m1 * m21 + m3 * m22, m0 * dx + m2 * dy + m4, m1 * dx + m3 * dy + m5];
1278
1112
  const pattern = this.doc.ref({
1279
1113
  Type: 'Pattern',
1280
1114
  PatternType: 1,
1281
- // tiling
1282
1115
  PaintType: 2,
1283
- // 1-colored, 2-uncolored
1284
1116
  TilingType: 2,
1285
- // 2-no distortion
1286
1117
  BBox: this.bBox,
1287
1118
  XStep: this.xStep,
1288
1119
  YStep: this.yStep,
@@ -1293,8 +1124,6 @@ class PDFTilingPattern$1 {
1293
1124
  return pattern;
1294
1125
  }
1295
1126
  embedPatternColorSpaces() {
1296
- // map each pattern to an underlying color space
1297
- // and embed on each page
1298
1127
  underlyingColorSpaces.forEach(csName => {
1299
1128
  const csId = this.getPatternColorSpaceId(csName);
1300
1129
  if (this.doc.page.colorSpaces[csId]) return;
@@ -1312,24 +1141,17 @@ class PDFTilingPattern$1 {
1312
1141
  this.id = 'P' + this.doc._patternCount;
1313
1142
  this.pattern = this.createPattern();
1314
1143
  }
1315
-
1316
- // patterns are embedded in each page
1317
1144
  if (!this.doc.page.patterns[this.id]) {
1318
1145
  this.doc.page.patterns[this.id] = this.pattern;
1319
1146
  }
1320
1147
  }
1321
1148
  apply(stroke, patternColor) {
1322
- // do any embedding/creating that might be needed
1323
1149
  this.embedPatternColorSpaces();
1324
1150
  this.embed();
1325
1151
  const normalizedColor = this.doc._normalizeColor(patternColor);
1326
1152
  if (!normalizedColor) throw Error(`invalid pattern color. (value: ${patternColor})`);
1327
-
1328
- // select one of the pattern color spaces
1329
1153
  const csId = this.getPatternColorSpaceId(this.doc._getColorSpace(normalizedColor));
1330
1154
  this.doc._setColorSpace(csId, stroke);
1331
-
1332
- // stroke/fill using the pattern and color (in the above underlying color space)
1333
1155
  const op = stroke ? 'SCN' : 'scn';
1334
1156
  return this.doc.addContent(`${normalizedColor.join(' ')} /${this.id} ${op}`);
1335
1157
  }
@@ -1349,11 +1171,10 @@ const {
1349
1171
  var ColorMixin = {
1350
1172
  initColor() {
1351
1173
  this.spotColors = {};
1352
- // The opacity dictionaries
1353
1174
  this._opacityRegistry = {};
1354
1175
  this._opacityCount = 0;
1355
1176
  this._patternCount = 0;
1356
- return this._gradCount = 0;
1177
+ this._gradCount = 0;
1357
1178
  },
1358
1179
  _normalizeColor(color) {
1359
1180
  if (typeof color === 'string') {
@@ -1370,10 +1191,8 @@ var ColorMixin = {
1370
1191
  }
1371
1192
  }
1372
1193
  if (Array.isArray(color)) {
1373
- // RGB
1374
1194
  if (color.length === 3) {
1375
1195
  color = color.map(part => part / 255);
1376
- // CMYK
1377
1196
  } else if (color.length === 4) {
1378
1197
  color = color.map(part => part / 100);
1379
1198
  }
@@ -1385,12 +1204,10 @@ var ColorMixin = {
1385
1204
  if (color instanceof PDFGradient) {
1386
1205
  color.apply(stroke);
1387
1206
  return true;
1388
- // see if tiling pattern, decode & apply it it
1389
1207
  } else if (Array.isArray(color) && color[0] instanceof PDFTilingPattern) {
1390
1208
  color[0].apply(stroke, color[1]);
1391
1209
  return true;
1392
1210
  }
1393
- // any other case should be a normal color and not a pattern
1394
1211
  return this._setColorCore(color, stroke);
1395
1212
  },
1396
1213
  _setColorCore(color, stroke) {
@@ -1424,9 +1241,6 @@ var ColorMixin = {
1424
1241
  if (set) {
1425
1242
  this.fillOpacity(opacity);
1426
1243
  }
1427
-
1428
- // save this for text wrapper, which needs to reset
1429
- // the fill color on new pages
1430
1244
  this._fillColor = [color, opacity];
1431
1245
  return this;
1432
1246
  },
@@ -1682,7 +1496,6 @@ const parse = function (path) {
1682
1496
  if (parameters[c] != null) {
1683
1497
  params = parameters[c];
1684
1498
  if (cmd) {
1685
- // save existing command
1686
1499
  if (curArg.length > 0) {
1687
1500
  args[args.length] = +curArg;
1688
1501
  }
@@ -1700,14 +1513,11 @@ const parse = function (path) {
1700
1513
  continue;
1701
1514
  }
1702
1515
  if (args.length === params) {
1703
- // handle reused commands
1704
1516
  ret[ret.length] = {
1705
1517
  cmd,
1706
1518
  args
1707
1519
  };
1708
1520
  args = [+curArg];
1709
-
1710
- // handle assumed commands
1711
1521
  if (cmd === 'M') {
1712
1522
  cmd = 'L';
1713
1523
  }
@@ -1718,8 +1528,6 @@ const parse = function (path) {
1718
1528
  args[args.length] = +curArg;
1719
1529
  }
1720
1530
  foundDecimal = c === '.';
1721
-
1722
- // fix for negative numbers or repeated decimals with no delimeter between commands
1723
1531
  curArg = ['-', '.'].includes(c) ? c : '';
1724
1532
  } else {
1725
1533
  curArg += c;
@@ -1728,18 +1536,13 @@ const parse = function (path) {
1728
1536
  }
1729
1537
  }
1730
1538
  }
1731
-
1732
- // add the last command
1733
1539
  if (curArg.length > 0) {
1734
1540
  if (args.length === params) {
1735
- // handle reused commands
1736
1541
  ret[ret.length] = {
1737
1542
  cmd,
1738
1543
  args
1739
1544
  };
1740
1545
  args = [+curArg];
1741
-
1742
- // handle assumed commands
1743
1546
  if (cmd === 'M') {
1744
1547
  cmd = 'L';
1745
1548
  }
@@ -1757,10 +1560,7 @@ const parse = function (path) {
1757
1560
  return ret;
1758
1561
  };
1759
1562
  const apply = function (commands, doc) {
1760
- // current point, control point, and subpath starting point
1761
1563
  cx = cy = px = py = sx = sy = 0;
1762
-
1763
- // run the commands
1764
1564
  for (let i = 0; i < commands.length; i++) {
1765
1565
  const c = commands[i];
1766
1566
  if (typeof runners[c.cmd] === 'function') {
@@ -1924,8 +1724,6 @@ const solveArc = function (doc, x, y, coords) {
1924
1724
  doc.bezierCurveTo(...bez);
1925
1725
  }
1926
1726
  };
1927
-
1928
- // from Inkscape svgtopdf, thanks!
1929
1727
  const arcToSegments = function (x, y, rx, ry, large, sweep, rotateX, ox, oy) {
1930
1728
  const th = rotateX * (Math.PI / 180);
1931
1729
  const sin_th = Math.sin(th);
@@ -2001,18 +1799,14 @@ class SVGPath {
2001
1799
  const {
2002
1800
  number: number$1
2003
1801
  } = PDFObject;
2004
-
2005
- // This constant is used to approximate a symmetrical arc using a cubic
2006
- // Bezier curve.
2007
1802
  const KAPPA = 4.0 * ((Math.sqrt(2) - 1.0) / 3.0);
2008
1803
  var VectorMixin = {
2009
1804
  initVector() {
2010
- this._ctm = [1, 0, 0, 1, 0, 0]; // current transformation matrix
2011
- return this._ctmStack = [];
1805
+ this._ctm = [1, 0, 0, 1, 0, 0];
1806
+ this._ctmStack = [];
2012
1807
  },
2013
1808
  save() {
2014
1809
  this._ctmStack.push(this._ctm.slice());
2015
- // TODO: save/restore colorspace and styles so not setting it unnessesarily all the time?
2016
1810
  return this.addContent('q');
2017
1811
  },
2018
1812
  restore() {
@@ -2086,8 +1880,6 @@ var VectorMixin = {
2086
1880
  r = 0;
2087
1881
  }
2088
1882
  r = Math.min(r, 0.5 * w, 0.5 * h);
2089
-
2090
- // amount to inset control points from corners (see `ellipse`)
2091
1883
  const c = r * (1.0 - KAPPA);
2092
1884
  this.moveTo(x + r, y);
2093
1885
  this.lineTo(x + w - r, y);
@@ -2101,7 +1893,6 @@ var VectorMixin = {
2101
1893
  return this.closePath();
2102
1894
  },
2103
1895
  ellipse(x, y, r1, r2) {
2104
- // based on http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas/2173084#2173084
2105
1896
  if (r2 == null) {
2106
1897
  r2 = r1;
2107
1898
  }
@@ -2131,10 +1922,8 @@ var VectorMixin = {
2131
1922
  const HALF_PI = 0.5 * Math.PI;
2132
1923
  let deltaAng = endAngle - startAngle;
2133
1924
  if (Math.abs(deltaAng) > TWO_PI) {
2134
- // draw only full circle if more than that is specified
2135
1925
  deltaAng = TWO_PI;
2136
1926
  } else if (deltaAng !== 0 && anticlockwise !== deltaAng < 0) {
2137
- // necessary to flip direction of rendering
2138
1927
  const dir = anticlockwise ? -1 : 1;
2139
1928
  deltaAng = dir * TWO_PI + deltaAng;
2140
1929
  }
@@ -2142,38 +1931,21 @@ var VectorMixin = {
2142
1931
  const segAng = deltaAng / numSegs;
2143
1932
  const handleLen = segAng / HALF_PI * KAPPA * radius;
2144
1933
  let curAng = startAngle;
2145
-
2146
- // component distances between anchor point and control point
2147
1934
  let deltaCx = -Math.sin(curAng) * handleLen;
2148
1935
  let deltaCy = Math.cos(curAng) * handleLen;
2149
-
2150
- // anchor point
2151
1936
  let ax = x + Math.cos(curAng) * radius;
2152
1937
  let ay = y + Math.sin(curAng) * radius;
2153
-
2154
- // calculate and render segments
2155
1938
  this.moveTo(ax, ay);
2156
1939
  for (let segIdx = 0; segIdx < numSegs; segIdx++) {
2157
- // starting control point
2158
1940
  const cp1x = ax + deltaCx;
2159
1941
  const cp1y = ay + deltaCy;
2160
-
2161
- // step angle
2162
1942
  curAng += segAng;
2163
-
2164
- // next anchor point
2165
1943
  ax = x + Math.cos(curAng) * radius;
2166
1944
  ay = y + Math.sin(curAng) * radius;
2167
-
2168
- // next control point delta
2169
1945
  deltaCx = -Math.sin(curAng) * handleLen;
2170
1946
  deltaCy = Math.cos(curAng) * handleLen;
2171
-
2172
- // ending control point
2173
1947
  const cp2x = ax - deltaCx;
2174
1948
  const cp2y = ay - deltaCy;
2175
-
2176
- // render segment
2177
1949
  this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, ax, ay);
2178
1950
  }
2179
1951
  return this;
@@ -2237,9 +2009,7 @@ var VectorMixin = {
2237
2009
  return this.addContent(`W${this._windingRule(rule)} n`);
2238
2010
  },
2239
2011
  transform(m11, m12, m21, m22, dx, dy) {
2240
- // keep track of the current transformation matrix
2241
2012
  if (m11 === 1 && m12 === 0 && m21 === 0 && m22 === 1 && dx === 0 && dy === 0) {
2242
- // Ignore identity transforms
2243
2013
  return this;
2244
2014
  }
2245
2015
  const m = this._ctm;
@@ -2395,21 +2165,12 @@ oslash ugrave uacute ucircumflex
2395
2165
  udieresis yacute thorn ydieresis\
2396
2166
  `.split(/\s+/);
2397
2167
  class AFMFont {
2398
- static open(filename) {
2399
- return new AFMFont(fs.readFileSync(filename, 'utf8'));
2400
- }
2401
2168
  constructor(contents) {
2402
- this.contents = contents;
2403
2169
  this.attributes = {};
2404
2170
  this.glyphWidths = {};
2405
2171
  this.boundingBoxes = {};
2406
2172
  this.kernPairs = {};
2407
- this.parse();
2408
- // todo: remove charWidths since appears to not be used
2409
- this.charWidths = new Array(256);
2410
- for (let char = 0; char <= 255; char++) {
2411
- this.charWidths[char] = this.glyphWidths[characters[char]];
2412
- }
2173
+ this.parse(contents);
2413
2174
  this.bbox = this.attributes['FontBBox'].split(/\s+/).map(e => +e);
2414
2175
  this.ascender = +(this.attributes['Ascender'] || 0);
2415
2176
  this.descender = +(this.attributes['Descender'] || 0);
@@ -2417,9 +2178,9 @@ class AFMFont {
2417
2178
  this.capHeight = +(this.attributes['CapHeight'] || 0);
2418
2179
  this.lineGap = this.bbox[3] - this.bbox[1] - (this.ascender - this.descender);
2419
2180
  }
2420
- parse() {
2181
+ parse(contents) {
2421
2182
  let section = '';
2422
- for (let line of this.contents.split('\n')) {
2183
+ for (let line of contents.split('\n')) {
2423
2184
  var match;
2424
2185
  var a;
2425
2186
  if (match = line.match(/^Start(\w+)/)) {
@@ -2512,21 +2273,18 @@ class PDFFont {
2512
2273
  return;
2513
2274
  }
2514
2275
  this.embed();
2515
- return this.embedded = true;
2276
+ this.embedded = true;
2516
2277
  }
2517
2278
  embed() {
2518
2279
  throw new Error('Must be implemented by subclasses');
2519
2280
  }
2520
- lineHeight(size, includeGap) {
2521
- if (includeGap == null) {
2522
- includeGap = false;
2523
- }
2281
+ lineHeight(size) {
2282
+ let includeGap = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2524
2283
  const gap = includeGap ? this.lineGap : 0;
2525
2284
  return (this.ascender + gap - this.descender) / 1000 * size;
2526
2285
  }
2527
2286
  }
2528
2287
 
2529
- // This insanity is so bundlers can inline the font files
2530
2288
  const STANDARD_FONTS = {
2531
2289
  Courier() {
2532
2290
  return fs.readFileSync(__dirname + '/data/Courier.afm', 'utf8');
@@ -2654,8 +2412,6 @@ class EmbeddedFont extends PDFFont {
2654
2412
  }
2655
2413
  layoutRun(text, features) {
2656
2414
  const run = this.font.layout(text, features);
2657
-
2658
- // Normalize position values
2659
2415
  for (let i = 0; i < run.positions.length; i++) {
2660
2416
  const position = run.positions[i];
2661
2417
  for (let key in position) {
@@ -2678,16 +2434,12 @@ class EmbeddedFont extends PDFFont {
2678
2434
  return run;
2679
2435
  }
2680
2436
  layout(text, features, onlyWidth) {
2681
- // Skip the cache if any user defined features are applied
2682
2437
  if (features) {
2683
2438
  return this.layoutRun(text, features);
2684
2439
  }
2685
2440
  let glyphs = onlyWidth ? null : [];
2686
2441
  let positions = onlyWidth ? null : [];
2687
2442
  let advanceWidth = 0;
2688
-
2689
- // Split the string by words to increase cache efficiency.
2690
- // For this purpose, spaces and tabs are a good enough delimeter.
2691
2443
  let last = 0;
2692
2444
  let index = 0;
2693
2445
  while (index <= text.length) {
@@ -2749,17 +2501,15 @@ class EmbeddedFont extends PDFFont {
2749
2501
  if (1 <= familyClass && familyClass <= 7) {
2750
2502
  flags |= 1 << 1;
2751
2503
  }
2752
- flags |= 1 << 2; // assume the font uses non-latin characters
2504
+ flags |= 1 << 2;
2753
2505
  if (familyClass === 10) {
2754
2506
  flags |= 1 << 3;
2755
2507
  }
2756
2508
  if (this.font.head.macStyle.italic) {
2757
2509
  flags |= 1 << 6;
2758
2510
  }
2759
-
2760
- // generate a tag (6 uppercase letters. 17 is the char code offset from '0' to 'A'. 73 will map to 'Z')
2761
2511
  const tag = [1, 2, 3, 4, 5, 6].map(i => String.fromCharCode((this.id.charCodeAt(i) || 73) + 17)).join('');
2762
- const name = tag + '+' + this.font.postscriptName.replaceAll(' ', '_');
2512
+ const name = tag + '+' + this.font.postscriptName?.replaceAll(' ', '_');
2763
2513
  const {
2764
2514
  bbox
2765
2515
  } = this.font;
@@ -2774,8 +2524,7 @@ class EmbeddedFont extends PDFFont {
2774
2524
  CapHeight: (this.font.capHeight || this.font.ascent) * this.scale,
2775
2525
  XHeight: (this.font.xHeight || 0) * this.scale,
2776
2526
  StemV: 0
2777
- }); // not sure how to calculate this
2778
-
2527
+ });
2779
2528
  if (isCFF) {
2780
2529
  descriptor.data.FontFile3 = fontFile;
2781
2530
  } else {
@@ -2817,17 +2566,11 @@ class EmbeddedFont extends PDFFont {
2817
2566
  };
2818
2567
  return this.dictionary.end();
2819
2568
  }
2820
-
2821
- // Maps the glyph ids encoded in the PDF back to unicode strings
2822
- // Because of ligature substitutions and the like, there may be one or more
2823
- // unicode characters represented by each glyph.
2824
2569
  toUnicodeCmap() {
2825
2570
  const cmap = this.document.ref();
2826
2571
  const entries = [];
2827
2572
  for (let codePoints of this.unicode) {
2828
2573
  const encoded = [];
2829
-
2830
- // encode codePoints to utf16
2831
2574
  for (let value of codePoints) {
2832
2575
  if (value > 0xffff) {
2833
2576
  value -= 0x10000;
@@ -2894,12 +2637,9 @@ class PDFFontFactory {
2894
2637
  }
2895
2638
 
2896
2639
  const isEqualFont = (font1, font2) => {
2897
- // compare font checksum
2898
2640
  if (font1.font._tables?.head?.checkSumAdjustment !== font2.font._tables?.head?.checkSumAdjustment) {
2899
2641
  return false;
2900
2642
  }
2901
-
2902
- // compare font name table
2903
2643
  if (JSON.stringify(font1.font._tables?.name?.records) !== JSON.stringify(font2.font._tables?.name?.records)) {
2904
2644
  return false;
2905
2645
  }
@@ -2908,18 +2648,18 @@ const isEqualFont = (font1, font2) => {
2908
2648
  var FontsMixin = {
2909
2649
  initFonts() {
2910
2650
  let defaultFont = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'Helvetica';
2911
- // Lookup table for embedded fonts
2651
+ let defaultFontFamily = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
2652
+ let defaultFontSize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 12;
2912
2653
  this._fontFamilies = {};
2913
2654
  this._fontCount = 0;
2914
-
2915
- // Font state
2916
- this._fontSize = 12;
2655
+ this._fontSource = defaultFont;
2656
+ this._fontFamily = defaultFontFamily;
2657
+ this._fontSize = defaultFontSize;
2917
2658
  this._font = null;
2659
+ this._remSize = defaultFontSize;
2918
2660
  this._registeredFonts = {};
2919
-
2920
- // Set the default font
2921
2661
  if (defaultFont) {
2922
- this.font(defaultFont);
2662
+ this.font(defaultFont, defaultFontFamily);
2923
2663
  }
2924
2664
  },
2925
2665
  font(src, family, size) {
@@ -2928,8 +2668,6 @@ var FontsMixin = {
2928
2668
  size = family;
2929
2669
  family = null;
2930
2670
  }
2931
-
2932
- // check registered fonts if src is a string
2933
2671
  if (typeof src === 'string' && this._registeredFonts[src]) {
2934
2672
  cacheKey = src;
2935
2673
  ({
@@ -2942,28 +2680,21 @@ var FontsMixin = {
2942
2680
  cacheKey = null;
2943
2681
  }
2944
2682
  }
2683
+ this._fontSource = src;
2684
+ this._fontFamily = family;
2945
2685
  if (size != null) {
2946
2686
  this.fontSize(size);
2947
2687
  }
2948
-
2949
- // fast path: check if the font is already in the PDF
2950
2688
  if (font = this._fontFamilies[cacheKey]) {
2951
2689
  this._font = font;
2952
2690
  return this;
2953
2691
  }
2954
-
2955
- // load the font
2956
2692
  const id = `F${++this._fontCount}`;
2957
2693
  this._font = PDFFontFactory.open(this, src, family, id);
2958
-
2959
- // check for existing font familes with the same name already in the PDF
2960
- // useful if the font was passed as a buffer
2961
2694
  if ((font = this._fontFamilies[this._font.name]) && isEqualFont(this._font, font)) {
2962
2695
  this._font = font;
2963
2696
  return this;
2964
2697
  }
2965
-
2966
- // save the font for reuse later
2967
2698
  if (cacheKey) {
2968
2699
  this._fontFamilies[cacheKey] = this._font;
2969
2700
  }
@@ -2973,13 +2704,10 @@ var FontsMixin = {
2973
2704
  return this;
2974
2705
  },
2975
2706
  fontSize(_fontSize) {
2976
- this._fontSize = _fontSize;
2707
+ this._fontSize = this.sizeToPoint(_fontSize);
2977
2708
  return this;
2978
2709
  },
2979
2710
  currentLineHeight(includeGap) {
2980
- if (includeGap == null) {
2981
- includeGap = false;
2982
- }
2983
2711
  return this._font.lineHeight(this._fontSize, includeGap);
2984
2712
  },
2985
2713
  registerFont(name, src, family) {
@@ -2988,6 +2716,67 @@ var FontsMixin = {
2988
2716
  family
2989
2717
  };
2990
2718
  return this;
2719
+ },
2720
+ sizeToPoint(size) {
2721
+ let defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2722
+ let page = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.page;
2723
+ let percentageWidth = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : undefined;
2724
+ if (!percentageWidth) percentageWidth = this._fontSize;
2725
+ if (typeof defaultValue !== 'number') defaultValue = this.sizeToPoint(defaultValue);
2726
+ if (size === undefined) return defaultValue;
2727
+ if (typeof size === 'number') return size;
2728
+ if (typeof size === 'boolean') return Number(size);
2729
+ const match = String(size).match(/((\d+)?(\.\d+)?)(em|in|px|cm|mm|pc|ex|ch|rem|vw|vh|vmin|vmax|%|pt)?/);
2730
+ if (!match) throw new Error(`Unsupported size '${size}'`);
2731
+ let multiplier;
2732
+ switch (match[4]) {
2733
+ case 'em':
2734
+ multiplier = this._fontSize;
2735
+ break;
2736
+ case 'in':
2737
+ multiplier = IN_TO_PT;
2738
+ break;
2739
+ case 'px':
2740
+ multiplier = PX_TO_IN * IN_TO_PT;
2741
+ break;
2742
+ case 'cm':
2743
+ multiplier = CM_TO_IN * IN_TO_PT;
2744
+ break;
2745
+ case 'mm':
2746
+ multiplier = MM_TO_CM * CM_TO_IN * IN_TO_PT;
2747
+ break;
2748
+ case 'pc':
2749
+ multiplier = PC_TO_PT;
2750
+ break;
2751
+ case 'ex':
2752
+ multiplier = this.currentLineHeight();
2753
+ break;
2754
+ case 'ch':
2755
+ multiplier = this.widthOfString('0');
2756
+ break;
2757
+ case 'rem':
2758
+ multiplier = this._remSize;
2759
+ break;
2760
+ case 'vw':
2761
+ multiplier = page.width / 100;
2762
+ break;
2763
+ case 'vh':
2764
+ multiplier = page.height / 100;
2765
+ break;
2766
+ case 'vmin':
2767
+ multiplier = Math.min(page.width, page.height) / 100;
2768
+ break;
2769
+ case 'vmax':
2770
+ multiplier = Math.max(page.width, page.height) / 100;
2771
+ break;
2772
+ case '%':
2773
+ multiplier = percentageWidth / 100;
2774
+ break;
2775
+ case 'pt':
2776
+ default:
2777
+ multiplier = 1;
2778
+ }
2779
+ return multiplier * Number(match[1]);
2991
2780
  }
2992
2781
  };
2993
2782
 
@@ -3002,7 +2791,7 @@ class LineWrapper extends EventEmitter {
3002
2791
  this.characterSpacing = (options.characterSpacing || 0) * this.horizontalScaling / 100;
3003
2792
  this.wordSpacing = (options.wordSpacing === 0) * this.horizontalScaling / 100;
3004
2793
  this.columns = options.columns || 1;
3005
- this.columnGap = (options.columnGap != null ? options.columnGap : 18) * this.horizontalScaling / 100; // 1/4 inch
2794
+ this.columnGap = (options.columnGap != null ? options.columnGap : 18) * this.horizontalScaling / 100;
3006
2795
  this.lineWidth = (options.width * this.horizontalScaling / 100 - this.columnGap * (this.columns - 1)) / this.columns;
3007
2796
  this.spaceLeft = this.lineWidth;
3008
2797
  this.startX = this.document.x;
@@ -3011,44 +2800,30 @@ class LineWrapper extends EventEmitter {
3011
2800
  this.ellipsis = options.ellipsis;
3012
2801
  this.continuedX = 0;
3013
2802
  this.features = options.features;
3014
-
3015
- // calculate the maximum Y position the text can appear at
3016
2803
  if (options.height != null) {
3017
2804
  this.height = options.height;
3018
- this.maxY = this.startY + options.height;
2805
+ this.maxY = PDFNumber(this.startY + options.height);
3019
2806
  } else {
3020
- this.maxY = this.document.page.maxY();
2807
+ this.maxY = PDFNumber(this.document.page.maxY());
3021
2808
  }
3022
-
3023
- // handle paragraph indents
3024
2809
  this.on('firstLine', options => {
3025
- // if this is the first line of the text segment, and
3026
- // we're continuing where we left off, indent that much
3027
- // otherwise use the user specified indent option
3028
2810
  const indent = this.continuedX || this.indent;
3029
2811
  this.document.x += indent;
3030
2812
  this.lineWidth -= indent;
3031
-
3032
- // if indentAllLines is set to true
3033
- // we're not resetting the indentation for this paragraph after the first line
3034
2813
  if (options.indentAllLines) {
3035
2814
  return;
3036
2815
  }
3037
-
3038
- // otherwise we start the next line without indent
3039
- return this.once('line', () => {
2816
+ this.once('line', () => {
3040
2817
  this.document.x -= indent;
3041
2818
  this.lineWidth += indent;
3042
2819
  if (options.continued && !this.continuedX) {
3043
2820
  this.continuedX = this.indent;
3044
2821
  }
3045
2822
  if (!options.continued) {
3046
- return this.continuedX = 0;
2823
+ this.continuedX = 0;
3047
2824
  }
3048
2825
  });
3049
2826
  });
3050
-
3051
- // handle left aligning last lines of paragraphs
3052
2827
  this.on('lastLine', options => {
3053
2828
  const {
3054
2829
  align
@@ -3057,7 +2832,7 @@ class LineWrapper extends EventEmitter {
3057
2832
  options.align = 'left';
3058
2833
  }
3059
2834
  this.lastLine = true;
3060
- return this.once('line', () => {
2835
+ this.once('line', () => {
3061
2836
  this.document.y += options.paragraphGap || 0;
3062
2837
  options.align = align;
3063
2838
  return this.lastLine = false;
@@ -3065,7 +2840,7 @@ class LineWrapper extends EventEmitter {
3065
2840
  });
3066
2841
  }
3067
2842
  wordWidth(word) {
3068
- return this.document.widthOfString(word, this) + this.characterSpacing + this.wordSpacing;
2843
+ return PDFNumber(this.document.widthOfString(word, this) + this.characterSpacing + this.wordSpacing);
3069
2844
  }
3070
2845
  canFit(word, w) {
3071
2846
  if (word[word.length - 1] != SOFT_HYPHEN) {
@@ -3074,7 +2849,6 @@ class LineWrapper extends EventEmitter {
3074
2849
  return w + this.wordWidth(HYPHEN) <= this.spaceLeft;
3075
2850
  }
3076
2851
  eachWord(text, fn) {
3077
- // setup a unicode line breaker
3078
2852
  let bk;
3079
2853
  const breaker = new LineBreaker(text);
3080
2854
  let last = null;
@@ -3083,19 +2857,12 @@ class LineWrapper extends EventEmitter {
3083
2857
  var shouldContinue;
3084
2858
  let word = text.slice((last != null ? last.position : undefined) || 0, bk.position);
3085
2859
  let w = wordWidths[word] != null ? wordWidths[word] : wordWidths[word] = this.wordWidth(word);
3086
-
3087
- // if the word is longer than the whole line, chop it up
3088
- // TODO: break by grapheme clusters, not JS string characters
3089
2860
  if (w > this.lineWidth + this.continuedX) {
3090
- // make some fake break objects
3091
2861
  let lbk = last;
3092
2862
  const fbk = {};
3093
2863
  while (word.length) {
3094
- // fit as much of the word as possible into the space we have
3095
2864
  var l, mightGrow;
3096
2865
  if (w > this.spaceLeft) {
3097
- // start our check at the end of our available space - this method is faster than a loop of each character and it resolves
3098
- // an issue with long loops when processing massive words, such as a huge number of spaces
3099
2866
  l = Math.ceil(this.spaceLeft / (w / word.length));
3100
2867
  w = this.wordWidth(word.slice(0, l));
3101
2868
  mightGrow = w <= this.spaceLeft && l < word.length;
@@ -3103,7 +2870,6 @@ class LineWrapper extends EventEmitter {
3103
2870
  l = word.length;
3104
2871
  }
3105
2872
  let mustShrink = w > this.spaceLeft && l > 0;
3106
- // shrink or grow word as necessary after our near-guess above
3107
2873
  while (mustShrink || mightGrow) {
3108
2874
  if (mustShrink) {
3109
2875
  w = this.wordWidth(word.slice(0, --l));
@@ -3114,20 +2880,14 @@ class LineWrapper extends EventEmitter {
3114
2880
  mightGrow = w <= this.spaceLeft && l < word.length;
3115
2881
  }
3116
2882
  }
3117
-
3118
- // check for the edge case where a single character cannot fit into a line.
3119
2883
  if (l === 0 && this.spaceLeft === this.lineWidth) {
3120
2884
  l = 1;
3121
2885
  }
3122
-
3123
- // send a required break unless this is the last piece and a linebreak is not specified
3124
2886
  fbk.required = bk.required || l < word.length;
3125
2887
  shouldContinue = fn(word.slice(0, l), w, fbk, lbk);
3126
2888
  lbk = {
3127
2889
  required: false
3128
2890
  };
3129
-
3130
- // get the remaining piece of the word
3131
2891
  word = word.slice(l);
3132
2892
  w = this.wordWidth(word);
3133
2893
  if (shouldContinue === false) {
@@ -3135,7 +2895,6 @@ class LineWrapper extends EventEmitter {
3135
2895
  }
3136
2896
  }
3137
2897
  } else {
3138
- // otherwise just emit the break as it was given to us
3139
2898
  shouldContinue = fn(word, w, bk, last);
3140
2899
  }
3141
2900
  if (shouldContinue === false) {
@@ -3145,7 +2904,6 @@ class LineWrapper extends EventEmitter {
3145
2904
  }
3146
2905
  }
3147
2906
  wrap(text, options) {
3148
- // override options from previous continued fragments
3149
2907
  this.horizontalScaling = options.horizontalScaling || 100;
3150
2908
  if (options.indent != null) {
3151
2909
  this.indent = options.indent * this.horizontalScaling / 100;
@@ -3159,10 +2917,6 @@ class LineWrapper extends EventEmitter {
3159
2917
  if (options.ellipsis != null) {
3160
2918
  this.ellipsis = options.ellipsis;
3161
2919
  }
3162
-
3163
- // make sure we're actually on the page
3164
- // and that the first line of is never by
3165
- // itself at the bottom of a page (orphans)
3166
2920
  const nextY = this.document.y + this.document.currentLineHeight(true);
3167
2921
  if (this.document.y > this.maxY || nextY > this.maxY) {
3168
2922
  this.nextSection();
@@ -3173,7 +2927,7 @@ class LineWrapper extends EventEmitter {
3173
2927
  let lc = 0;
3174
2928
  let {
3175
2929
  y
3176
- } = this.document; // used to reset Y pos if options.continued (below)
2930
+ } = this.document;
3177
2931
  const emitLine = () => {
3178
2932
  options.textWidth = textWidth + this.wordSpacing * (wc - 1);
3179
2933
  options.wordCount = wc;
@@ -3196,23 +2950,17 @@ class LineWrapper extends EventEmitter {
3196
2950
  wc++;
3197
2951
  }
3198
2952
  if (bk.required || !this.canFit(word, w)) {
3199
- // if the user specified a max height and an ellipsis, and is about to pass the
3200
- // max height and max columns after the next line, append the ellipsis
3201
2953
  const lh = this.document.currentLineHeight(true);
3202
- if (this.height != null && this.ellipsis && this.document.y + lh * 2 > this.maxY && this.column >= this.columns) {
2954
+ if (this.height != null && this.ellipsis && PDFNumber(this.document.y + lh * 2) > this.maxY && this.column >= this.columns) {
3203
2955
  if (this.ellipsis === true) {
3204
2956
  this.ellipsis = '…';
3205
- } // map default ellipsis character
2957
+ }
3206
2958
  buffer = buffer.replace(/\s+$/, '');
3207
2959
  textWidth = this.wordWidth(buffer + this.ellipsis);
3208
-
3209
- // remove characters from the buffer until the ellipsis fits
3210
- // to avoid infinite loop need to stop while-loop if buffer is empty string
3211
2960
  while (buffer && textWidth > this.lineWidth) {
3212
2961
  buffer = buffer.slice(0, -1).replace(/\s+$/, '');
3213
2962
  textWidth = this.wordWidth(buffer + this.ellipsis);
3214
2963
  }
3215
- // need to add ellipsis only if there is enough space for it
3216
2964
  if (textWidth <= this.lineWidth) {
3217
2965
  buffer = buffer + this.ellipsis;
3218
2966
  }
@@ -3227,35 +2975,25 @@ class LineWrapper extends EventEmitter {
3227
2975
  }
3228
2976
  this.emit('lastLine', options, this);
3229
2977
  }
3230
-
3231
- // Previous entry is a soft hyphen - add visible hyphen.
3232
2978
  if (buffer[buffer.length - 1] == SOFT_HYPHEN) {
3233
2979
  buffer = buffer.slice(0, -1) + HYPHEN;
3234
2980
  this.spaceLeft -= this.wordWidth(HYPHEN);
3235
2981
  }
3236
2982
  emitLine();
3237
-
3238
- // if we've reached the edge of the page,
3239
- // continue on a new page or column
3240
- if (this.document.y + lh > this.maxY) {
2983
+ if (PDFNumber(this.document.y + lh) > this.maxY) {
3241
2984
  const shouldContinue = this.nextSection();
3242
-
3243
- // stop if we reached the maximum height
3244
2985
  if (!shouldContinue) {
3245
2986
  wc = 0;
3246
2987
  buffer = '';
3247
2988
  return false;
3248
2989
  }
3249
2990
  }
3250
-
3251
- // reset the space left and buffer
3252
2991
  if (bk.required) {
3253
2992
  this.spaceLeft = this.lineWidth;
3254
2993
  buffer = '';
3255
2994
  textWidth = 0;
3256
2995
  return wc = 0;
3257
2996
  } else {
3258
- // reset the space left and buffer
3259
2997
  this.spaceLeft = this.lineWidth - w;
3260
2998
  buffer = word;
3261
2999
  textWidth = w;
@@ -3270,25 +3008,19 @@ class LineWrapper extends EventEmitter {
3270
3008
  emitLine();
3271
3009
  }
3272
3010
  this.emit('sectionEnd', options, this);
3273
-
3274
- // if the wrap is set to be continued, save the X position
3275
- // to start the first line of the next segment at, and reset
3276
- // the y position
3277
3011
  if (options.continued === true) {
3278
3012
  if (lc > 1) {
3279
3013
  this.continuedX = 0;
3280
3014
  }
3281
3015
  this.continuedX += options.textWidth || 0;
3282
- return this.document.y = y;
3016
+ this.document.y = y;
3283
3017
  } else {
3284
- return this.document.x = this.startX;
3018
+ this.document.x = this.startX;
3285
3019
  }
3286
3020
  }
3287
3021
  nextSection(options) {
3288
3022
  this.emit('sectionEnd', options, this);
3289
3023
  if (++this.column > this.columns) {
3290
- // if a max height was specified by the user, we're done.
3291
- // otherwise, the default is to make a new page at the bottom.
3292
3024
  if (this.height != null) {
3293
3025
  return false;
3294
3026
  }
@@ -3317,10 +3049,9 @@ const {
3317
3049
  var TextMixin = {
3318
3050
  initText() {
3319
3051
  this._line = this._line.bind(this);
3320
- // Current coordinates
3321
3052
  this.x = 0;
3322
3053
  this.y = 0;
3323
- return this._lineGap = 0;
3054
+ this._lineGap = 0;
3324
3055
  },
3325
3056
  lineGap(_lineGap) {
3326
3057
  this._lineGap = _lineGap;
@@ -3342,11 +3073,7 @@ var TextMixin = {
3342
3073
  },
3343
3074
  _text(text, x, y, options, lineCallback) {
3344
3075
  options = this._initOptions(x, y, options);
3345
-
3346
- // Convert text to a string
3347
3076
  text = text == null ? '' : `${text}`;
3348
-
3349
- // if the wordSpacing option is specified, remove multiple consecutive spaces
3350
3077
  if (options.wordSpacing) {
3351
3078
  text = text.replace(/\s{2,}/g, ' ');
3352
3079
  }
@@ -3355,8 +3082,12 @@ var TextMixin = {
3355
3082
  options.structParent.add(this.struct(options.structType || 'P', [this.markStructureContent(options.structType || 'P')]));
3356
3083
  }
3357
3084
  };
3358
-
3359
- // word wrapping
3085
+ if (options.rotation !== 0) {
3086
+ this.save();
3087
+ this.rotate(-options.rotation, {
3088
+ origin: [this.x, this.y]
3089
+ });
3090
+ }
3360
3091
  if (options.width) {
3361
3092
  let wrapper = this._wrapper;
3362
3093
  if (!wrapper) {
@@ -3367,14 +3098,13 @@ var TextMixin = {
3367
3098
  this._wrapper = options.continued ? wrapper : null;
3368
3099
  this._textOptions = options.continued ? options : null;
3369
3100
  wrapper.wrap(text, options);
3370
-
3371
- // render paragraphs as single lines
3372
3101
  } else {
3373
3102
  for (let line of text.split('\n')) {
3374
3103
  addStructure();
3375
3104
  lineCallback(line, options);
3376
3105
  }
3377
3106
  }
3107
+ if (options.rotation !== 0) this.restore();
3378
3108
  return this;
3379
3109
  },
3380
3110
  text(text, x, y, options) {
@@ -3385,17 +3115,108 @@ var TextMixin = {
3385
3115
  const horizontalScaling = options.horizontalScaling || 100;
3386
3116
  return (this._font.widthOfString(string, this._fontSize, options.features) + (options.characterSpacing || 0) * (string.length - 1)) * horizontalScaling / 100;
3387
3117
  },
3118
+ boundsOfString(string, x, y, options) {
3119
+ options = this._initOptions(x, y, options);
3120
+ ({
3121
+ x,
3122
+ y
3123
+ } = this);
3124
+ const lineGap = options.lineGap ?? this._lineGap ?? 0;
3125
+ const lineHeight = this.currentLineHeight(true) + lineGap;
3126
+ let contentWidth = 0;
3127
+ string = String(string ?? '');
3128
+ if (options.wordSpacing) {
3129
+ string = string.replace(/\s{2,}/g, ' ');
3130
+ }
3131
+ if (options.width) {
3132
+ let wrapper = new LineWrapper(this, options);
3133
+ wrapper.on('line', (text, options) => {
3134
+ this.y += lineHeight;
3135
+ text = text.replace(/\n/g, '');
3136
+ if (text.length) {
3137
+ let wordSpacing = options.wordSpacing ?? 0;
3138
+ const characterSpacing = options.characterSpacing ?? 0;
3139
+ if (options.width && options.align === 'justify') {
3140
+ const words = text.trim().split(/\s+/);
3141
+ const textWidth = this.widthOfString(text.replace(/\s+/g, ''), options);
3142
+ const spaceWidth = this.widthOfString(' ') + characterSpacing;
3143
+ wordSpacing = Math.max(0, (options.lineWidth - textWidth) / Math.max(1, words.length - 1) - spaceWidth);
3144
+ }
3145
+ contentWidth = Math.max(contentWidth, options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1));
3146
+ }
3147
+ });
3148
+ wrapper.wrap(string, options);
3149
+ } else {
3150
+ for (let line of string.split('\n')) {
3151
+ const lineWidth = this.widthOfString(line, options);
3152
+ this.y += lineHeight;
3153
+ contentWidth = Math.max(contentWidth, lineWidth);
3154
+ }
3155
+ }
3156
+ let contentHeight = this.y - y;
3157
+ if (options.height) contentHeight = Math.min(contentHeight, options.height);
3158
+ this.x = x;
3159
+ this.y = y;
3160
+ if (options.rotation === 0) {
3161
+ return {
3162
+ x,
3163
+ y,
3164
+ width: contentWidth,
3165
+ height: contentHeight
3166
+ };
3167
+ } else if (options.rotation === 90) {
3168
+ return {
3169
+ x: x,
3170
+ y: y - contentWidth,
3171
+ width: contentHeight,
3172
+ height: contentWidth
3173
+ };
3174
+ } else if (options.rotation === 180) {
3175
+ return {
3176
+ x: x - contentWidth,
3177
+ y: y - contentHeight,
3178
+ width: contentWidth,
3179
+ height: contentHeight
3180
+ };
3181
+ } else if (options.rotation === 270) {
3182
+ return {
3183
+ x: x - contentHeight,
3184
+ y: y,
3185
+ width: contentHeight,
3186
+ height: contentWidth
3187
+ };
3188
+ }
3189
+ const cos = cosine(options.rotation);
3190
+ const sin = sine(options.rotation);
3191
+ const x1 = x;
3192
+ const y1 = y;
3193
+ const x2 = x + contentWidth * cos;
3194
+ const y2 = y - contentWidth * sin;
3195
+ const x3 = x + contentWidth * cos + contentHeight * sin;
3196
+ const y3 = y - contentWidth * sin + contentHeight * cos;
3197
+ const x4 = x + contentHeight * sin;
3198
+ const y4 = y + contentHeight * cos;
3199
+ const xMin = Math.min(x1, x2, x3, x4);
3200
+ const xMax = Math.max(x1, x2, x3, x4);
3201
+ const yMin = Math.min(y1, y2, y3, y4);
3202
+ const yMax = Math.max(y1, y2, y3, y4);
3203
+ return {
3204
+ x: xMin,
3205
+ y: yMin,
3206
+ width: xMax - xMin,
3207
+ height: yMax - yMin
3208
+ };
3209
+ },
3388
3210
  heightOfString(text, options) {
3389
3211
  const {
3390
3212
  x,
3391
3213
  y
3392
3214
  } = this;
3393
3215
  options = this._initOptions(options);
3394
- options.height = Infinity; // don't break pages
3395
-
3216
+ options.height = Infinity;
3396
3217
  const lineGap = options.lineGap || this._lineGap || 0;
3397
3218
  this._text(text, this.x, this.y, options, () => {
3398
- return this.y += this.currentLineHeight(true) + lineGap;
3219
+ this.y += this.currentLineHeight(true) + lineGap;
3399
3220
  });
3400
3221
  const height = this.y - y;
3401
3222
  this.x = x;
@@ -3493,12 +3314,12 @@ var TextMixin = {
3493
3314
  wrapper.on('sectionStart', () => {
3494
3315
  const pos = indent + itemIndent * (level - 1);
3495
3316
  this.x += pos;
3496
- return wrapper.lineWidth -= pos;
3317
+ wrapper.lineWidth -= pos;
3497
3318
  });
3498
3319
  wrapper.on('sectionEnd', () => {
3499
3320
  const pos = indent + itemIndent * (level - 1);
3500
3321
  this.x -= pos;
3501
- return wrapper.lineWidth += pos;
3322
+ wrapper.lineWidth += pos;
3502
3323
  });
3503
3324
  wrapper.wrap(listItem, options);
3504
3325
  };
@@ -3515,11 +3336,7 @@ var TextMixin = {
3515
3336
  options = x;
3516
3337
  x = null;
3517
3338
  }
3518
-
3519
- // clone options object
3520
3339
  const result = Object.assign({}, options);
3521
-
3522
- // extend options with previous values for continued text
3523
3340
  if (this._textOptions) {
3524
3341
  for (let key in this._textOptions) {
3525
3342
  const val = this._textOptions[key];
@@ -3530,16 +3347,12 @@ var TextMixin = {
3530
3347
  }
3531
3348
  }
3532
3349
  }
3533
-
3534
- // Update the current position
3535
3350
  if (x != null) {
3536
3351
  this.x = x;
3537
3352
  }
3538
3353
  if (y != null) {
3539
3354
  this.y = y;
3540
3355
  }
3541
-
3542
- // wrap to margins if no x or y position passed
3543
3356
  if (result.lineBreak !== false) {
3544
3357
  if (result.width == null) {
3545
3358
  result.width = this.page.width - this.x - this.page.margins.right;
@@ -3551,8 +3364,9 @@ var TextMixin = {
3551
3364
  }
3552
3365
  if (result.columnGap == null) {
3553
3366
  result.columnGap = 18;
3554
- } // 1/4 inch
3555
-
3367
+ }
3368
+ result.rotation = Number(options.rotation ?? 0) % 360;
3369
+ if (result.rotation < 0) result.rotation += 360;
3556
3370
  return result;
3557
3371
  },
3558
3372
  _line(text) {
@@ -3561,9 +3375,9 @@ var TextMixin = {
3561
3375
  this._fragment(text, this.x, this.y, options);
3562
3376
  const lineGap = options.lineGap || this._lineGap || 0;
3563
3377
  if (!wrapper) {
3564
- return this.x += this.widthOfString(text, options);
3378
+ this.x += this.widthOfString(text, options);
3565
3379
  } else {
3566
- return this.y += this.currentLineHeight(true) + lineGap;
3380
+ this.y += this.currentLineHeight(true) + lineGap;
3567
3381
  }
3568
3382
  },
3569
3383
  _fragment(text, x, y, options) {
@@ -3572,14 +3386,10 @@ var TextMixin = {
3572
3386
  if (text.length === 0) {
3573
3387
  return;
3574
3388
  }
3575
-
3576
- // handle options
3577
3389
  const align = options.align || 'left';
3578
3390
  let wordSpacing = options.wordSpacing || 0;
3579
3391
  const characterSpacing = options.characterSpacing || 0;
3580
3392
  const horizontalScaling = options.horizontalScaling || 100;
3581
-
3582
- // text alignments
3583
3393
  if (options.width) {
3584
3394
  switch (align) {
3585
3395
  case 'right':
@@ -3590,7 +3400,6 @@ var TextMixin = {
3590
3400
  x += options.lineWidth / 2 - options.textWidth / 2;
3591
3401
  break;
3592
3402
  case 'justify':
3593
- // calculate the word spacing value
3594
3403
  words = text.trim().split(/\s+/);
3595
3404
  textWidth = this.widthOfString(text.replace(/\s+/g, ''), options);
3596
3405
  var spaceWidth = this.widthOfString(' ') + characterSpacing;
@@ -3598,8 +3407,6 @@ var TextMixin = {
3598
3407
  break;
3599
3408
  }
3600
3409
  }
3601
-
3602
- // text baseline alignments based on http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
3603
3410
  if (typeof options.baseline === 'number') {
3604
3411
  dy = -options.baseline;
3605
3412
  } else {
@@ -3632,11 +3439,7 @@ var TextMixin = {
3632
3439
  }
3633
3440
  dy = dy / 1000 * this._fontSize;
3634
3441
  }
3635
-
3636
- // calculate the actual rendered width of the string after word and character spacing
3637
3442
  const renderedWidth = options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1);
3638
-
3639
- // create link annotations if the link option is given
3640
3443
  if (options.link != null) {
3641
3444
  this.link(x, y, renderedWidth, this.currentLineHeight(), options.link);
3642
3445
  }
@@ -3646,8 +3449,6 @@ var TextMixin = {
3646
3449
  if (options.destination != null) {
3647
3450
  this.addNamedDestination(options.destination, 'XYZ', x, y, null);
3648
3451
  }
3649
-
3650
- // create underline
3651
3452
  if (options.underline) {
3652
3453
  this.save();
3653
3454
  if (!options.stroke) {
@@ -3661,8 +3462,6 @@ var TextMixin = {
3661
3462
  this.stroke();
3662
3463
  this.restore();
3663
3464
  }
3664
-
3665
- // create strikethrough line
3666
3465
  if (options.strike) {
3667
3466
  this.save();
3668
3467
  if (!options.stroke) {
@@ -3677,8 +3476,6 @@ var TextMixin = {
3677
3476
  this.restore();
3678
3477
  }
3679
3478
  this.save();
3680
-
3681
- // oblique (angle in degrees or boolean)
3682
3479
  if (options.oblique) {
3683
3480
  let skew;
3684
3481
  if (typeof options.oblique === 'number') {
@@ -3690,45 +3487,24 @@ var TextMixin = {
3690
3487
  this.transform(1, 0, skew, 1, -skew * dy, 0);
3691
3488
  this.transform(1, 0, 0, 1, -x, -y);
3692
3489
  }
3693
-
3694
- // flip coordinate system
3695
3490
  this.transform(1, 0, 0, -1, 0, this.page.height);
3696
3491
  y = this.page.height - y - dy;
3697
-
3698
- // add current font to page if necessary
3699
3492
  if (this.page.fonts[this._font.id] == null) {
3700
3493
  this.page.fonts[this._font.id] = this._font.ref();
3701
3494
  }
3702
-
3703
- // begin the text object
3704
3495
  this.addContent('BT');
3705
-
3706
- // text position
3707
3496
  this.addContent(`1 0 0 1 ${number(x)} ${number(y)} Tm`);
3708
-
3709
- // font and font size
3710
3497
  this.addContent(`/${this._font.id} ${number(this._fontSize)} Tf`);
3711
-
3712
- // rendering mode
3713
3498
  const mode = options.fill && options.stroke ? 2 : options.stroke ? 1 : 0;
3714
3499
  if (mode) {
3715
3500
  this.addContent(`${mode} Tr`);
3716
3501
  }
3717
-
3718
- // Character spacing
3719
3502
  if (characterSpacing) {
3720
3503
  this.addContent(`${number(characterSpacing)} Tc`);
3721
3504
  }
3722
-
3723
- // Horizontal scaling
3724
3505
  if (horizontalScaling !== 100) {
3725
3506
  this.addContent(`${horizontalScaling} Tz`);
3726
3507
  }
3727
-
3728
- // Add the actual text
3729
- // If we have a word spacing value, we need to encode each word separately
3730
- // since the normal Tw operator only works on character code 32, which isn't
3731
- // used for embedded fonts.
3732
3508
  if (wordSpacing) {
3733
3509
  words = text.trim().split(/\s+/);
3734
3510
  wordSpacing += this.widthOfString(' ') + characterSpacing;
@@ -3739,9 +3515,6 @@ var TextMixin = {
3739
3515
  const [encodedWord, positionsWord] = this._font.encode(word, options.features);
3740
3516
  encoded = encoded.concat(encodedWord);
3741
3517
  positions = positions.concat(positionsWord);
3742
-
3743
- // add the word spacing to the end of the word
3744
- // clone object because of cache
3745
3518
  const space = {};
3746
3519
  const object = positions[positions.length - 1];
3747
3520
  for (let key in object) {
@@ -3758,60 +3531,42 @@ var TextMixin = {
3758
3531
  const commands = [];
3759
3532
  let last = 0;
3760
3533
  let hadOffset = false;
3761
-
3762
- // Adds a segment of text to the TJ command buffer
3763
3534
  const addSegment = cur => {
3764
3535
  if (last < cur) {
3765
3536
  const hex = encoded.slice(last, cur).join('');
3766
3537
  const advance = positions[cur - 1].xAdvance - positions[cur - 1].advanceWidth;
3767
3538
  commands.push(`<${hex}> ${number(-advance)}`);
3768
3539
  }
3769
- return last = cur;
3540
+ last = cur;
3770
3541
  };
3771
-
3772
- // Flushes the current TJ commands to the output stream
3773
3542
  const flush = i => {
3774
3543
  addSegment(i);
3775
3544
  if (commands.length > 0) {
3776
3545
  this.addContent(`[${commands.join(' ')}] TJ`);
3777
- return commands.length = 0;
3546
+ commands.length = 0;
3778
3547
  }
3779
3548
  };
3780
3549
  for (i = 0; i < positions.length; i++) {
3781
- // If we have an x or y offset, we have to break out of the current TJ command
3782
- // so we can move the text position.
3783
3550
  const pos = positions[i];
3784
3551
  if (pos.xOffset || pos.yOffset) {
3785
- // Flush the current buffer
3786
3552
  flush(i);
3787
-
3788
- // Move the text position and flush just the current character
3789
3553
  this.addContent(`1 0 0 1 ${number(x + pos.xOffset * scale)} ${number(y + pos.yOffset * scale)} Tm`);
3790
3554
  flush(i + 1);
3791
3555
  hadOffset = true;
3792
3556
  } else {
3793
- // If the last character had an offset, reset the text position
3794
3557
  if (hadOffset) {
3795
3558
  this.addContent(`1 0 0 1 ${number(x)} ${number(y)} Tm`);
3796
3559
  hadOffset = false;
3797
3560
  }
3798
-
3799
- // Group segments that don't have any advance adjustments
3800
3561
  if (pos.xAdvance - pos.advanceWidth !== 0) {
3801
3562
  addSegment(i + 1);
3802
3563
  }
3803
3564
  }
3804
3565
  x += pos.xAdvance * scale;
3805
3566
  }
3806
-
3807
- // Flush any remaining commands
3808
3567
  flush(i);
3809
-
3810
- // end the text object
3811
3568
  this.addContent('ET');
3812
-
3813
- // restore flipped coordinate system
3814
- return this.restore();
3569
+ this.restore();
3815
3570
  }
3816
3571
  };
3817
3572
 
@@ -3829,8 +3584,6 @@ class JPEG {
3829
3584
  if (this.data.readUInt16BE(0) !== 0xffd8) {
3830
3585
  throw 'SOI not found in JPEG';
3831
3586
  }
3832
-
3833
- // Parse the EXIF orientation
3834
3587
  this.orientation = exif.fromBuffer(this.data).Orientation || 1;
3835
3588
  let pos = 2;
3836
3589
  while (pos < this.data.length) {
@@ -3867,16 +3620,10 @@ class JPEG {
3867
3620
  ColorSpace: this.colorSpace,
3868
3621
  Filter: 'DCTDecode'
3869
3622
  });
3870
-
3871
- // add extra decode params for CMYK images. By swapping the
3872
- // min and max values from the default, we invert the colors. See
3873
- // section 4.8.4 of the spec.
3874
3623
  if (this.colorSpace === 'DeviceCMYK') {
3875
3624
  this.obj.data['Decode'] = [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0];
3876
3625
  }
3877
3626
  this.obj.end(this.data);
3878
-
3879
- // free memory
3880
3627
  return this.data = null;
3881
3628
  }
3882
3629
  }
@@ -3919,24 +3666,14 @@ class PNGImage {
3919
3666
  if (this.image.palette.length === 0) {
3920
3667
  this.obj.data['ColorSpace'] = this.image.colorSpace;
3921
3668
  } else {
3922
- // embed the color palette in the PDF as an object stream
3923
3669
  const palette = this.document.ref();
3924
3670
  palette.end(Buffer.from(this.image.palette));
3925
-
3926
- // build the color space array for the image
3927
3671
  this.obj.data['ColorSpace'] = ['Indexed', 'DeviceRGB', this.image.palette.length / 3 - 1, palette];
3928
3672
  }
3929
-
3930
- // For PNG color types 0, 2 and 3, the transparency data is stored in
3931
- // a dedicated PNG chunk.
3932
3673
  if (this.image.transparency.grayscale != null) {
3933
- // Use Color Key Masking (spec section 4.8.5)
3934
- // An array with N elements, where N is two times the number of color components.
3935
3674
  const val = this.image.transparency.grayscale;
3936
3675
  this.obj.data['Mask'] = [val, val];
3937
3676
  } else if (this.image.transparency.rgb) {
3938
- // Use Color Key Masking (spec section 4.8.5)
3939
- // An array with N elements, where N is two times the number of color components.
3940
3677
  const {
3941
3678
  rgb
3942
3679
  } = this.image.transparency;
@@ -3946,14 +3683,9 @@ class PNGImage {
3946
3683
  }
3947
3684
  this.obj.data['Mask'] = mask;
3948
3685
  } else if (this.image.transparency.indexed) {
3949
- // Create a transparency SMask for the image based on the data
3950
- // in the PLTE and tRNS sections. See below for details on SMasks.
3951
3686
  dataDecoded = true;
3952
3687
  return this.loadIndexedAlphaChannel();
3953
3688
  } else if (hasAlphaChannel) {
3954
- // For PNG color types 4 and 6, the transparency data is stored as a alpha
3955
- // channel mixed in with the main image data. Separate this data out into an
3956
- // SMask object and store it separately in the PDF.
3957
3689
  dataDecoded = true;
3958
3690
  return this.splitAlphaChannel();
3959
3691
  }
@@ -3977,11 +3709,7 @@ class PNGImage {
3977
3709
  sMask.end(this.alphaChannel);
3978
3710
  this.obj.data['SMask'] = sMask;
3979
3711
  }
3980
-
3981
- // add the actual image data
3982
3712
  this.obj.end(this.imgData);
3983
-
3984
- // free memory
3985
3713
  this.image = null;
3986
3714
  return this.imgData = null;
3987
3715
  }
@@ -3994,7 +3722,6 @@ class PNGImage {
3994
3722
  const alphaChannel = Buffer.alloc(pixelCount);
3995
3723
  let i = p = a = 0;
3996
3724
  const len = pixels.length;
3997
- // For 16bit images copy only most significant byte (MSB) - PNG data is always stored in network byte order (MSB first)
3998
3725
  const skipByteCount = this.image.bits === 16 ? 1 : 0;
3999
3726
  while (i < len) {
4000
3727
  for (let colorIndex = 0; colorIndex < colorCount; colorIndex++) {
@@ -4029,10 +3756,6 @@ class PNGImage {
4029
3756
  }
4030
3757
  }
4031
3758
 
4032
- /*
4033
- PDFImage - embeds images in PDF documents
4034
- By Devon Govett
4035
- */
4036
3759
  class PDFImage {
4037
3760
  static open(src, label) {
4038
3761
  let data;
@@ -4041,8 +3764,8 @@ class PDFImage {
4041
3764
  } else if (src instanceof ArrayBuffer) {
4042
3765
  data = Buffer.from(new Uint8Array(src));
4043
3766
  } else {
4044
- let match;
4045
- if (match = /^data:.+?;base64,(.*)$/.exec(src)) {
3767
+ const match = /^data:.+?;base64,(.*)$/.exec(src);
3768
+ if (match) {
4046
3769
  data = Buffer.from(match[1], 'base64');
4047
3770
  } else {
4048
3771
  data = fs.readFileSync(src);
@@ -4064,18 +3787,17 @@ class PDFImage {
4064
3787
  var ImagesMixin = {
4065
3788
  initImages() {
4066
3789
  this._imageRegistry = {};
4067
- return this._imageCount = 0;
3790
+ this._imageCount = 0;
4068
3791
  },
4069
3792
  image(src, x, y) {
4070
3793
  let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
4071
- let bh, bp, bw, image, ip, left, left1, rotateAngle, originX, originY;
3794
+ let bh, bp, bw, image, ip, left, left1, originX, originY;
4072
3795
  if (typeof x === 'object') {
4073
3796
  options = x;
4074
3797
  x = null;
4075
3798
  }
4076
-
4077
- // Ignore orientation based on document options or image options
4078
3799
  const ignoreOrientation = options.ignoreOrientation || options.ignoreOrientation !== false && this.options.ignoreOrientation;
3800
+ const inDocumentFlow = typeof y !== 'number';
4079
3801
  x = (left = x != null ? x : options.x) != null ? left : this.x;
4080
3802
  y = (left1 = y != null ? y : options.y) != null ? left1 : this.y;
4081
3803
  if (typeof src === 'string') {
@@ -4098,8 +3820,6 @@ var ImagesMixin = {
4098
3820
  width,
4099
3821
  height
4100
3822
  } = image;
4101
-
4102
- // If EXIF orientation calls for it, swap width and height
4103
3823
  if (!ignoreOrientation && image.orientation > 4) {
4104
3824
  [width, height] = [height, width];
4105
3825
  }
@@ -4151,80 +3871,70 @@ var ImagesMixin = {
4151
3871
  y = y + bh - h;
4152
3872
  }
4153
3873
  }
3874
+ let rotateAngle = 0;
3875
+ let xTransform = x;
3876
+ let yTransform = y;
3877
+ let hTransform = h;
3878
+ let wTransform = w;
4154
3879
  if (!ignoreOrientation) {
4155
3880
  switch (image.orientation) {
4156
- // No orientation (need to flip image, though, because of the default transform matrix on the document)
4157
3881
  default:
4158
3882
  case 1:
4159
- h = -h;
4160
- y -= h;
4161
- rotateAngle = 0;
3883
+ hTransform = -h;
3884
+ yTransform += h;
4162
3885
  break;
4163
- // Flip Horizontal
4164
3886
  case 2:
4165
- w = -w;
4166
- h = -h;
4167
- x -= w;
4168
- y -= h;
4169
- rotateAngle = 0;
3887
+ wTransform = -w;
3888
+ hTransform = -h;
3889
+ xTransform += w;
3890
+ yTransform += h;
4170
3891
  break;
4171
- // Rotate 180 degrees
4172
3892
  case 3:
4173
3893
  originX = x;
4174
3894
  originY = y;
4175
- h = -h;
4176
- x -= w;
3895
+ hTransform = -h;
3896
+ xTransform -= w;
4177
3897
  rotateAngle = 180;
4178
3898
  break;
4179
- // Flip vertical
4180
3899
  case 4:
4181
- // Do nothing, image will be flipped
4182
-
4183
3900
  break;
4184
- // Flip horizontally and rotate 270 degrees CW
4185
3901
  case 5:
4186
3902
  originX = x;
4187
3903
  originY = y;
4188
- [w, h] = [h, w];
4189
- y -= h;
3904
+ wTransform = h;
3905
+ hTransform = w;
3906
+ yTransform -= hTransform;
4190
3907
  rotateAngle = 90;
4191
3908
  break;
4192
- // Rotate 90 degrees CW
4193
3909
  case 6:
4194
3910
  originX = x;
4195
3911
  originY = y;
4196
- [w, h] = [h, w];
4197
- h = -h;
3912
+ wTransform = h;
3913
+ hTransform = -w;
4198
3914
  rotateAngle = 90;
4199
3915
  break;
4200
- // Flip horizontally and rotate 90 degrees CW
4201
3916
  case 7:
4202
3917
  originX = x;
4203
3918
  originY = y;
4204
- [w, h] = [h, w];
4205
- h = -h;
4206
- w = -w;
4207
- x -= w;
3919
+ hTransform = -w;
3920
+ wTransform = -h;
3921
+ xTransform += h;
4208
3922
  rotateAngle = 90;
4209
3923
  break;
4210
- // Rotate 270 degrees CW
4211
3924
  case 8:
4212
3925
  originX = x;
4213
3926
  originY = y;
4214
- [w, h] = [h, w];
4215
- h = -h;
4216
- x -= w;
4217
- y -= h;
3927
+ wTransform = h;
3928
+ hTransform = -w;
3929
+ xTransform -= h;
3930
+ yTransform += w;
4218
3931
  rotateAngle = -90;
4219
3932
  break;
4220
3933
  }
4221
3934
  } else {
4222
- h = -h;
4223
- y -= h;
4224
- rotateAngle = 0;
3935
+ hTransform = -h;
3936
+ yTransform += h;
4225
3937
  }
4226
-
4227
- // create link annotations if the link option is given
4228
3938
  if (options.link != null) {
4229
3939
  this.link(x, y, w, h, options.link);
4230
3940
  }
@@ -4234,9 +3944,7 @@ var ImagesMixin = {
4234
3944
  if (options.destination != null) {
4235
3945
  this.addNamedDestination(options.destination, 'XYZ', x, y, null);
4236
3946
  }
4237
-
4238
- // Set the current y position to below the image if it is in the document flow
4239
- if (this.y === y) {
3947
+ if (inDocumentFlow) {
4240
3948
  this.y += h;
4241
3949
  }
4242
3950
  this.save();
@@ -4245,7 +3953,7 @@ var ImagesMixin = {
4245
3953
  origin: [originX, originY]
4246
3954
  });
4247
3955
  }
4248
- this.transform(w, 0, 0, h, x, y);
3956
+ this.transform(wTransform, 0, 0, hTransform, xTransform, yTransform);
4249
3957
  this.addContent(`/${image.label} Do`);
4250
3958
  this.restore();
4251
3959
  return this;
@@ -4271,19 +3979,17 @@ var AnnotationsMixin = {
4271
3979
  options.Rect = this._convertRect(x, y, w, h);
4272
3980
  options.Border = [0, 0, 0];
4273
3981
  if (options.Subtype === 'Link' && typeof options.F === 'undefined') {
4274
- options.F = 1 << 2; // Print Annotation Flag
3982
+ options.F = 1 << 2;
4275
3983
  }
4276
3984
  if (options.Subtype !== 'Link') {
4277
3985
  if (options.C == null) {
4278
3986
  options.C = this._normalizeColor(options.color || [0, 0, 0]);
4279
3987
  }
4280
- } // convert colors
3988
+ }
4281
3989
  delete options.color;
4282
3990
  if (typeof options.Dest === 'string') {
4283
3991
  options.Dest = new String(options.Dest);
4284
3992
  }
4285
-
4286
- // Capitalize keys
4287
3993
  for (let key in options) {
4288
3994
  const val = options[key];
4289
3995
  options[key[0].toUpperCase() + key.slice(1)] = val;
@@ -4319,7 +4025,6 @@ var AnnotationsMixin = {
4319
4025
  let options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
4320
4026
  options.Subtype = 'Link';
4321
4027
  if (typeof url === 'number') {
4322
- // Link to a page in the document (the page must already exist)
4323
4028
  const pages = this._root.data.Pages.data;
4324
4029
  if (url >= 0 && url < pages.Kids.length) {
4325
4030
  options.A = this.ref({
@@ -4331,7 +4036,6 @@ var AnnotationsMixin = {
4331
4036
  throw new Error(`The document has no page ${url}`);
4332
4037
  }
4333
4038
  } else {
4334
- // Link to an external url
4335
4039
  options.A = this.ref({
4336
4040
  S: 'URI',
4337
4041
  URI: new String(url)
@@ -4394,14 +4098,11 @@ var AnnotationsMixin = {
4394
4098
  fileAnnotation(x, y, w, h) {
4395
4099
  let file = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
4396
4100
  let options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
4397
- // create hidden file
4398
4101
  const filespec = this.file(file.src, Object.assign({
4399
4102
  hidden: true
4400
4103
  }, file));
4401
4104
  options.Subtype = 'FileAttachment';
4402
4105
  options.FS = filespec;
4403
-
4404
- // add description from filespec unless description (Contents) has already been set
4405
4106
  if (options.Contents) {
4406
4107
  options.Contents = new String(options.Contents);
4407
4108
  } else if (filespec.data.Desc) {
@@ -4410,14 +4111,9 @@ var AnnotationsMixin = {
4410
4111
  return this.annotate(x, y, w, h, options);
4411
4112
  },
4412
4113
  _convertRect(x1, y1, w, h) {
4413
- // flip y1 and y2
4414
4114
  let y2 = y1;
4415
4115
  y1 += h;
4416
-
4417
- // make x2
4418
4116
  let x2 = x1 + w;
4419
-
4420
- // apply current transformation matrix to points
4421
4117
  const [m0, m1, m2, m3, m4, m5] = this._ctm;
4422
4118
  x1 = m0 * x1 + m2 * y1 + m4;
4423
4119
  y1 = m1 * x1 + m3 * y1 + m5;
@@ -4481,7 +4177,7 @@ class PDFOutline {
4481
4177
 
4482
4178
  var OutlineMixin = {
4483
4179
  initOutline() {
4484
- return this.outline = new PDFOutline(this, null, null, null);
4180
+ this.outline = new PDFOutline(this, null, null, null);
4485
4181
  },
4486
4182
  endOutline() {
4487
4183
  this.outline.endOutline();
@@ -4492,11 +4188,6 @@ var OutlineMixin = {
4492
4188
  }
4493
4189
  };
4494
4190
 
4495
- /*
4496
- PDFStructureContent - a reference to a marked structure content
4497
- By Ben Schmidt
4498
- */
4499
-
4500
4191
  class PDFStructureContent {
4501
4192
  constructor(pageRef, mcid) {
4502
4193
  this.refs = [{
@@ -4509,10 +4200,6 @@ class PDFStructureContent {
4509
4200
  }
4510
4201
  }
4511
4202
 
4512
- /*
4513
- PDFStructureElement - represents an element in the PDF logical structure tree
4514
- By Ben Schmidt
4515
- */
4516
4203
  class PDFStructureElement {
4517
4204
  constructor(document, type) {
4518
4205
  let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
@@ -4522,7 +4209,6 @@ class PDFStructureElement {
4522
4209
  this._ended = false;
4523
4210
  this._flushed = false;
4524
4211
  this.dictionary = document.ref({
4525
- // Type: "StructElem",
4526
4212
  S: type
4527
4213
  });
4528
4214
  const data = this.dictionary.data;
@@ -4571,7 +4257,6 @@ class PDFStructureElement {
4571
4257
  this._addContentToParentTree(child);
4572
4258
  }
4573
4259
  if (typeof child === 'function' && this._attached) {
4574
- // _contentForClosure() adds the content to the parent tree
4575
4260
  child = this._contentForClosure(child);
4576
4261
  }
4577
4262
  this._children.push(child);
@@ -4648,10 +4333,6 @@ class PDFStructureElement {
4648
4333
  this.dictionary.data.K = [];
4649
4334
  this._children.forEach(child => this._flushChild(child));
4650
4335
  this.dictionary.end();
4651
-
4652
- // free memory used by children; the dictionary itself may still be
4653
- // referenced by a parent structure element or root, but we can
4654
- // at least trim the tree here
4655
4336
  this._children = [];
4656
4337
  this.dictionary.data.K = null;
4657
4338
  this._flushed = true;
@@ -4673,7 +4354,7 @@ class PDFStructureElement {
4673
4354
  this.dictionary.data.K.push(mcid);
4674
4355
  } else {
4675
4356
  this.dictionary.data.K.push({
4676
- Type: "MCR",
4357
+ Type: 'MCR',
4677
4358
  Pg: pageRef,
4678
4359
  MCID: mcid
4679
4360
  });
@@ -4683,25 +4364,18 @@ class PDFStructureElement {
4683
4364
  }
4684
4365
  }
4685
4366
 
4686
- /*
4687
- PDFNumberTree - represents a number tree object
4688
- */
4689
4367
  class PDFNumberTree extends PDFTree {
4690
4368
  _compareKeys(a, b) {
4691
4369
  return parseInt(a) - parseInt(b);
4692
4370
  }
4693
4371
  _keysName() {
4694
- return "Nums";
4372
+ return 'Nums';
4695
4373
  }
4696
4374
  _dataForKey(k) {
4697
4375
  return parseInt(k);
4698
4376
  }
4699
4377
  }
4700
4378
 
4701
- /*
4702
- Markings mixin - support marked content sequences in content streams
4703
- By Ben Schmidt
4704
- */
4705
4379
  var MarkingsMixin = {
4706
4380
  initMarkings(options) {
4707
4381
  this.structChildren = [];
@@ -4841,7 +4515,6 @@ var MarkingsMixin = {
4841
4515
  return this.getStructTreeRoot().data.ParentTree;
4842
4516
  },
4843
4517
  createStructParentTreeNextKey() {
4844
- // initialise the MarkInfo dictionary
4845
4518
  this.getMarkInfoDictionary();
4846
4519
  const structTreeRoot = this.getStructTreeRoot();
4847
4520
  const key = structTreeRoot.data.ParentTreeNextKey++;
@@ -4905,10 +4578,6 @@ const FORMAT_DEFAULT = {
4905
4578
  }
4906
4579
  };
4907
4580
  var AcroFormMixin = {
4908
- /**
4909
- * Must call if adding AcroForms to a document. Must also call font() before
4910
- * this method to set the default font.
4911
- */
4912
4581
  initForm() {
4913
4582
  if (!this._font) {
4914
4583
  throw new Error('Must set a font before calling initForm method');
@@ -4931,9 +4600,6 @@ var AcroFormMixin = {
4931
4600
  this._root.data.AcroForm = AcroForm;
4932
4601
  return this;
4933
4602
  },
4934
- /**
4935
- * Called automatically by document.js
4936
- */
4937
4603
  endAcroForm() {
4938
4604
  if (this._root.data.AcroForm) {
4939
4605
  if (!Object.keys(this._acroform.fonts).length && !this._acroform.defaultFont) {
@@ -4959,13 +4625,6 @@ var AcroFormMixin = {
4959
4625
  }
4960
4626
  return this;
4961
4627
  },
4962
- /**
4963
- * Creates and adds a form field to the document. Form fields are intermediate
4964
- * nodes in a PDF form that are used to specify form name heirarchy and form
4965
- * value defaults.
4966
- * @param {string} name - field name (T attribute in field dictionary)
4967
- * @param {object} options - other attributes to include in field dictionary
4968
- */
4969
4628
  formField(name) {
4970
4629
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
4971
4630
  let fieldDict = this._fieldDict(name, null, options);
@@ -4973,26 +4632,13 @@ var AcroFormMixin = {
4973
4632
  this._addToParent(fieldRef);
4974
4633
  return fieldRef;
4975
4634
  },
4976
- /**
4977
- * Creates and adds a Form Annotation to the document. Form annotations are
4978
- * called Widget annotations internally within a PDF file.
4979
- * @param {string} name - form field name (T attribute of widget annotation
4980
- * dictionary)
4981
- * @param {number} x
4982
- * @param {number} y
4983
- * @param {number} w
4984
- * @param {number} h
4985
- * @param {object} options
4986
- */
4987
4635
  formAnnotation(name, type, x, y, w, h) {
4988
4636
  let options = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {};
4989
4637
  let fieldDict = this._fieldDict(name, type, options);
4990
4638
  fieldDict.Subtype = 'Widget';
4991
4639
  if (fieldDict.F === undefined) {
4992
- fieldDict.F = 4; // print the annotation
4640
+ fieldDict.F = 4;
4993
4641
  }
4994
-
4995
- // Add Field annot to page, and get it's ref
4996
4642
  this.annotate(x, y, w, h, fieldDict);
4997
4643
  let annotRef = this.page.annotations[this.page.annotations.length - 1];
4998
4644
  return this._addToParent(annotRef);
@@ -5160,23 +4806,18 @@ var AcroFormMixin = {
5160
4806
  delete options.align;
5161
4807
  }
5162
4808
  if (result !== 0) {
5163
- options.Q = result; // default
4809
+ options.Q = result;
5164
4810
  }
5165
4811
  return options;
5166
4812
  },
5167
4813
  _resolveFont(options) {
5168
- // add current font to document-level AcroForm dict if necessary
5169
4814
  if (this._acroform.fonts[this._font.id] == null) {
5170
4815
  this._acroform.fonts[this._font.id] = this._font.ref();
5171
4816
  }
5172
-
5173
- // add current font to field's resource dict (RD) if not the default acroform font
5174
4817
  if (this._acroform.defaultFont !== this._font.name) {
5175
4818
  options.DR = {
5176
4819
  Font: {}
5177
4820
  };
5178
-
5179
- // Get the fontSize option. If not set use auto sizing
5180
4821
  const fontSize = options.fontSize || 0;
5181
4822
  options.DR.Font[this._font.id] = this._font.ref();
5182
4823
  options.DA = new String(`/${this._font.id} ${fontSize} Tf 0 g`);
@@ -5228,19 +4869,6 @@ var AcroFormMixin = {
5228
4869
  };
5229
4870
 
5230
4871
  var AttachmentsMixin = {
5231
- /**
5232
- * Embed contents of `src` in PDF
5233
- * @param {Buffer | ArrayBuffer | string} src input Buffer, ArrayBuffer, base64 encoded string or path to file
5234
- * @param {object} options
5235
- * * options.name: filename to be shown in PDF, will use `src` if none set
5236
- * * options.type: filetype to be shown in PDF
5237
- * * options.description: description to be shown in PDF
5238
- * * options.hidden: if true, do not add attachment to EmbeddedFiles dictionary. Useful for file attachment annotations
5239
- * * options.creationDate: override creation date
5240
- * * options.modifiedDate: override modified date
5241
- * * options.relationship: Relationship between the PDF document and its attached file. Can be 'Alternative', 'Data', 'Source', 'Supplement' or 'Unspecified'.
5242
- * @returns filespec reference
5243
- */
5244
4872
  file(src) {
5245
4873
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
5246
4874
  options.name = options.name || src;
@@ -5258,8 +4886,8 @@ var AttachmentsMixin = {
5258
4886
  } else if (src instanceof ArrayBuffer) {
5259
4887
  data = Buffer.from(new Uint8Array(src));
5260
4888
  } else {
5261
- let match;
5262
- if (match = /^data:(.*?);base64,(.*)$/.exec(src)) {
4889
+ const match = /^data:(.*?);base64,(.*)$/.exec(src);
4890
+ if (match) {
5263
4891
  if (match[1]) {
5264
4892
  refBody.Subtype = match[1].replace('/', '#2F');
5265
4893
  }
@@ -5269,8 +4897,6 @@ var AttachmentsMixin = {
5269
4897
  if (!data) {
5270
4898
  throw new Error(`Could not read contents of file at filepath ${src}`);
5271
4899
  }
5272
-
5273
- // update CreationDate and ModDate
5274
4900
  const {
5275
4901
  birthtime,
5276
4902
  ctime
@@ -5279,26 +4905,18 @@ var AttachmentsMixin = {
5279
4905
  refBody.Params.ModDate = ctime;
5280
4906
  }
5281
4907
  }
5282
-
5283
- // override creation date and modified date
5284
4908
  if (options.creationDate instanceof Date) {
5285
4909
  refBody.Params.CreationDate = options.creationDate;
5286
4910
  }
5287
4911
  if (options.modifiedDate instanceof Date) {
5288
4912
  refBody.Params.ModDate = options.modifiedDate;
5289
4913
  }
5290
- // add optional subtype
5291
4914
  if (options.type) {
5292
4915
  refBody.Subtype = options.type.replace('/', '#2F');
5293
4916
  }
5294
-
5295
- // add checksum and size information
5296
4917
  const checksum = CryptoJS.MD5(CryptoJS.lib.WordArray.create(new Uint8Array(data)));
5297
4918
  refBody.Params.CheckSum = new String(checksum);
5298
4919
  refBody.Params.Size = data.byteLength;
5299
-
5300
- // save some space when embedding the same file again
5301
- // if a file with the same name and metadata exists, reuse its reference
5302
4920
  let ref;
5303
4921
  if (!this._fileRegistry) this._fileRegistry = {};
5304
4922
  let file = this._fileRegistry[options.name];
@@ -5312,7 +4930,6 @@ var AttachmentsMixin = {
5312
4930
  ref
5313
4931
  };
5314
4932
  }
5315
- // add filespec for embedded file
5316
4933
  const fileSpecBody = {
5317
4934
  Type: 'Filespec',
5318
4935
  AFRelationship: options.relationship,
@@ -5330,8 +4947,6 @@ var AttachmentsMixin = {
5330
4947
  if (!options.hidden) {
5331
4948
  this.addNamedEmbeddedFile(options.name, filespec);
5332
4949
  }
5333
-
5334
- // Add file to the catalogue to be PDF/A3 compliant
5335
4950
  if (this._root.data.AF) {
5336
4951
  this._root.data.AF.push(filespec);
5337
4952
  } else {
@@ -5340,8 +4955,6 @@ var AttachmentsMixin = {
5340
4955
  return filespec;
5341
4956
  }
5342
4957
  };
5343
-
5344
- /** check two embedded file metadata objects for equality */
5345
4958
  function isEqual(a, b) {
5346
4959
  return a.Subtype === b.Subtype && a.Params.CheckSum.toString() === b.Params.CheckSum.toString() && a.Params.Size === b.Params.Size && a.Params.CreationDate.getTime() === b.Params.CreationDate.getTime() && (a.Params.ModDate === undefined && b.Params.ModDate === undefined || a.Params.ModDate.getTime() === b.Params.ModDate.getTime());
5347
4960
  }
@@ -5352,7 +4965,6 @@ var PDFA = {
5352
4965
  this.subset_conformance = pSubset.charAt(pSubset.length - 1).toUpperCase();
5353
4966
  this.subset = parseInt(pSubset.charAt(pSubset.length - 2));
5354
4967
  } else {
5355
- // Default to Basic conformance when user doesn't specify
5356
4968
  this.subset_conformance = 'B';
5357
4969
  this.subset = parseInt(pSubset.charAt(pSubset.length - 1));
5358
4970
  }
@@ -5437,6 +5049,656 @@ var SubsetMixin = {
5437
5049
  }
5438
5050
  };
5439
5051
 
5052
+ const ROW_FIELDS = ['height', 'minHeight', 'maxHeight'];
5053
+ const COLUMN_FIELDS = ['width', 'minWidth', 'maxWidth'];
5054
+ function memoize(fn, maxSize) {
5055
+ const cache = new Map();
5056
+ return function () {
5057
+ const key = arguments.length <= 0 ? undefined : arguments[0];
5058
+ if (!cache.has(key)) {
5059
+ cache.set(key, fn(...arguments));
5060
+ if (cache.size > maxSize) cache.delete(cache.keys().next());
5061
+ }
5062
+ return cache.get(key);
5063
+ };
5064
+ }
5065
+ function isObject(item) {
5066
+ return item && typeof item === 'object' && !Array.isArray(item);
5067
+ }
5068
+ function deepMerge(target) {
5069
+ if (!isObject(target)) return target;
5070
+ target = deepClone(target);
5071
+ for (var _len = arguments.length, sources = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
5072
+ sources[_key - 1] = arguments[_key];
5073
+ }
5074
+ for (const source of sources) {
5075
+ if (isObject(source)) {
5076
+ for (const key in source) {
5077
+ if (isObject(source[key])) {
5078
+ if (!(key in target)) target[key] = {};
5079
+ target[key] = deepMerge(target[key], source[key]);
5080
+ } else if (source[key] !== undefined) {
5081
+ target[key] = deepClone(source[key]);
5082
+ }
5083
+ }
5084
+ }
5085
+ }
5086
+ return target;
5087
+ }
5088
+ function deepClone(obj) {
5089
+ let result = obj;
5090
+ if (obj && typeof obj == 'object') {
5091
+ result = Array.isArray(obj) ? [] : {};
5092
+ for (const key in obj) result[key] = deepClone(obj[key]);
5093
+ }
5094
+ return result;
5095
+ }
5096
+
5097
+ function normalizedDefaultStyle(defaultStyleInternal) {
5098
+ let defaultStyle = defaultStyleInternal;
5099
+ if (typeof defaultStyle !== 'object') defaultStyle = {
5100
+ text: defaultStyle
5101
+ };
5102
+ const defaultRowStyle = Object.fromEntries(Object.entries(defaultStyle).filter(_ref => {
5103
+ let [k] = _ref;
5104
+ return ROW_FIELDS.includes(k);
5105
+ }));
5106
+ const defaultColStyle = Object.fromEntries(Object.entries(defaultStyle).filter(_ref2 => {
5107
+ let [k] = _ref2;
5108
+ return COLUMN_FIELDS.includes(k);
5109
+ }));
5110
+ defaultStyle.padding = normalizeSides(defaultStyle.padding);
5111
+ defaultStyle.border = normalizeSides(defaultStyle.border);
5112
+ defaultStyle.borderColor = normalizeSides(defaultStyle.borderColor);
5113
+ defaultStyle.align = normalizeAlignment(defaultStyle.align);
5114
+ return {
5115
+ defaultStyle,
5116
+ defaultRowStyle,
5117
+ defaultColStyle
5118
+ };
5119
+ }
5120
+ function normalizedRowStyle(defaultRowStyle, rowStyleInternal, i) {
5121
+ let rowStyle = rowStyleInternal(i);
5122
+ if (rowStyle == null || typeof rowStyle !== 'object') {
5123
+ rowStyle = {
5124
+ height: rowStyle
5125
+ };
5126
+ }
5127
+ rowStyle.padding = normalizeSides(rowStyle.padding);
5128
+ rowStyle.border = normalizeSides(rowStyle.border);
5129
+ rowStyle.borderColor = normalizeSides(rowStyle.borderColor);
5130
+ rowStyle.align = normalizeAlignment(rowStyle.align);
5131
+ rowStyle = deepMerge(defaultRowStyle, rowStyle);
5132
+ const document = this.document;
5133
+ const page = document.page;
5134
+ const contentHeight = page.contentHeight;
5135
+ if (rowStyle.height == null || rowStyle.height === 'auto') {
5136
+ rowStyle.height = 'auto';
5137
+ } else {
5138
+ rowStyle.height = document.sizeToPoint(rowStyle.height, 0, page, contentHeight);
5139
+ }
5140
+ rowStyle.minHeight = document.sizeToPoint(rowStyle.minHeight, 0, page, contentHeight);
5141
+ rowStyle.maxHeight = document.sizeToPoint(rowStyle.maxHeight, 0, page, contentHeight);
5142
+ return rowStyle;
5143
+ }
5144
+ function normalizedColumnStyle(defaultColStyle, colStyleInternal, i) {
5145
+ let colStyle = colStyleInternal(i);
5146
+ if (colStyle == null || typeof colStyle !== 'object') {
5147
+ colStyle = {
5148
+ width: colStyle
5149
+ };
5150
+ }
5151
+ colStyle.padding = normalizeSides(colStyle.padding);
5152
+ colStyle.border = normalizeSides(colStyle.border);
5153
+ colStyle.borderColor = normalizeSides(colStyle.borderColor);
5154
+ colStyle.align = normalizeAlignment(colStyle.align);
5155
+ colStyle = deepMerge(defaultColStyle, colStyle);
5156
+ if (colStyle.width == null || colStyle.width === '*') {
5157
+ colStyle.width = '*';
5158
+ } else {
5159
+ colStyle.width = this.document.sizeToPoint(colStyle.width, 0, this.document.page, this._maxWidth);
5160
+ }
5161
+ colStyle.minWidth = this.document.sizeToPoint(colStyle.minWidth, 0, this.document.page, this._maxWidth);
5162
+ colStyle.maxWidth = this.document.sizeToPoint(colStyle.maxWidth, 0, this.document.page, this._maxWidth);
5163
+ return colStyle;
5164
+ }
5165
+ function normalizeAlignment(align) {
5166
+ return align == null || typeof align === 'string' ? {
5167
+ x: align,
5168
+ y: align
5169
+ } : align;
5170
+ }
5171
+
5172
+ function normalizeTable() {
5173
+ const doc = this.document;
5174
+ const opts = this.opts;
5175
+ let index = doc._tableIndex++;
5176
+ this._id = new String(opts.id ?? `table-${index}`);
5177
+ this._position = {
5178
+ x: doc.sizeToPoint(opts.position?.x, doc.x),
5179
+ y: doc.sizeToPoint(opts.position?.y, doc.y)
5180
+ };
5181
+ this._maxWidth = doc.sizeToPoint(opts.maxWidth, doc.page.width - doc.page.margins.right - this._position.x);
5182
+ const {
5183
+ defaultStyle,
5184
+ defaultColStyle,
5185
+ defaultRowStyle
5186
+ } = normalizedDefaultStyle(opts.defaultStyle);
5187
+ this._defaultStyle = defaultStyle;
5188
+ let colStyle;
5189
+ if (opts.columnStyles) {
5190
+ if (Array.isArray(opts.columnStyles)) {
5191
+ colStyle = i => opts.columnStyles[i];
5192
+ } else if (typeof opts.columnStyles === 'function') {
5193
+ colStyle = memoize(i => opts.columnStyles(i), Infinity);
5194
+ } else if (typeof opts.columnStyles === 'object') {
5195
+ colStyle = () => opts.columnStyles;
5196
+ }
5197
+ }
5198
+ if (!colStyle) colStyle = () => ({});
5199
+ this._colStyle = normalizedColumnStyle.bind(this, defaultColStyle, colStyle);
5200
+ let rowStyle;
5201
+ if (opts.rowStyles) {
5202
+ if (Array.isArray(opts.rowStyles)) {
5203
+ rowStyle = i => opts.rowStyles[i];
5204
+ } else if (typeof opts.rowStyles === 'function') {
5205
+ rowStyle = memoize(i => opts.rowStyles(i), 10);
5206
+ } else if (typeof opts.rowStyles === 'object') {
5207
+ rowStyle = () => opts.rowStyles;
5208
+ }
5209
+ }
5210
+ if (!rowStyle) rowStyle = () => ({});
5211
+ this._rowStyle = normalizedRowStyle.bind(this, defaultRowStyle, rowStyle);
5212
+ }
5213
+ function normalizeText(text) {
5214
+ if (text != null) text = `${text}`;
5215
+ return text;
5216
+ }
5217
+ function normalizeCell(cell, rowIndex, colIndex) {
5218
+ const colStyle = this._colStyle(colIndex);
5219
+ let rowStyle = this._rowStyle(rowIndex);
5220
+ const font = deepMerge({}, colStyle.font, rowStyle.font, cell.font);
5221
+ const customFont = Object.values(font).filter(v => v != null).length > 0;
5222
+ const doc = this.document;
5223
+ const rollbackFont = doc._fontSource;
5224
+ const rollbackFontSize = doc._fontSize;
5225
+ const rollbackFontFamily = doc._fontFamily;
5226
+ if (customFont) {
5227
+ if (font.src) doc.font(font.src, font.family);
5228
+ if (font.size) doc.fontSize(font.size);
5229
+ rowStyle = this._rowStyle(rowIndex);
5230
+ }
5231
+ cell.padding = normalizeSides(cell.padding);
5232
+ cell.border = normalizeSides(cell.border);
5233
+ cell.borderColor = normalizeSides(cell.borderColor);
5234
+ const config = deepMerge(this._defaultStyle, colStyle, rowStyle, cell);
5235
+ config.rowIndex = rowIndex;
5236
+ config.colIndex = colIndex;
5237
+ config.font = font ?? {};
5238
+ config.customFont = customFont;
5239
+ config.text = normalizeText(config.text);
5240
+ config.rowSpan = config.rowSpan ?? 1;
5241
+ config.colSpan = config.colSpan ?? 1;
5242
+ config.padding = normalizeSides(config.padding, '0.25em', x => doc.sizeToPoint(x, '0.25em'));
5243
+ config.border = normalizeSides(config.border, 1, x => doc.sizeToPoint(x, 1));
5244
+ config.borderColor = normalizeSides(config.borderColor, 'black', x => x ?? 'black');
5245
+ config.align = normalizeAlignment(config.align);
5246
+ config.align.x = config.align.x ?? 'left';
5247
+ config.align.y = config.align.y ?? 'top';
5248
+ config.textStroke = doc.sizeToPoint(config.textStroke, 0);
5249
+ config.textStrokeColor = config.textStrokeColor ?? 'black';
5250
+ config.textColor = config.textColor ?? 'black';
5251
+ config.textOptions = config.textOptions ?? {};
5252
+ config.id = new String(config.id ?? `${this._id}-${rowIndex}-${colIndex}`);
5253
+ config.type = config.type?.toUpperCase() === 'TH' ? 'TH' : 'TD';
5254
+ if (config.scope) {
5255
+ config.scope = config.scope.toLowerCase();
5256
+ if (config.scope === 'row') config.scope = 'Row';else if (config.scope === 'both') config.scope = 'Both';else if (config.scope === 'column') config.scope = 'Column';
5257
+ }
5258
+ if (typeof this.opts.debug === 'boolean') config.debug = this.opts.debug;
5259
+ if (customFont) doc.font(rollbackFont, rollbackFontFamily, rollbackFontSize);
5260
+ return config;
5261
+ }
5262
+ function normalizeRow(row, rowIndex) {
5263
+ if (!this._cellClaim) this._cellClaim = new Set();
5264
+ let colIndex = 0;
5265
+ return row.map(cell => {
5266
+ if (cell == null || typeof cell !== 'object') cell = {
5267
+ text: cell
5268
+ };
5269
+ while (this._cellClaim.has(`${rowIndex},${colIndex}`)) {
5270
+ colIndex++;
5271
+ }
5272
+ cell = normalizeCell.call(this, cell, rowIndex, colIndex);
5273
+ for (let i = 0; i < cell.rowSpan; i++) {
5274
+ for (let j = 0; j < cell.colSpan; j++) {
5275
+ this._cellClaim.add(`${rowIndex + i},${colIndex + j}`);
5276
+ }
5277
+ }
5278
+ colIndex += cell.colSpan;
5279
+ return cell;
5280
+ });
5281
+ }
5282
+
5283
+ function ensure(row) {
5284
+ this._columnWidths = [];
5285
+ ensureColumnWidths.call(this, row.reduce((a, cell) => a + cell.colSpan, 0));
5286
+ this._rowHeights = [];
5287
+ this._rowYPos = [this._position.y];
5288
+ this._rowBuffer = new Set();
5289
+ }
5290
+ function ensureColumnWidths(numCols) {
5291
+ let starColumnIndexes = [];
5292
+ let starMinAcc = 0;
5293
+ let unclaimedWidth = this._maxWidth;
5294
+ for (let i = 0; i < numCols; i++) {
5295
+ let col = this._colStyle(i);
5296
+ if (col.width === '*') {
5297
+ starColumnIndexes[i] = col;
5298
+ starMinAcc += col.minWidth;
5299
+ } else {
5300
+ unclaimedWidth -= col.width;
5301
+ this._columnWidths[i] = col.width;
5302
+ }
5303
+ }
5304
+ let starColCount = starColumnIndexes.reduce(x => x + 1, 0);
5305
+ if (starMinAcc >= unclaimedWidth) {
5306
+ starColumnIndexes.forEach((cell, i) => {
5307
+ this._columnWidths[i] = cell.minWidth;
5308
+ });
5309
+ } else if (starColCount > 0) {
5310
+ starColumnIndexes.forEach((col, i) => {
5311
+ let starSize = unclaimedWidth / starColCount;
5312
+ this._columnWidths[i] = Math.max(starSize, col.minWidth);
5313
+ if (col.maxWidth > 0) {
5314
+ this._columnWidths[i] = Math.min(this._columnWidths[i], col.maxWidth);
5315
+ }
5316
+ unclaimedWidth -= this._columnWidths[i];
5317
+ starColCount--;
5318
+ });
5319
+ }
5320
+ let tempX = this._position.x;
5321
+ this._columnXPos = Array.from(this._columnWidths, v => {
5322
+ const t = tempX;
5323
+ tempX += v;
5324
+ return t;
5325
+ });
5326
+ }
5327
+ function measure(row, rowIndex) {
5328
+ row.forEach(cell => this._rowBuffer.add(cell));
5329
+ if (rowIndex > 0) {
5330
+ this._rowYPos[rowIndex] = this._rowYPos[rowIndex - 1] + this._rowHeights[rowIndex - 1];
5331
+ }
5332
+ const rowStyle = this._rowStyle(rowIndex);
5333
+ let toRender = [];
5334
+ this._rowBuffer.forEach(cell => {
5335
+ if (cell.rowIndex + cell.rowSpan - 1 === rowIndex) {
5336
+ toRender.push(measureCell.call(this, cell, rowStyle.height));
5337
+ this._rowBuffer.delete(cell);
5338
+ }
5339
+ });
5340
+ let rowHeight = rowStyle.height;
5341
+ if (rowHeight === 'auto') {
5342
+ rowHeight = toRender.reduce((acc, cell) => {
5343
+ let minHeight = cell.textBounds.height + cell.padding.top + cell.padding.bottom;
5344
+ for (let i = 0; i < cell.rowSpan - 1; i++) {
5345
+ minHeight -= this._rowHeights[cell.rowIndex + i];
5346
+ }
5347
+ return Math.max(acc, minHeight);
5348
+ }, 0);
5349
+ }
5350
+ rowHeight = Math.max(rowHeight, rowStyle.minHeight);
5351
+ if (rowStyle.maxHeight > 0) {
5352
+ rowHeight = Math.min(rowHeight, rowStyle.maxHeight);
5353
+ }
5354
+ this._rowHeights[rowIndex] = rowHeight;
5355
+ let newPage = false;
5356
+ if (rowHeight > this.document.page.contentHeight) {
5357
+ console.warn(new Error(`Row ${rowIndex} requested more than the safe page height, row has been clamped`).stack.slice(7));
5358
+ this._rowHeights[rowIndex] = this.document.page.maxY() - this._rowYPos[rowIndex];
5359
+ } else if (this._rowYPos[rowIndex] + rowHeight >= this.document.page.maxY()) {
5360
+ this._rowYPos[rowIndex] = this.document.page.margins.top;
5361
+ newPage = true;
5362
+ }
5363
+ return {
5364
+ newPage,
5365
+ toRender: toRender.map(cell => measureCell.call(this, cell, rowHeight))
5366
+ };
5367
+ }
5368
+ function measureCell(cell, rowHeight) {
5369
+ let cellWidth = 0;
5370
+ for (let i = 0; i < cell.colSpan; i++) {
5371
+ cellWidth += this._columnWidths[cell.colIndex + i];
5372
+ }
5373
+ let cellHeight = rowHeight;
5374
+ if (cellHeight === 'auto') {
5375
+ cellHeight = this.document.page.contentHeight;
5376
+ } else {
5377
+ for (let i = 0; i < cell.rowSpan - 1; i++) {
5378
+ cellHeight += this._rowHeights[cell.rowIndex + i];
5379
+ }
5380
+ }
5381
+ const textAllocatedWidth = cellWidth - cell.padding.left - cell.padding.right;
5382
+ const textAllocatedHeight = cellHeight - cell.padding.top - cell.padding.bottom;
5383
+ const rotation = cell.textOptions.rotation ?? 0;
5384
+ const {
5385
+ width: textMaxWidth,
5386
+ height: textMaxHeight
5387
+ } = computeBounds(rotation, textAllocatedWidth, textAllocatedHeight);
5388
+ const textOptions = {
5389
+ align: cell.align.x,
5390
+ ellipsis: true,
5391
+ stroke: cell.textStroke > 0,
5392
+ fill: true,
5393
+ width: textMaxWidth,
5394
+ height: textMaxHeight,
5395
+ rotation,
5396
+ ...cell.textOptions
5397
+ };
5398
+ let textBounds = {
5399
+ x: 0,
5400
+ y: 0,
5401
+ width: 0,
5402
+ height: 0
5403
+ };
5404
+ if (cell.text) {
5405
+ const rollbackFont = this.document._fontSource;
5406
+ const rollbackFontSize = this.document._fontSize;
5407
+ const rollbackFontFamily = this.document._fontFamily;
5408
+ if (cell.font?.src) this.document.font(cell.font.src, cell.font?.family);
5409
+ if (cell.font?.size) this.document.fontSize(cell.font.size);
5410
+ const unRotatedTextBounds = this.document.boundsOfString(cell.text, 0, 0, {
5411
+ ...textOptions,
5412
+ rotation: 0
5413
+ });
5414
+ textOptions.width = unRotatedTextBounds.width;
5415
+ textOptions.height = unRotatedTextBounds.height;
5416
+ textBounds = this.document.boundsOfString(cell.text, 0, 0, textOptions);
5417
+ this.document.font(rollbackFont, rollbackFontFamily, rollbackFontSize);
5418
+ }
5419
+ return {
5420
+ ...cell,
5421
+ textOptions,
5422
+ x: this._columnXPos[cell.colIndex],
5423
+ y: this._rowYPos[cell.rowIndex],
5424
+ textX: this._columnXPos[cell.colIndex] + cell.padding.left,
5425
+ textY: this._rowYPos[cell.rowIndex] + cell.padding.top,
5426
+ width: cellWidth,
5427
+ height: cellHeight,
5428
+ textAllocatedHeight,
5429
+ textAllocatedWidth,
5430
+ textBounds
5431
+ };
5432
+ }
5433
+ function computeBounds(rotation, allocWidth, allocHeight) {
5434
+ let textMaxWidth, textMaxHeight;
5435
+ const cos = cosine(rotation);
5436
+ const sin = sine(rotation);
5437
+ if (rotation === 0 || rotation === 180) {
5438
+ textMaxWidth = allocWidth;
5439
+ textMaxHeight = allocHeight;
5440
+ } else if (rotation === 90 || rotation === 270) {
5441
+ textMaxWidth = allocHeight;
5442
+ textMaxHeight = allocWidth;
5443
+ } else if (rotation < 90 || rotation > 180 && rotation < 270) {
5444
+ textMaxWidth = allocWidth / (2 * cos);
5445
+ textMaxHeight = allocWidth / (2 * sin);
5446
+ } else {
5447
+ textMaxHeight = allocWidth / (2 * cos);
5448
+ textMaxWidth = allocWidth / (2 * sin);
5449
+ }
5450
+ const EF = sin * textMaxWidth;
5451
+ const FG = cos * textMaxHeight;
5452
+ if (EF + FG > allocHeight) {
5453
+ const denominator = cos * cos - sin * sin;
5454
+ if (rotation === 0 || rotation === 180) {
5455
+ textMaxWidth = allocWidth;
5456
+ textMaxHeight = allocHeight;
5457
+ } else if (rotation === 90 || rotation === 270) {
5458
+ textMaxWidth = allocHeight;
5459
+ textMaxHeight = allocWidth;
5460
+ } else if (rotation < 90 || rotation > 180 && rotation < 270) {
5461
+ textMaxWidth = (allocWidth * cos - allocHeight * sin) / denominator;
5462
+ textMaxHeight = (allocHeight * cos - allocWidth * sin) / denominator;
5463
+ } else {
5464
+ textMaxHeight = (allocWidth * cos - allocHeight * sin) / denominator;
5465
+ textMaxWidth = (allocHeight * cos - allocWidth * sin) / denominator;
5466
+ }
5467
+ }
5468
+ return {
5469
+ width: Math.abs(textMaxWidth),
5470
+ height: Math.abs(textMaxHeight)
5471
+ };
5472
+ }
5473
+
5474
+ function accommodateTable() {
5475
+ const structParent = this.opts.structParent;
5476
+ if (structParent) {
5477
+ this._tableStruct = this.document.struct('Table');
5478
+ this._tableStruct.dictionary.data.ID = this._id;
5479
+ if (structParent instanceof PDFStructureElement) {
5480
+ structParent.add(this._tableStruct);
5481
+ } else if (structParent instanceof PDFDocument) {
5482
+ structParent.addStructure(this._tableStruct);
5483
+ }
5484
+ this._headerRowLookup = {};
5485
+ this._headerColumnLookup = {};
5486
+ }
5487
+ }
5488
+ function accommodateCleanup() {
5489
+ if (this._tableStruct) this._tableStruct.end();
5490
+ }
5491
+ function accessibleRow(row, rowIndex, renderCell) {
5492
+ const rowStruct = this.document.struct('TR');
5493
+ rowStruct.dictionary.data.ID = new String(`${this._id}-${rowIndex}`);
5494
+ this._tableStruct.add(rowStruct);
5495
+ row.forEach(cell => renderCell(cell, rowStruct));
5496
+ rowStruct.end();
5497
+ }
5498
+ function accessibleCell(cell, rowStruct, callback) {
5499
+ const doc = this.document;
5500
+ const cellStruct = doc.struct(cell.type, {
5501
+ title: cell.title
5502
+ });
5503
+ cellStruct.dictionary.data.ID = cell.id;
5504
+ rowStruct.add(cellStruct);
5505
+ const padding = cell.padding;
5506
+ const border = cell.border;
5507
+ const attributes = {
5508
+ O: 'Table',
5509
+ Width: cell.width,
5510
+ Height: cell.height,
5511
+ Padding: [padding.top, padding.bottom, padding.left, padding.right],
5512
+ RowSpan: cell.rowSpan > 1 ? cell.rowSpan : undefined,
5513
+ ColSpan: cell.colSpan > 1 ? cell.colSpan : undefined,
5514
+ BorderThickness: [border.top, border.bottom, border.left, border.right]
5515
+ };
5516
+ if (cell.type === 'TH') {
5517
+ if (cell.scope === 'Row' || cell.scope === 'Both') {
5518
+ for (let i = 0; i < cell.rowSpan; i++) {
5519
+ if (!this._headerRowLookup[cell.rowIndex + i]) {
5520
+ this._headerRowLookup[cell.rowIndex + i] = [];
5521
+ }
5522
+ this._headerRowLookup[cell.rowIndex + i].push(cell.id);
5523
+ }
5524
+ attributes.Scope = cell.scope;
5525
+ }
5526
+ if (cell.scope === 'Column' || cell.scope === 'Both') {
5527
+ for (let i = 0; i < cell.colSpan; i++) {
5528
+ if (!this._headerColumnLookup[cell.colIndex + i]) {
5529
+ this._headerColumnLookup[cell.colIndex + i] = [];
5530
+ }
5531
+ this._headerColumnLookup[cell.colIndex + i].push(cell.id);
5532
+ }
5533
+ attributes.Scope = cell.scope;
5534
+ }
5535
+ }
5536
+ const Headers = new Set([...Array.from({
5537
+ length: cell.colSpan
5538
+ }, (_, i) => this._headerColumnLookup[cell.colIndex + i]).flat(), ...Array.from({
5539
+ length: cell.rowSpan
5540
+ }, (_, i) => this._headerRowLookup[cell.rowIndex + i]).flat()].filter(Boolean));
5541
+ if (Headers.size) attributes.Headers = Array.from(Headers);
5542
+ const normalizeColor = doc._normalizeColor;
5543
+ if (cell.backgroundColor != null) {
5544
+ attributes.BackgroundColor = normalizeColor(cell.backgroundColor);
5545
+ }
5546
+ const hasBorder = [border.top, border.bottom, border.left, border.right];
5547
+ if (hasBorder.some(x => x)) {
5548
+ const borderColor = cell.borderColor;
5549
+ attributes.BorderColor = [hasBorder[0] ? normalizeColor(borderColor.top) : null, hasBorder[1] ? normalizeColor(borderColor.bottom) : null, hasBorder[2] ? normalizeColor(borderColor.left) : null, hasBorder[3] ? normalizeColor(borderColor.right) : null];
5550
+ }
5551
+ Object.keys(attributes).forEach(key => attributes[key] === undefined && delete attributes[key]);
5552
+ cellStruct.dictionary.data.A = doc.ref(attributes);
5553
+ cellStruct.add(callback);
5554
+ cellStruct.end();
5555
+ cellStruct.dictionary.data.A.end();
5556
+ }
5557
+
5558
+ function renderRow(row, rowIndex) {
5559
+ if (this._tableStruct) {
5560
+ accessibleRow.call(this, row, rowIndex, renderCell.bind(this));
5561
+ } else {
5562
+ row.forEach(cell => renderCell.call(this, cell));
5563
+ }
5564
+ return this._rowYPos[rowIndex] + this._rowHeights[rowIndex];
5565
+ }
5566
+ function renderCell(cell, rowStruct) {
5567
+ const cellRenderer = () => {
5568
+ if (cell.backgroundColor != null) {
5569
+ this.document.save().rect(cell.x, cell.y, cell.width, cell.height).fill(cell.backgroundColor).restore();
5570
+ }
5571
+ renderBorder.call(this, cell.border, cell.borderColor, cell.x, cell.y, cell.width, cell.height);
5572
+ if (cell.debug) {
5573
+ this.document.save();
5574
+ this.document.dash(1, {
5575
+ space: 1
5576
+ }).lineWidth(1).strokeOpacity(0.3);
5577
+ this.document.rect(cell.x, cell.y, cell.width, cell.height).stroke('green');
5578
+ this.document.restore();
5579
+ }
5580
+ if (cell.text) renderCellText.call(this, cell);
5581
+ };
5582
+ if (rowStruct) accessibleCell.call(this, cell, rowStruct, cellRenderer);else cellRenderer();
5583
+ }
5584
+ function renderCellText(cell) {
5585
+ const doc = this.document;
5586
+ const rollbackFont = doc._fontSource;
5587
+ const rollbackFontSize = doc._fontSize;
5588
+ const rollbackFontFamily = doc._fontFamily;
5589
+ if (cell.customFont) {
5590
+ if (cell.font.src) doc.font(cell.font.src, cell.font.family);
5591
+ if (cell.font.size) doc.fontSize(cell.font.size);
5592
+ }
5593
+ const x = cell.textX;
5594
+ const y = cell.textY;
5595
+ const Ah = cell.textAllocatedHeight;
5596
+ const Aw = cell.textAllocatedWidth;
5597
+ const Cw = cell.textBounds.width;
5598
+ const Ch = cell.textBounds.height;
5599
+ const Ox = -cell.textBounds.x;
5600
+ const Oy = -cell.textBounds.y;
5601
+ const PxScale = cell.align.x === 'right' ? 1 : cell.align.x === 'center' ? 0.5 : 0;
5602
+ const Px = (Aw - Cw) * PxScale;
5603
+ const PyScale = cell.align.y === 'bottom' ? 1 : cell.align.y === 'center' ? 0.5 : 0;
5604
+ const Py = (Ah - Ch) * PyScale;
5605
+ const dx = Px + Ox;
5606
+ const dy = Py + Oy;
5607
+ if (cell.debug) {
5608
+ doc.save();
5609
+ doc.dash(1, {
5610
+ space: 1
5611
+ }).lineWidth(1).strokeOpacity(0.3);
5612
+ if (cell.text) {
5613
+ doc.moveTo(x + Px, y).lineTo(x + Px, y + Ah).moveTo(x + Px + Cw, y).lineTo(x + Px + Cw, y + Ah).stroke('blue').moveTo(x, y + Py).lineTo(x + Aw, y + Py).moveTo(x, y + Py + Ch).lineTo(x + Aw, y + Py + Ch).stroke('green');
5614
+ }
5615
+ doc.rect(x, y, Aw, Ah).stroke('orange');
5616
+ doc.restore();
5617
+ }
5618
+ doc.save().rect(x, y, Aw, Ah).clip();
5619
+ doc.fillColor(cell.textColor).strokeColor(cell.textStrokeColor);
5620
+ if (cell.textStroke > 0) doc.lineWidth(cell.textStroke);
5621
+ doc.text(cell.text, x + dx, y + dy, cell.textOptions);
5622
+ doc.restore();
5623
+ if (cell.font) doc.font(rollbackFont, rollbackFontFamily, rollbackFontSize);
5624
+ }
5625
+ function renderBorder(border, borderColor, x, y, width, height, mask) {
5626
+ border = Object.fromEntries(Object.entries(border).map(_ref => {
5627
+ let [k, v] = _ref;
5628
+ return [k, mask && !mask[k] ? 0 : v];
5629
+ }));
5630
+ const doc = this.document;
5631
+ if ([border.right, border.bottom, border.left].every(val => val === border.top)) {
5632
+ if (border.top > 0) {
5633
+ doc.save().lineWidth(border.top).rect(x, y, width, height).stroke(borderColor.top).restore();
5634
+ }
5635
+ } else {
5636
+ if (border.top > 0) {
5637
+ doc.save().lineWidth(border.top).moveTo(x, y).lineTo(x + width, y).stroke(borderColor.top).restore();
5638
+ }
5639
+ if (border.right > 0) {
5640
+ doc.save().lineWidth(border.right).moveTo(x + width, y).lineTo(x + width, y + height).stroke(borderColor.right).restore();
5641
+ }
5642
+ if (border.bottom > 0) {
5643
+ doc.save().lineWidth(border.bottom).moveTo(x + width, y + height).lineTo(x, y + height).stroke(borderColor.bottom).restore();
5644
+ }
5645
+ if (border.left > 0) {
5646
+ doc.save().lineWidth(border.left).moveTo(x, y + height).lineTo(x, y).stroke(borderColor.left).restore();
5647
+ }
5648
+ }
5649
+ }
5650
+
5651
+ class PDFTable {
5652
+ constructor(document) {
5653
+ let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
5654
+ this.document = document;
5655
+ this.opts = Object.freeze(opts);
5656
+ normalizeTable.call(this);
5657
+ accommodateTable.call(this);
5658
+ this._currRowIndex = 0;
5659
+ this._ended = false;
5660
+ if (opts.data) {
5661
+ for (const row of opts.data) this.row(row);
5662
+ return this.end();
5663
+ }
5664
+ }
5665
+ row(row) {
5666
+ let lastRow = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
5667
+ if (this._ended) {
5668
+ throw new Error(`Table was marked as ended on row ${this._currRowIndex}`);
5669
+ }
5670
+ row = Array.from(row);
5671
+ row = normalizeRow.call(this, row, this._currRowIndex);
5672
+ if (this._currRowIndex === 0) ensure.call(this, row);
5673
+ const {
5674
+ newPage,
5675
+ toRender
5676
+ } = measure.call(this, row, this._currRowIndex);
5677
+ if (newPage) this.document.continueOnNewPage();
5678
+ const yPos = renderRow.call(this, toRender, this._currRowIndex);
5679
+ this.document.x = this._position.x;
5680
+ this.document.y = yPos;
5681
+ if (lastRow) return this.end();
5682
+ this._currRowIndex++;
5683
+ return this;
5684
+ }
5685
+ end() {
5686
+ while (this._rowBuffer?.size) this.row([]);
5687
+ this._ended = true;
5688
+ accommodateCleanup.call(this);
5689
+ return this.document;
5690
+ }
5691
+ }
5692
+
5693
+ var TableMixin = {
5694
+ initTables() {
5695
+ this._tableIndex = 0;
5696
+ },
5697
+ table(opts) {
5698
+ return new PDFTable(this, opts);
5699
+ }
5700
+ };
5701
+
5440
5702
  class PDFMetadata {
5441
5703
  constructor() {
5442
5704
  this._metadata = `
@@ -5480,7 +5742,7 @@ var MetadataMixin = {
5480
5742
  _addInfo() {
5481
5743
  this.appendXML(`
5482
5744
  <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">
5483
- <xmp:CreateDate>${this.info.CreationDate.toISOString().split('.')[0] + "Z"}</xmp:CreateDate>
5745
+ <xmp:CreateDate>${this.info.CreationDate.toISOString().split('.')[0] + 'Z'}</xmp:CreateDate>
5484
5746
  <xmp:CreatorTool>${this.info.Creator}</xmp:CreatorTool>
5485
5747
  </rdf:Description>
5486
5748
  `);
@@ -5533,11 +5795,6 @@ var MetadataMixin = {
5533
5795
  endMetadata() {
5534
5796
  this._addInfo();
5535
5797
  this.metadata.end();
5536
-
5537
- /*
5538
- Metadata was introduced in PDF 1.4, so adding it to 1.3
5539
- will likely only take up more space.
5540
- */
5541
5798
  if (this.version != 1.3) {
5542
5799
  this.metadataRef = this.ref({
5543
5800
  length: this.metadata.getLength(),
@@ -5552,17 +5809,11 @@ var MetadataMixin = {
5552
5809
  }
5553
5810
  };
5554
5811
 
5555
- /*
5556
- PDFDocument - represents an entire PDF document
5557
- By Devon Govett
5558
- */
5559
5812
  class PDFDocument extends stream.Readable {
5560
5813
  constructor() {
5561
5814
  let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
5562
5815
  super(options);
5563
5816
  this.options = options;
5564
-
5565
- // PDF version
5566
5817
  switch (options.pdfVersion) {
5567
5818
  case '1.4':
5568
5819
  this.version = 1.4;
@@ -5581,13 +5832,9 @@ class PDFDocument extends stream.Readable {
5581
5832
  this.version = 1.3;
5582
5833
  break;
5583
5834
  }
5584
-
5585
- // Whether streams should be compressed
5586
5835
  this.compress = this.options.compress != null ? this.options.compress : true;
5587
5836
  this._pageBuffer = [];
5588
5837
  this._pageBufferStart = 0;
5589
-
5590
- // The PDF object store
5591
5838
  this._offsets = [];
5592
5839
  this._waiting = 0;
5593
5840
  this._ended = false;
@@ -5608,11 +5855,7 @@ class PDFDocument extends stream.Readable {
5608
5855
  if (this.options.lang) {
5609
5856
  this._root.data.Lang = new String(this.options.lang);
5610
5857
  }
5611
-
5612
- // The current page
5613
5858
  this.page = null;
5614
-
5615
- // Initialize mixins
5616
5859
  this.initMetadata();
5617
5860
  this.initColor();
5618
5861
  this.initVector();
@@ -5621,9 +5864,8 @@ class PDFDocument extends stream.Readable {
5621
5864
  this.initImages();
5622
5865
  this.initOutline();
5623
5866
  this.initMarkings(options);
5867
+ this.initTables();
5624
5868
  this.initSubset(options);
5625
-
5626
- // Initialize the metadata
5627
5869
  this.info = {
5628
5870
  Producer: 'PDFKit',
5629
5871
  Creator: 'PDFKit',
@@ -5640,21 +5882,10 @@ class PDFDocument extends stream.Readable {
5640
5882
  DisplayDocTitle: true
5641
5883
  });
5642
5884
  }
5643
-
5644
- // Generate file ID
5645
5885
  this._id = PDFSecurity.generateFileID(this.info);
5646
-
5647
- // Initialize security settings
5648
5886
  this._security = PDFSecurity.create(this, options);
5649
-
5650
- // Write the header
5651
- // PDF version
5652
5887
  this._write(`%PDF-${this.version}`);
5653
-
5654
- // 4 binary chars, as recommended by the spec
5655
5888
  this._write('%\xFF\xFF\xFF\xFF');
5656
-
5657
- // Add the first page
5658
5889
  if (this.options.autoFirstPage !== false) {
5659
5890
  this.addPage();
5660
5891
  }
@@ -5665,27 +5896,16 @@ class PDFDocument extends stream.Readable {
5665
5896
  options
5666
5897
  } = this);
5667
5898
  }
5668
-
5669
- // end the current page if needed
5670
5899
  if (!this.options.bufferPages) {
5671
5900
  this.flushPages();
5672
5901
  }
5673
-
5674
- // create a page object
5675
5902
  this.page = new PDFPage(this, options);
5676
5903
  this._pageBuffer.push(this.page);
5677
-
5678
- // add the page to the object store
5679
5904
  const pages = this._root.data.Pages.data;
5680
5905
  pages.Kids.push(this.page.dictionary);
5681
5906
  pages.Count++;
5682
-
5683
- // reset x and y coordinates
5684
5907
  this.x = this.page.margins.left;
5685
5908
  this.y = this.page.margins.top;
5686
-
5687
- // flip PDF coordinate system so that the origin is in
5688
- // the top left rather than the bottom left
5689
5909
  this._ctm = [1, 0, 0, 1, 0, 0];
5690
5910
  this.transform(1, 0, 0, -1, 0, this.page.height);
5691
5911
  this.emit('pageAdded');
@@ -5693,7 +5913,7 @@ class PDFDocument extends stream.Readable {
5693
5913
  }
5694
5914
  continueOnNewPage(options) {
5695
5915
  const pageMarkings = this.endPageMarkings(this.page);
5696
- this.addPage(options);
5916
+ this.addPage(options ?? this.page._options);
5697
5917
  this.initPageMarkings(pageMarkings);
5698
5918
  return this;
5699
5919
  }
@@ -5711,8 +5931,6 @@ class PDFDocument extends stream.Readable {
5711
5931
  return this.page = page;
5712
5932
  }
5713
5933
  flushPages() {
5714
- // this local variable exists so we're future-proof against
5715
- // reentrant calls to flushPages.
5716
5934
  const pages = this._pageBuffer;
5717
5935
  this._pageBuffer = [];
5718
5936
  this._pageBufferStart += pages.length;
@@ -5736,13 +5954,10 @@ class PDFDocument extends stream.Readable {
5736
5954
  }
5737
5955
  addNamedEmbeddedFile(name, ref) {
5738
5956
  if (!this._root.data.Names.data.EmbeddedFiles) {
5739
- // disabling /Limits for this tree fixes attachments not showing in Adobe Reader
5740
5957
  this._root.data.Names.data.EmbeddedFiles = new PDFNameTree({
5741
5958
  limits: false
5742
5959
  });
5743
5960
  }
5744
-
5745
- // add filespec to EmbeddedFiles
5746
5961
  this._root.data.Names.data.EmbeddedFiles.add(name, ref);
5747
5962
  }
5748
5963
  addNamedJavaScript(name, js) {
@@ -5757,19 +5972,17 @@ class PDFDocument extends stream.Readable {
5757
5972
  }
5758
5973
  ref(data) {
5759
5974
  const ref = new PDFReference(this, this._offsets.length + 1, data);
5760
- this._offsets.push(null); // placeholder for this object's offset once it is finalized
5975
+ this._offsets.push(null);
5761
5976
  this._waiting++;
5762
5977
  return ref;
5763
5978
  }
5764
5979
  _read() {}
5765
- // do nothing, but this method is required by node
5766
-
5767
5980
  _write(data) {
5768
5981
  if (!Buffer.isBuffer(data)) {
5769
5982
  data = Buffer.from(data + '\n', 'binary');
5770
5983
  }
5771
5984
  this.push(data);
5772
- return this._offset += data.length;
5985
+ this._offset += data.length;
5773
5986
  }
5774
5987
  addContent(data) {
5775
5988
  this.page.write(data);
@@ -5779,7 +5992,7 @@ class PDFDocument extends stream.Readable {
5779
5992
  this._offsets[ref.id - 1] = ref.offset;
5780
5993
  if (--this._waiting === 0 && this._ended) {
5781
5994
  this._finalize();
5782
- return this._ended = false;
5995
+ this._ended = false;
5783
5996
  }
5784
5997
  }
5785
5998
  end() {
@@ -5816,13 +6029,12 @@ class PDFDocument extends stream.Readable {
5816
6029
  this._security.end();
5817
6030
  }
5818
6031
  if (this._waiting === 0) {
5819
- return this._finalize();
6032
+ this._finalize();
5820
6033
  } else {
5821
- return this._ended = true;
6034
+ this._ended = true;
5822
6035
  }
5823
6036
  }
5824
6037
  _finalize() {
5825
- // generate xref
5826
6038
  const xRefOffset = this._offset;
5827
6039
  this._write('xref');
5828
6040
  this._write(`0 ${this._offsets.length + 1}`);
@@ -5831,8 +6043,6 @@ class PDFDocument extends stream.Readable {
5831
6043
  offset = `0000000000${offset}`.slice(-10);
5832
6044
  this._write(offset + ' 00000 n ');
5833
6045
  }
5834
-
5835
- // trailer
5836
6046
  const trailer = {
5837
6047
  Size: this._offsets.length + 1,
5838
6048
  Root: this._root,
@@ -5847,9 +6057,7 @@ class PDFDocument extends stream.Readable {
5847
6057
  this._write('startxref');
5848
6058
  this._write(`${xRefOffset}`);
5849
6059
  this._write('%%EOF');
5850
-
5851
- // end the stream
5852
- return this.push(null);
6060
+ this.push(null);
5853
6061
  }
5854
6062
  toString() {
5855
6063
  return '[object PDFDocument]';
@@ -5870,6 +6078,7 @@ mixin(MarkingsMixin);
5870
6078
  mixin(AcroFormMixin);
5871
6079
  mixin(AttachmentsMixin);
5872
6080
  mixin(SubsetMixin);
6081
+ mixin(TableMixin);
5873
6082
  PDFDocument.LineWrapper = LineWrapper;
5874
6083
 
5875
6084
  export { PDFDocument as default };