pdfkit 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,23 +12,15 @@ var LineBreaker = require('linebreak');
12
12
  var exif = require('jpeg-exif');
13
13
  var PNG = require('png-js');
14
14
 
15
- /*
16
- PDFAbstractReference - abstract class for PDF reference
17
- */
18
-
19
15
  class PDFAbstractReference {
20
16
  toString() {
21
17
  throw new Error('Must be implemented by subclasses');
22
18
  }
23
19
  }
24
20
 
25
- /*
26
- PDFTree - abstract base class for name and number tree objects
27
- */
28
21
  class PDFTree {
29
22
  constructor(options = {}) {
30
23
  this._items = {};
31
- // disable /Limits output for this tree
32
24
  this.limits = typeof options.limits === 'boolean' ? options.limits : true;
33
25
  }
34
26
  add(key, val) {
@@ -38,7 +30,6 @@ class PDFTree {
38
30
  return this._items[key];
39
31
  }
40
32
  toString() {
41
- // Needs to be sorted by key
42
33
  const sortedKeys = Object.keys(this._items).sort((a, b) => this._compareKeys(a, b));
43
34
  const out = ['<<'];
44
35
  if (this.limits && sortedKeys.length > 1) {
@@ -54,15 +45,13 @@ class PDFTree {
54
45
  out.push('>>');
55
46
  return out.join('\n');
56
47
  }
57
- _compareKeys(/*a, b*/
58
- ) {
48
+ _compareKeys() {
59
49
  throw new Error('Must be implemented by subclasses');
60
50
  }
61
51
  _keysName() {
62
52
  throw new Error('Must be implemented by subclasses');
63
53
  }
64
- _dataForKey(/*k*/
65
- ) {
54
+ _dataForKey() {
66
55
  throw new Error('Must be implemented by subclasses');
67
56
  }
68
57
  }
@@ -87,10 +76,6 @@ class SpotColor {
87
76
  }
88
77
  }
89
78
 
90
- /*
91
- PDFObject - converts JavaScript types into their corresponding PDF types.
92
- By Devon Govett
93
- */
94
79
  const pad = (str, length) => (Array(length + 1).join('0') + str).slice(-length);
95
80
  const escapableRe = /[\n\r\t\b\f()\\]/g;
96
81
  const escapable = {
@@ -103,8 +88,6 @@ const escapable = {
103
88
  '(': '\\(',
104
89
  ')': '\\)'
105
90
  };
106
-
107
- // Convert little endian UTF-16 to big endian
108
91
  const swapBytes = function (buff) {
109
92
  const l = buff.length;
110
93
  if (l & 0x01) {
@@ -120,14 +103,10 @@ const swapBytes = function (buff) {
120
103
  };
121
104
  class PDFObject {
122
105
  static convert(object, encryptFn = null) {
123
- // String literals are converted to the PDF name type
124
106
  if (typeof object === 'string') {
125
107
  return `/${object}`;
126
-
127
- // String objects are converted to PDF strings (UTF-16)
128
108
  } else if (object instanceof String) {
129
109
  let string = object;
130
- // Detect if this is a unicode string
131
110
  let isUnicode = false;
132
111
  for (let i = 0, end = string.length; i < end; i++) {
133
112
  if (string.charCodeAt(i) > 0x7f) {
@@ -135,39 +114,27 @@ class PDFObject {
135
114
  break;
136
115
  }
137
116
  }
138
-
139
- // If so, encode it as big endian UTF-16
140
117
  let stringBuffer;
141
118
  if (isUnicode) {
142
119
  stringBuffer = swapBytes(Buffer.from(`\ufeff${string}`, 'utf16le'));
143
120
  } else {
144
121
  stringBuffer = Buffer.from(string.valueOf(), 'ascii');
145
122
  }
146
-
147
- // Encrypt the string when necessary
148
123
  if (encryptFn) {
149
124
  string = encryptFn(stringBuffer).toString('binary');
150
125
  } else {
151
126
  string = stringBuffer.toString('binary');
152
127
  }
153
-
154
- // Escape characters as required by the spec
155
128
  string = string.replace(escapableRe, c => escapable[c]);
156
129
  return `(${string})`;
157
-
158
- // Buffers are converted to PDF hex strings
159
130
  } else if (Buffer.isBuffer(object)) {
160
131
  return `<${object.toString('hex')}>`;
161
132
  } else if (object instanceof PDFAbstractReference || object instanceof PDFTree || object instanceof SpotColor) {
162
133
  return object.toString();
163
134
  } else if (object instanceof Date) {
164
135
  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';
165
-
166
- // Encrypt the string when necessary
167
136
  if (encryptFn) {
168
137
  string = encryptFn(Buffer.from(string, 'ascii')).toString('binary');
169
-
170
- // Escape characters as required by the spec
171
138
  string = string.replace(escapableRe, c => escapable[c]);
172
139
  }
173
140
  return `(${string})`;
@@ -196,10 +163,6 @@ class PDFObject {
196
163
  }
197
164
  }
198
165
 
199
- /*
200
- PDFReference - represents a reference to another object in the PDF object heirarchy
201
- By Devon Govett
202
- */
203
166
  class PDFReference extends PDFAbstractReference {
204
167
  constructor(document, id, data = {}) {
205
168
  super();
@@ -222,14 +185,14 @@ class PDFReference extends PDFAbstractReference {
222
185
  this.buffer.push(chunk);
223
186
  this.data.Length += chunk.length;
224
187
  if (this.compress) {
225
- return this.data.Filter = 'FlateDecode';
188
+ this.data.Filter = 'FlateDecode';
226
189
  }
227
190
  }
228
191
  end(chunk) {
229
192
  if (chunk) {
230
193
  this.write(chunk);
231
194
  }
232
- return this.finalize();
195
+ this.finalize();
233
196
  }
234
197
  finalize() {
235
198
  this.offset = this.document._offset;
@@ -249,7 +212,7 @@ class PDFReference extends PDFAbstractReference {
249
212
  if (this.buffer.length) {
250
213
  this.document._write('stream');
251
214
  this.document._write(this.buffer);
252
- this.buffer = []; // free up memory
215
+ this.buffer = [];
253
216
  this.document._write('\nendstream');
254
217
  }
255
218
  this.document._write('endobj');
@@ -260,10 +223,69 @@ class PDFReference extends PDFAbstractReference {
260
223
  }
261
224
  }
262
225
 
263
- /*
264
- PDFPage - represents a single page in the PDF document
265
- By Devon Govett
266
- */
226
+ function PDFNumber(n) {
227
+ return Math.fround(n);
228
+ }
229
+ function normalizeSides(sides, defaultDefinition = undefined, transformer = v => v) {
230
+ if (sides == null || typeof sides === 'object' && Object.keys(sides).length === 0) {
231
+ sides = defaultDefinition;
232
+ }
233
+ if (sides == null || typeof sides !== 'object') {
234
+ sides = {
235
+ top: sides,
236
+ right: sides,
237
+ bottom: sides,
238
+ left: sides
239
+ };
240
+ } else if (Array.isArray(sides)) {
241
+ if (sides.length === 2) {
242
+ sides = {
243
+ vertical: sides[0],
244
+ horizontal: sides[1]
245
+ };
246
+ } else {
247
+ sides = {
248
+ top: sides[0],
249
+ right: sides[1],
250
+ bottom: sides[2],
251
+ left: sides[3]
252
+ };
253
+ }
254
+ }
255
+ if ('vertical' in sides || 'horizontal' in sides) {
256
+ sides = {
257
+ top: sides.vertical,
258
+ right: sides.horizontal,
259
+ bottom: sides.vertical,
260
+ left: sides.horizontal
261
+ };
262
+ }
263
+ return {
264
+ top: transformer(sides.top),
265
+ right: transformer(sides.right),
266
+ bottom: transformer(sides.bottom),
267
+ left: transformer(sides.left)
268
+ };
269
+ }
270
+ const MM_TO_CM = 1 / 10;
271
+ const CM_TO_IN = 1 / 2.54;
272
+ const PX_TO_IN = 1 / 96;
273
+ const IN_TO_PT = 72;
274
+ const PC_TO_PT = 12;
275
+ function cosine(a) {
276
+ if (a === 0) return 1;
277
+ if (a === 90) return 0;
278
+ if (a === 180) return -1;
279
+ if (a === 270) return 0;
280
+ return Math.cos(a * Math.PI / 180);
281
+ }
282
+ function sine(a) {
283
+ if (a === 0) return 0;
284
+ if (a === 90) return 1;
285
+ if (a === 180) return 0;
286
+ if (a === 270) return -1;
287
+ return Math.sin(a * Math.PI / 180);
288
+ }
267
289
 
268
290
  const DEFAULT_MARGINS = {
269
291
  top: 72,
@@ -326,35 +348,19 @@ const SIZES = {
326
348
  class PDFPage {
327
349
  constructor(document, options = {}) {
328
350
  this.document = document;
351
+ this._options = options;
329
352
  this.size = options.size || 'letter';
330
353
  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
354
  const dimensions = Array.isArray(this.size) ? this.size : SIZES[this.size.toUpperCase()];
348
355
  this.width = dimensions[this.layout === 'portrait' ? 0 : 1];
349
356
  this.height = dimensions[this.layout === 'portrait' ? 1 : 0];
350
357
  this.content = this.document.ref();
351
-
352
- // Initialize the Font, XObject, and ExtGState dictionaries
358
+ if (options.font) document.font(options.font, options.fontFamily);
359
+ if (options.fontSize) document.fontSize(options.fontSize);
360
+ this.margins = normalizeSides(options.margin ?? options.margins, DEFAULT_MARGINS, x => document.sizeToPoint(x, 0, this));
353
361
  this.resources = this.document.ref({
354
362
  ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI']
355
363
  });
356
-
357
- // The page dictionary
358
364
  this.dictionary = this.document.ref({
359
365
  Type: 'Page',
360
366
  Parent: this.document._root.data.Pages,
@@ -364,8 +370,6 @@ class PDFPage {
364
370
  });
365
371
  this.markings = [];
366
372
  }
367
-
368
- // Lazily create these objects
369
373
  get fonts() {
370
374
  const data = this.resources.data;
371
375
  return data.Font != null ? data.Font : data.Font = {};
@@ -394,14 +398,18 @@ class PDFPage {
394
398
  const data = this.dictionary.data;
395
399
  return data.StructParents != null ? data.StructParents : data.StructParents = this.document.createStructParentTreeNextKey();
396
400
  }
401
+ get contentWidth() {
402
+ return this.width - this.margins.left - this.margins.right;
403
+ }
404
+ get contentHeight() {
405
+ return this.height - this.margins.top - this.margins.bottom;
406
+ }
397
407
  maxY() {
398
408
  return this.height - this.margins.bottom;
399
409
  }
400
410
  write(chunk) {
401
411
  return this.content.write(chunk);
402
412
  }
403
-
404
- // Set tab order if document is tagged for accessibility.
405
413
  _setTabOrder() {
406
414
  if (!this.dictionary.Tabs && this.document.hasMarkInfoDictionary()) {
407
415
  this.dictionary.data.Tabs = 'S';
@@ -419,191 +427,57 @@ class PDFPage {
419
427
  }
420
428
  }
421
429
 
422
- /*
423
- PDFNameTree - represents a name tree object
424
- */
425
430
  class PDFNameTree extends PDFTree {
426
431
  _compareKeys(a, b) {
427
432
  return a.localeCompare(b);
428
433
  }
429
434
  _keysName() {
430
- return "Names";
435
+ return 'Names';
431
436
  }
432
437
  _dataForKey(k) {
433
438
  return new String(k);
434
439
  }
435
440
  }
436
441
 
437
- /**
438
- * Check if value is in a range group.
439
- * @param {number} value
440
- * @param {number[]} rangeGroup
441
- * @returns {boolean}
442
- */
443
442
  function inRange(value, rangeGroup) {
444
443
  if (value < rangeGroup[0]) return false;
445
444
  let startRange = 0;
446
445
  let endRange = rangeGroup.length / 2;
447
446
  while (startRange <= endRange) {
448
447
  const middleRange = Math.floor((startRange + endRange) / 2);
449
-
450
- // actual array index
451
448
  const arrayIndex = middleRange * 2;
452
-
453
- // Check if value is in range pointed by actual index
454
449
  if (value >= rangeGroup[arrayIndex] && value <= rangeGroup[arrayIndex + 1]) {
455
450
  return true;
456
451
  }
457
452
  if (value > rangeGroup[arrayIndex + 1]) {
458
- // Search Right Side Of Array
459
453
  startRange = middleRange + 1;
460
454
  } else {
461
- // Search Left Side Of Array
462
455
  endRange = middleRange - 1;
463
456
  }
464
457
  }
465
458
  return false;
466
459
  }
467
460
 
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
461
  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
462
  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
463
  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
464
  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
-
465
+ 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
466
  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
-
467
+ 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];
468
+ 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];
469
+ 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
470
  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
471
  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
472
  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
473
  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
474
  const isBidirectionalL = character => inRange(character, bidirectional_l);
580
475
 
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
476
  const mapping2space = isNonASCIISpaceCharacter;
588
-
589
- /**
590
- * the "commonly mapped to nothing" characters [StringPrep, B.1]
591
- * that can be mapped to nothing.
592
- */
593
477
  const mapping2nothing = isCommonlyMappedToNothing;
594
-
595
- // utils
596
478
  const getCodePoint = character => character.codePointAt(0);
597
479
  const first = x => x[0];
598
480
  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
481
  function toCodePoints(input) {
608
482
  const codepoints = [];
609
483
  const size = input.length;
@@ -621,14 +495,6 @@ function toCodePoints(input) {
621
495
  }
622
496
  return codepoints;
623
497
  }
624
-
625
- /**
626
- * SASLprep.
627
- * @param {string} input
628
- * @param {Object} opts
629
- * @param {boolean} opts.allowUnassigned
630
- * @returns {string}
631
- */
632
498
  function saslprep(input, opts = {}) {
633
499
  if (typeof input !== 'string') {
634
500
  throw new TypeError('Expected string.');
@@ -636,49 +502,24 @@ function saslprep(input, opts = {}) {
636
502
  if (input.length === 0) {
637
503
  return '';
638
504
  }
639
-
640
- // 1. Map
641
- const mapped_input = toCodePoints(input)
642
- // 1.1 mapping to space
643
- .map(character => mapping2space(character) ? 0x20 : character)
644
- // 1.2 mapping to nothing
645
- .filter(character => !mapping2nothing(character));
646
-
647
- // 2. Normalize
505
+ const mapped_input = toCodePoints(input).map(character => mapping2space(character) ? 0x20 : character).filter(character => !mapping2nothing(character));
648
506
  const normalized_input = String.fromCodePoint.apply(null, mapped_input).normalize('NFKC');
649
507
  const normalized_map = toCodePoints(normalized_input);
650
-
651
- // 3. Prohibit
652
508
  const hasProhibited = normalized_map.some(isProhibitedCharacter);
653
509
  if (hasProhibited) {
654
510
  throw new Error('Prohibited character, see https://tools.ietf.org/html/rfc4013#section-2.3');
655
511
  }
656
-
657
- // Unassigned Code Points
658
512
  if (opts.allowUnassigned !== true) {
659
513
  const hasUnassigned = normalized_map.some(isUnassignedCodePoint);
660
514
  if (hasUnassigned) {
661
515
  throw new Error('Unassigned code point, see https://tools.ietf.org/html/rfc4013#section-2.5');
662
516
  }
663
517
  }
664
-
665
- // 4. check bidi
666
-
667
518
  const hasBidiRAL = normalized_map.some(isBidirectionalRAL);
668
519
  const hasBidiL = normalized_map.some(isBidirectionalL);
669
-
670
- // 4.1 If a string contains any RandALCat character, the string MUST NOT
671
- // contain any LCat character.
672
520
  if (hasBidiRAL && hasBidiL) {
673
521
  throw new Error('String must not contain RandALCat and LCat at the same time,' + ' see https://tools.ietf.org/html/rfc3454#section-6');
674
522
  }
675
-
676
- /**
677
- * 4.2 If a string contains any RandALCat character, a RandALCat
678
- * character MUST be the first character of the string, and a
679
- * RandALCat character MUST be the last character of the string.
680
- */
681
-
682
523
  const isFirstBidiRAL = isBidirectionalRAL(getCodePoint(first(normalized_input)));
683
524
  const isLastBidiRAL = isBidirectionalRAL(getCodePoint(last(normalized_input)));
684
525
  if (hasBidiRAL && !(isFirstBidiRAL && isLastBidiRAL)) {
@@ -687,15 +528,10 @@ function saslprep(input, opts = {}) {
687
528
  return normalized_input;
688
529
  }
689
530
 
690
- /*
691
- PDFSecurity - represents PDF security settings
692
- By Yang Liu <hi@zesik.com>
693
- */
694
531
  class PDFSecurity {
695
532
  static generateFileID(info = {}) {
696
533
  let infoStr = `${info.CreationDate.getTime()}\n`;
697
534
  for (let key in info) {
698
- // eslint-disable-next-line no-prototype-builtins
699
535
  if (!info.hasOwnProperty(key)) {
700
536
  continue;
701
537
  }
@@ -1069,8 +905,6 @@ class PDFGradient$1 {
1069
905
  }
1070
906
  this.embedded = true;
1071
907
  this.matrix = m;
1072
-
1073
- // if the last stop comes before 100%, add a copy at 100%
1074
908
  const last = this.stops[stopsLength - 1];
1075
909
  if (last[0] < 1) {
1076
910
  this.stops.push([1, last[1], last[2]]);
@@ -1093,14 +927,11 @@ class PDFGradient$1 {
1093
927
  stops.push(fn);
1094
928
  fn.end();
1095
929
  }
1096
-
1097
- // if there are only two stops, we don't need a stitching function
1098
930
  if (stopsLength === 1) {
1099
931
  fn = stops[0];
1100
932
  } else {
1101
933
  fn = this.doc.ref({
1102
934
  FunctionType: 3,
1103
- // stitching function
1104
935
  Domain: [0, 1],
1105
936
  Functions: stops,
1106
937
  Bounds: bounds,
@@ -1181,7 +1012,6 @@ class PDFGradient$1 {
1181
1012
  return pattern;
1182
1013
  }
1183
1014
  apply(stroke) {
1184
- // apply gradient transform to existing document ctm
1185
1015
  const [m0, m1, m2, m3, m4, m5] = this.doc._ctm;
1186
1016
  const [m11, m12, m21, m22, dx, dy] = this.transform;
1187
1017
  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];
@@ -1244,10 +1074,6 @@ var Gradient = {
1244
1074
  PDFRadialGradient: PDFRadialGradient$1
1245
1075
  };
1246
1076
 
1247
- /*
1248
- PDF tiling pattern support. Uncolored only.
1249
- */
1250
-
1251
1077
  const underlyingColorSpaces = ['DeviceCMYK', 'DeviceRGB'];
1252
1078
  class PDFTilingPattern$1 {
1253
1079
  constructor(doc, bBox, xStep, yStep, stream) {
@@ -1258,23 +1084,16 @@ class PDFTilingPattern$1 {
1258
1084
  this.stream = stream;
1259
1085
  }
1260
1086
  createPattern() {
1261
- // no resources needed for our current usage
1262
- // required entry
1263
1087
  const resources = this.doc.ref();
1264
1088
  resources.end();
1265
- // apply default transform matrix (flipped in the default doc._ctm)
1266
- // see document.js & gradient.js
1267
1089
  const [m0, m1, m2, m3, m4, m5] = this.doc._ctm;
1268
1090
  const [m11, m12, m21, m22, dx, dy] = [1, 0, 0, 1, 0, 0];
1269
1091
  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];
1270
1092
  const pattern = this.doc.ref({
1271
1093
  Type: 'Pattern',
1272
1094
  PatternType: 1,
1273
- // tiling
1274
1095
  PaintType: 2,
1275
- // 1-colored, 2-uncolored
1276
1096
  TilingType: 2,
1277
- // 2-no distortion
1278
1097
  BBox: this.bBox,
1279
1098
  XStep: this.xStep,
1280
1099
  YStep: this.yStep,
@@ -1285,8 +1104,6 @@ class PDFTilingPattern$1 {
1285
1104
  return pattern;
1286
1105
  }
1287
1106
  embedPatternColorSpaces() {
1288
- // map each pattern to an underlying color space
1289
- // and embed on each page
1290
1107
  underlyingColorSpaces.forEach(csName => {
1291
1108
  const csId = this.getPatternColorSpaceId(csName);
1292
1109
  if (this.doc.page.colorSpaces[csId]) return;
@@ -1304,24 +1121,17 @@ class PDFTilingPattern$1 {
1304
1121
  this.id = 'P' + this.doc._patternCount;
1305
1122
  this.pattern = this.createPattern();
1306
1123
  }
1307
-
1308
- // patterns are embedded in each page
1309
1124
  if (!this.doc.page.patterns[this.id]) {
1310
1125
  this.doc.page.patterns[this.id] = this.pattern;
1311
1126
  }
1312
1127
  }
1313
1128
  apply(stroke, patternColor) {
1314
- // do any embedding/creating that might be needed
1315
1129
  this.embedPatternColorSpaces();
1316
1130
  this.embed();
1317
1131
  const normalizedColor = this.doc._normalizeColor(patternColor);
1318
1132
  if (!normalizedColor) throw Error(`invalid pattern color. (value: ${patternColor})`);
1319
-
1320
- // select one of the pattern color spaces
1321
1133
  const csId = this.getPatternColorSpaceId(this.doc._getColorSpace(normalizedColor));
1322
1134
  this.doc._setColorSpace(csId, stroke);
1323
-
1324
- // stroke/fill using the pattern and color (in the above underlying color space)
1325
1135
  const op = stroke ? 'SCN' : 'scn';
1326
1136
  return this.doc.addContent(`${normalizedColor.join(' ')} /${this.id} ${op}`);
1327
1137
  }
@@ -1341,11 +1151,10 @@ const {
1341
1151
  var ColorMixin = {
1342
1152
  initColor() {
1343
1153
  this.spotColors = {};
1344
- // The opacity dictionaries
1345
1154
  this._opacityRegistry = {};
1346
1155
  this._opacityCount = 0;
1347
1156
  this._patternCount = 0;
1348
- return this._gradCount = 0;
1157
+ this._gradCount = 0;
1349
1158
  },
1350
1159
  _normalizeColor(color) {
1351
1160
  if (typeof color === 'string') {
@@ -1362,10 +1171,8 @@ var ColorMixin = {
1362
1171
  }
1363
1172
  }
1364
1173
  if (Array.isArray(color)) {
1365
- // RGB
1366
1174
  if (color.length === 3) {
1367
1175
  color = color.map(part => part / 255);
1368
- // CMYK
1369
1176
  } else if (color.length === 4) {
1370
1177
  color = color.map(part => part / 100);
1371
1178
  }
@@ -1377,12 +1184,10 @@ var ColorMixin = {
1377
1184
  if (color instanceof PDFGradient) {
1378
1185
  color.apply(stroke);
1379
1186
  return true;
1380
- // see if tiling pattern, decode & apply it it
1381
1187
  } else if (Array.isArray(color) && color[0] instanceof PDFTilingPattern) {
1382
1188
  color[0].apply(stroke, color[1]);
1383
1189
  return true;
1384
1190
  }
1385
- // any other case should be a normal color and not a pattern
1386
1191
  return this._setColorCore(color, stroke);
1387
1192
  },
1388
1193
  _setColorCore(color, stroke) {
@@ -1416,9 +1221,6 @@ var ColorMixin = {
1416
1221
  if (set) {
1417
1222
  this.fillOpacity(opacity);
1418
1223
  }
1419
-
1420
- // save this for text wrapper, which needs to reset
1421
- // the fill color on new pages
1422
1224
  this._fillColor = [color, opacity];
1423
1225
  return this;
1424
1226
  },
@@ -1674,7 +1476,6 @@ const parse = function (path) {
1674
1476
  if (parameters[c] != null) {
1675
1477
  params = parameters[c];
1676
1478
  if (cmd) {
1677
- // save existing command
1678
1479
  if (curArg.length > 0) {
1679
1480
  args[args.length] = +curArg;
1680
1481
  }
@@ -1692,14 +1493,11 @@ const parse = function (path) {
1692
1493
  continue;
1693
1494
  }
1694
1495
  if (args.length === params) {
1695
- // handle reused commands
1696
1496
  ret[ret.length] = {
1697
1497
  cmd,
1698
1498
  args
1699
1499
  };
1700
1500
  args = [+curArg];
1701
-
1702
- // handle assumed commands
1703
1501
  if (cmd === 'M') {
1704
1502
  cmd = 'L';
1705
1503
  }
@@ -1710,8 +1508,6 @@ const parse = function (path) {
1710
1508
  args[args.length] = +curArg;
1711
1509
  }
1712
1510
  foundDecimal = c === '.';
1713
-
1714
- // fix for negative numbers or repeated decimals with no delimeter between commands
1715
1511
  curArg = ['-', '.'].includes(c) ? c : '';
1716
1512
  } else {
1717
1513
  curArg += c;
@@ -1720,18 +1516,13 @@ const parse = function (path) {
1720
1516
  }
1721
1517
  }
1722
1518
  }
1723
-
1724
- // add the last command
1725
1519
  if (curArg.length > 0) {
1726
1520
  if (args.length === params) {
1727
- // handle reused commands
1728
1521
  ret[ret.length] = {
1729
1522
  cmd,
1730
1523
  args
1731
1524
  };
1732
1525
  args = [+curArg];
1733
-
1734
- // handle assumed commands
1735
1526
  if (cmd === 'M') {
1736
1527
  cmd = 'L';
1737
1528
  }
@@ -1749,10 +1540,7 @@ const parse = function (path) {
1749
1540
  return ret;
1750
1541
  };
1751
1542
  const apply = function (commands, doc) {
1752
- // current point, control point, and subpath starting point
1753
1543
  cx = cy = px = py = sx = sy = 0;
1754
-
1755
- // run the commands
1756
1544
  for (let i = 0; i < commands.length; i++) {
1757
1545
  const c = commands[i];
1758
1546
  if (typeof runners[c.cmd] === 'function') {
@@ -1916,8 +1704,6 @@ const solveArc = function (doc, x, y, coords) {
1916
1704
  doc.bezierCurveTo(...bez);
1917
1705
  }
1918
1706
  };
1919
-
1920
- // from Inkscape svgtopdf, thanks!
1921
1707
  const arcToSegments = function (x, y, rx, ry, large, sweep, rotateX, ox, oy) {
1922
1708
  const th = rotateX * (Math.PI / 180);
1923
1709
  const sin_th = Math.sin(th);
@@ -1993,18 +1779,14 @@ class SVGPath {
1993
1779
  const {
1994
1780
  number: number$1
1995
1781
  } = PDFObject;
1996
-
1997
- // This constant is used to approximate a symmetrical arc using a cubic
1998
- // Bezier curve.
1999
1782
  const KAPPA = 4.0 * ((Math.sqrt(2) - 1.0) / 3.0);
2000
1783
  var VectorMixin = {
2001
1784
  initVector() {
2002
- this._ctm = [1, 0, 0, 1, 0, 0]; // current transformation matrix
2003
- return this._ctmStack = [];
1785
+ this._ctm = [1, 0, 0, 1, 0, 0];
1786
+ this._ctmStack = [];
2004
1787
  },
2005
1788
  save() {
2006
1789
  this._ctmStack.push(this._ctm.slice());
2007
- // TODO: save/restore colorspace and styles so not setting it unnessesarily all the time?
2008
1790
  return this.addContent('q');
2009
1791
  },
2010
1792
  restore() {
@@ -2077,8 +1859,6 @@ var VectorMixin = {
2077
1859
  r = 0;
2078
1860
  }
2079
1861
  r = Math.min(r, 0.5 * w, 0.5 * h);
2080
-
2081
- // amount to inset control points from corners (see `ellipse`)
2082
1862
  const c = r * (1.0 - KAPPA);
2083
1863
  this.moveTo(x + r, y);
2084
1864
  this.lineTo(x + w - r, y);
@@ -2092,7 +1872,6 @@ var VectorMixin = {
2092
1872
  return this.closePath();
2093
1873
  },
2094
1874
  ellipse(x, y, r1, r2) {
2095
- // based on http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas/2173084#2173084
2096
1875
  if (r2 == null) {
2097
1876
  r2 = r1;
2098
1877
  }
@@ -2122,10 +1901,8 @@ var VectorMixin = {
2122
1901
  const HALF_PI = 0.5 * Math.PI;
2123
1902
  let deltaAng = endAngle - startAngle;
2124
1903
  if (Math.abs(deltaAng) > TWO_PI) {
2125
- // draw only full circle if more than that is specified
2126
1904
  deltaAng = TWO_PI;
2127
1905
  } else if (deltaAng !== 0 && anticlockwise !== deltaAng < 0) {
2128
- // necessary to flip direction of rendering
2129
1906
  const dir = anticlockwise ? -1 : 1;
2130
1907
  deltaAng = dir * TWO_PI + deltaAng;
2131
1908
  }
@@ -2133,38 +1910,21 @@ var VectorMixin = {
2133
1910
  const segAng = deltaAng / numSegs;
2134
1911
  const handleLen = segAng / HALF_PI * KAPPA * radius;
2135
1912
  let curAng = startAngle;
2136
-
2137
- // component distances between anchor point and control point
2138
1913
  let deltaCx = -Math.sin(curAng) * handleLen;
2139
1914
  let deltaCy = Math.cos(curAng) * handleLen;
2140
-
2141
- // anchor point
2142
1915
  let ax = x + Math.cos(curAng) * radius;
2143
1916
  let ay = y + Math.sin(curAng) * radius;
2144
-
2145
- // calculate and render segments
2146
1917
  this.moveTo(ax, ay);
2147
1918
  for (let segIdx = 0; segIdx < numSegs; segIdx++) {
2148
- // starting control point
2149
1919
  const cp1x = ax + deltaCx;
2150
1920
  const cp1y = ay + deltaCy;
2151
-
2152
- // step angle
2153
1921
  curAng += segAng;
2154
-
2155
- // next anchor point
2156
1922
  ax = x + Math.cos(curAng) * radius;
2157
1923
  ay = y + Math.sin(curAng) * radius;
2158
-
2159
- // next control point delta
2160
1924
  deltaCx = -Math.sin(curAng) * handleLen;
2161
1925
  deltaCy = Math.cos(curAng) * handleLen;
2162
-
2163
- // ending control point
2164
1926
  const cp2x = ax - deltaCx;
2165
1927
  const cp2y = ay - deltaCy;
2166
-
2167
- // render segment
2168
1928
  this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, ax, ay);
2169
1929
  }
2170
1930
  return this;
@@ -2225,9 +1985,7 @@ var VectorMixin = {
2225
1985
  return this.addContent(`W${this._windingRule(rule)} n`);
2226
1986
  },
2227
1987
  transform(m11, m12, m21, m22, dx, dy) {
2228
- // keep track of the current transformation matrix
2229
1988
  if (m11 === 1 && m12 === 0 && m21 === 0 && m22 === 1 && dx === 0 && dy === 0) {
2230
- // Ignore identity transforms
2231
1989
  return this;
2232
1990
  }
2233
1991
  const m = this._ctm;
@@ -2391,7 +2149,6 @@ class AFMFont {
2391
2149
  this.boundingBoxes = {};
2392
2150
  this.kernPairs = {};
2393
2151
  this.parse();
2394
- // todo: remove charWidths since appears to not be used
2395
2152
  this.charWidths = new Array(256);
2396
2153
  for (let char = 0; char <= 255; char++) {
2397
2154
  this.charWidths[char] = this.glyphWidths[characters[char]];
@@ -2498,21 +2255,17 @@ class PDFFont {
2498
2255
  return;
2499
2256
  }
2500
2257
  this.embed();
2501
- return this.embedded = true;
2258
+ this.embedded = true;
2502
2259
  }
2503
2260
  embed() {
2504
2261
  throw new Error('Must be implemented by subclasses');
2505
2262
  }
2506
- lineHeight(size, includeGap) {
2507
- if (includeGap == null) {
2508
- includeGap = false;
2509
- }
2263
+ lineHeight(size, includeGap = false) {
2510
2264
  const gap = includeGap ? this.lineGap : 0;
2511
2265
  return (this.ascender + gap - this.descender) / 1000 * size;
2512
2266
  }
2513
2267
  }
2514
2268
 
2515
- // This insanity is so bundlers can inline the font files
2516
2269
  const STANDARD_FONTS = {
2517
2270
  Courier() {
2518
2271
  return "StartFontMetrics 4.1\r\nComment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.\r\nComment Creation Date: Thu May 1 17:27:09 1997\r\nComment UniqueID 43050\r\nComment VMusage 39754 50779\r\nFontName Courier\r\nFullName Courier\r\nFamilyName Courier\r\nWeight Medium\r\nItalicAngle 0\r\nIsFixedPitch true\r\nCharacterSet ExtendedRoman\r\nFontBBox -23 -250 715 805 \r\nUnderlinePosition -100\r\nUnderlineThickness 50\r\nVersion 003.000\r\nNotice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.\r\nEncodingScheme AdobeStandardEncoding\r\nCapHeight 562\r\nXHeight 426\r\nAscender 629\r\nDescender -157\r\nStdHW 51\r\nStdVW 51\r\nStartCharMetrics 315\r\nC 32 ; WX 600 ; N space ; B 0 0 0 0 ;\r\nC 33 ; WX 600 ; N exclam ; B 236 -15 364 572 ;\r\nC 34 ; WX 600 ; N quotedbl ; B 187 328 413 562 ;\r\nC 35 ; WX 600 ; N numbersign ; B 93 -32 507 639 ;\r\nC 36 ; WX 600 ; N dollar ; B 105 -126 496 662 ;\r\nC 37 ; WX 600 ; N percent ; B 81 -15 518 622 ;\r\nC 38 ; WX 600 ; N ampersand ; B 63 -15 538 543 ;\r\nC 39 ; WX 600 ; N quoteright ; B 213 328 376 562 ;\r\nC 40 ; WX 600 ; N parenleft ; B 269 -108 440 622 ;\r\nC 41 ; WX 600 ; N parenright ; B 160 -108 331 622 ;\r\nC 42 ; WX 600 ; N asterisk ; B 116 257 484 607 ;\r\nC 43 ; WX 600 ; N plus ; B 80 44 520 470 ;\r\nC 44 ; WX 600 ; N comma ; B 181 -112 344 122 ;\r\nC 45 ; WX 600 ; N hyphen ; B 103 231 497 285 ;\r\nC 46 ; WX 600 ; N period ; B 229 -15 371 109 ;\r\nC 47 ; WX 600 ; N slash ; B 125 -80 475 629 ;\r\nC 48 ; WX 600 ; N zero ; B 106 -15 494 622 ;\r\nC 49 ; WX 600 ; N one ; B 96 0 505 622 ;\r\nC 50 ; WX 600 ; N two ; B 70 0 471 622 ;\r\nC 51 ; WX 600 ; N three ; B 75 -15 466 622 ;\r\nC 52 ; WX 600 ; N four ; B 78 0 500 622 ;\r\nC 53 ; WX 600 ; N five ; B 92 -15 497 607 ;\r\nC 54 ; WX 600 ; N six ; B 111 -15 497 622 ;\r\nC 55 ; WX 600 ; N seven ; B 82 0 483 607 ;\r\nC 56 ; WX 600 ; N eight ; B 102 -15 498 622 ;\r\nC 57 ; WX 600 ; N nine ; B 96 -15 489 622 ;\r\nC 58 ; WX 600 ; N colon ; B 229 -15 371 385 ;\r\nC 59 ; WX 600 ; N semicolon ; B 181 -112 371 385 ;\r\nC 60 ; WX 600 ; N less ; B 41 42 519 472 ;\r\nC 61 ; WX 600 ; N equal ; B 80 138 520 376 ;\r\nC 62 ; WX 600 ; N greater ; B 66 42 544 472 ;\r\nC 63 ; WX 600 ; N question ; B 129 -15 492 572 ;\r\nC 64 ; WX 600 ; N at ; B 77 -15 533 622 ;\r\nC 65 ; WX 600 ; N A ; B 3 0 597 562 ;\r\nC 66 ; WX 600 ; N B ; B 43 0 559 562 ;\r\nC 67 ; WX 600 ; N C ; B 41 -18 540 580 ;\r\nC 68 ; WX 600 ; N D ; B 43 0 574 562 ;\r\nC 69 ; WX 600 ; N E ; B 53 0 550 562 ;\r\nC 70 ; WX 600 ; N F ; B 53 0 545 562 ;\r\nC 71 ; WX 600 ; N G ; B 31 -18 575 580 ;\r\nC 72 ; WX 600 ; N H ; B 32 0 568 562 ;\r\nC 73 ; WX 600 ; N I ; B 96 0 504 562 ;\r\nC 74 ; WX 600 ; N J ; B 34 -18 566 562 ;\r\nC 75 ; WX 600 ; N K ; B 38 0 582 562 ;\r\nC 76 ; WX 600 ; N L ; B 47 0 554 562 ;\r\nC 77 ; WX 600 ; N M ; B 4 0 596 562 ;\r\nC 78 ; WX 600 ; N N ; B 7 -13 593 562 ;\r\nC 79 ; WX 600 ; N O ; B 43 -18 557 580 ;\r\nC 80 ; WX 600 ; N P ; B 79 0 558 562 ;\r\nC 81 ; WX 600 ; N Q ; B 43 -138 557 580 ;\r\nC 82 ; WX 600 ; N R ; B 38 0 588 562 ;\r\nC 83 ; WX 600 ; N S ; B 72 -20 529 580 ;\r\nC 84 ; WX 600 ; N T ; B 38 0 563 562 ;\r\nC 85 ; WX 600 ; N U ; B 17 -18 583 562 ;\r\nC 86 ; WX 600 ; N V ; B -4 -13 604 562 ;\r\nC 87 ; WX 600 ; N W ; B -3 -13 603 562 ;\r\nC 88 ; WX 600 ; N X ; B 23 0 577 562 ;\r\nC 89 ; WX 600 ; N Y ; B 24 0 576 562 ;\r\nC 90 ; WX 600 ; N Z ; B 86 0 514 562 ;\r\nC 91 ; WX 600 ; N bracketleft ; B 269 -108 442 622 ;\r\nC 92 ; WX 600 ; N backslash ; B 118 -80 482 629 ;\r\nC 93 ; WX 600 ; N bracketright ; B 158 -108 331 622 ;\r\nC 94 ; WX 600 ; N asciicircum ; B 94 354 506 622 ;\r\nC 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ;\r\nC 96 ; WX 600 ; N quoteleft ; B 224 328 387 562 ;\r\nC 97 ; WX 600 ; N a ; B 53 -15 559 441 ;\r\nC 98 ; WX 600 ; N b ; B 14 -15 575 629 ;\r\nC 99 ; WX 600 ; N c ; B 66 -15 529 441 ;\r\nC 100 ; WX 600 ; N d ; B 45 -15 591 629 ;\r\nC 101 ; WX 600 ; N e ; B 66 -15 548 441 ;\r\nC 102 ; WX 600 ; N f ; B 114 0 531 629 ; L i fi ; L l fl ;\r\nC 103 ; WX 600 ; N g ; B 45 -157 566 441 ;\r\nC 104 ; WX 600 ; N h ; B 18 0 582 629 ;\r\nC 105 ; WX 600 ; N i ; B 95 0 505 657 ;\r\nC 106 ; WX 600 ; N j ; B 82 -157 410 657 ;\r\nC 107 ; WX 600 ; N k ; B 43 0 580 629 ;\r\nC 108 ; WX 600 ; N l ; B 95 0 505 629 ;\r\nC 109 ; WX 600 ; N m ; B -5 0 605 441 ;\r\nC 110 ; WX 600 ; N n ; B 26 0 575 441 ;\r\nC 111 ; WX 600 ; N o ; B 62 -15 538 441 ;\r\nC 112 ; WX 600 ; N p ; B 9 -157 555 441 ;\r\nC 113 ; WX 600 ; N q ; B 45 -157 591 441 ;\r\nC 114 ; WX 600 ; N r ; B 60 0 559 441 ;\r\nC 115 ; WX 600 ; N s ; B 80 -15 513 441 ;\r\nC 116 ; WX 600 ; N t ; B 87 -15 530 561 ;\r\nC 117 ; WX 600 ; N u ; B 21 -15 562 426 ;\r\nC 118 ; WX 600 ; N v ; B 10 -10 590 426 ;\r\nC 119 ; WX 600 ; N w ; B -4 -10 604 426 ;\r\nC 120 ; WX 600 ; N x ; B 20 0 580 426 ;\r\nC 121 ; WX 600 ; N y ; B 7 -157 592 426 ;\r\nC 122 ; WX 600 ; N z ; B 99 0 502 426 ;\r\nC 123 ; WX 600 ; N braceleft ; B 182 -108 437 622 ;\r\nC 124 ; WX 600 ; N bar ; B 275 -250 326 750 ;\r\nC 125 ; WX 600 ; N braceright ; B 163 -108 418 622 ;\r\nC 126 ; WX 600 ; N asciitilde ; B 63 197 540 320 ;\r\nC 161 ; WX 600 ; N exclamdown ; B 236 -157 364 430 ;\r\nC 162 ; WX 600 ; N cent ; B 96 -49 500 614 ;\r\nC 163 ; WX 600 ; N sterling ; B 84 -21 521 611 ;\r\nC 164 ; WX 600 ; N fraction ; B 92 -57 509 665 ;\r\nC 165 ; WX 600 ; N yen ; B 26 0 574 562 ;\r\nC 166 ; WX 600 ; N florin ; B 4 -143 539 622 ;\r\nC 167 ; WX 600 ; N section ; B 113 -78 488 580 ;\r\nC 168 ; WX 600 ; N currency ; B 73 58 527 506 ;\r\nC 169 ; WX 600 ; N quotesingle ; B 259 328 341 562 ;\r\nC 170 ; WX 600 ; N quotedblleft ; B 143 328 471 562 ;\r\nC 171 ; WX 600 ; N guillemotleft ; B 37 70 563 446 ;\r\nC 172 ; WX 600 ; N guilsinglleft ; B 149 70 451 446 ;\r\nC 173 ; WX 600 ; N guilsinglright ; B 149 70 451 446 ;\r\nC 174 ; WX 600 ; N fi ; B 3 0 597 629 ;\r\nC 175 ; WX 600 ; N fl ; B 3 0 597 629 ;\r\nC 177 ; WX 600 ; N endash ; B 75 231 525 285 ;\r\nC 178 ; WX 600 ; N dagger ; B 141 -78 459 580 ;\r\nC 179 ; WX 600 ; N daggerdbl ; B 141 -78 459 580 ;\r\nC 180 ; WX 600 ; N periodcentered ; B 222 189 378 327 ;\r\nC 182 ; WX 600 ; N paragraph ; B 50 -78 511 562 ;\r\nC 183 ; WX 600 ; N bullet ; B 172 130 428 383 ;\r\nC 184 ; WX 600 ; N quotesinglbase ; B 213 -134 376 100 ;\r\nC 185 ; WX 600 ; N quotedblbase ; B 143 -134 457 100 ;\r\nC 186 ; WX 600 ; N quotedblright ; B 143 328 457 562 ;\r\nC 187 ; WX 600 ; N guillemotright ; B 37 70 563 446 ;\r\nC 188 ; WX 600 ; N ellipsis ; B 37 -15 563 111 ;\r\nC 189 ; WX 600 ; N perthousand ; B 3 -15 600 622 ;\r\nC 191 ; WX 600 ; N questiondown ; B 108 -157 471 430 ;\r\nC 193 ; WX 600 ; N grave ; B 151 497 378 672 ;\r\nC 194 ; WX 600 ; N acute ; B 242 497 469 672 ;\r\nC 195 ; WX 600 ; N circumflex ; B 124 477 476 654 ;\r\nC 196 ; WX 600 ; N tilde ; B 105 489 503 606 ;\r\nC 197 ; WX 600 ; N macron ; B 120 525 480 565 ;\r\nC 198 ; WX 600 ; N breve ; B 153 501 447 609 ;\r\nC 199 ; WX 600 ; N dotaccent ; B 249 537 352 640 ;\r\nC 200 ; WX 600 ; N dieresis ; B 148 537 453 640 ;\r\nC 202 ; WX 600 ; N ring ; B 218 463 382 627 ;\r\nC 203 ; WX 600 ; N cedilla ; B 224 -151 362 10 ;\r\nC 205 ; WX 600 ; N hungarumlaut ; B 133 497 540 672 ;\r\nC 206 ; WX 600 ; N ogonek ; B 211 -172 407 4 ;\r\nC 207 ; WX 600 ; N caron ; B 124 492 476 669 ;\r\nC 208 ; WX 600 ; N emdash ; B 0 231 600 285 ;\r\nC 225 ; WX 600 ; N AE ; B 3 0 550 562 ;\r\nC 227 ; WX 600 ; N ordfeminine ; B 156 249 442 580 ;\r\nC 232 ; WX 600 ; N Lslash ; B 47 0 554 562 ;\r\nC 233 ; WX 600 ; N Oslash ; B 43 -80 557 629 ;\r\nC 234 ; WX 600 ; N OE ; B 7 0 567 562 ;\r\nC 235 ; WX 600 ; N ordmasculine ; B 157 249 443 580 ;\r\nC 241 ; WX 600 ; N ae ; B 19 -15 570 441 ;\r\nC 245 ; WX 600 ; N dotlessi ; B 95 0 505 426 ;\r\nC 248 ; WX 600 ; N lslash ; B 95 0 505 629 ;\r\nC 249 ; WX 600 ; N oslash ; B 62 -80 538 506 ;\r\nC 250 ; WX 600 ; N oe ; B 19 -15 559 441 ;\r\nC 251 ; WX 600 ; N germandbls ; B 48 -15 588 629 ;\r\nC -1 ; WX 600 ; N Idieresis ; B 96 0 504 753 ;\r\nC -1 ; WX 600 ; N eacute ; B 66 -15 548 672 ;\r\nC -1 ; WX 600 ; N abreve ; B 53 -15 559 609 ;\r\nC -1 ; WX 600 ; N uhungarumlaut ; B 21 -15 580 672 ;\r\nC -1 ; WX 600 ; N ecaron ; B 66 -15 548 669 ;\r\nC -1 ; WX 600 ; N Ydieresis ; B 24 0 576 753 ;\r\nC -1 ; WX 600 ; N divide ; B 87 48 513 467 ;\r\nC -1 ; WX 600 ; N Yacute ; B 24 0 576 805 ;\r\nC -1 ; WX 600 ; N Acircumflex ; B 3 0 597 787 ;\r\nC -1 ; WX 600 ; N aacute ; B 53 -15 559 672 ;\r\nC -1 ; WX 600 ; N Ucircumflex ; B 17 -18 583 787 ;\r\nC -1 ; WX 600 ; N yacute ; B 7 -157 592 672 ;\r\nC -1 ; WX 600 ; N scommaaccent ; B 80 -250 513 441 ;\r\nC -1 ; WX 600 ; N ecircumflex ; B 66 -15 548 654 ;\r\nC -1 ; WX 600 ; N Uring ; B 17 -18 583 760 ;\r\nC -1 ; WX 600 ; N Udieresis ; B 17 -18 583 753 ;\r\nC -1 ; WX 600 ; N aogonek ; B 53 -172 587 441 ;\r\nC -1 ; WX 600 ; N Uacute ; B 17 -18 583 805 ;\r\nC -1 ; WX 600 ; N uogonek ; B 21 -172 590 426 ;\r\nC -1 ; WX 600 ; N Edieresis ; B 53 0 550 753 ;\r\nC -1 ; WX 600 ; N Dcroat ; B 30 0 574 562 ;\r\nC -1 ; WX 600 ; N commaaccent ; B 198 -250 335 -58 ;\r\nC -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ;\r\nC -1 ; WX 600 ; N Emacron ; B 53 0 550 698 ;\r\nC -1 ; WX 600 ; N ccaron ; B 66 -15 529 669 ;\r\nC -1 ; WX 600 ; N aring ; B 53 -15 559 627 ;\r\nC -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 593 562 ;\r\nC -1 ; WX 600 ; N lacute ; B 95 0 505 805 ;\r\nC -1 ; WX 600 ; N agrave ; B 53 -15 559 672 ;\r\nC -1 ; WX 600 ; N Tcommaaccent ; B 38 -250 563 562 ;\r\nC -1 ; WX 600 ; N Cacute ; B 41 -18 540 805 ;\r\nC -1 ; WX 600 ; N atilde ; B 53 -15 559 606 ;\r\nC -1 ; WX 600 ; N Edotaccent ; B 53 0 550 753 ;\r\nC -1 ; WX 600 ; N scaron ; B 80 -15 513 669 ;\r\nC -1 ; WX 600 ; N scedilla ; B 80 -151 513 441 ;\r\nC -1 ; WX 600 ; N iacute ; B 95 0 505 672 ;\r\nC -1 ; WX 600 ; N lozenge ; B 18 0 443 706 ;\r\nC -1 ; WX 600 ; N Rcaron ; B 38 0 588 802 ;\r\nC -1 ; WX 600 ; N Gcommaaccent ; B 31 -250 575 580 ;\r\nC -1 ; WX 600 ; N ucircumflex ; B 21 -15 562 654 ;\r\nC -1 ; WX 600 ; N acircumflex ; B 53 -15 559 654 ;\r\nC -1 ; WX 600 ; N Amacron ; B 3 0 597 698 ;\r\nC -1 ; WX 600 ; N rcaron ; B 60 0 559 669 ;\r\nC -1 ; WX 600 ; N ccedilla ; B 66 -151 529 441 ;\r\nC -1 ; WX 600 ; N Zdotaccent ; B 86 0 514 753 ;\r\nC -1 ; WX 600 ; N Thorn ; B 79 0 538 562 ;\r\nC -1 ; WX 600 ; N Omacron ; B 43 -18 557 698 ;\r\nC -1 ; WX 600 ; N Racute ; B 38 0 588 805 ;\r\nC -1 ; WX 600 ; N Sacute ; B 72 -20 529 805 ;\r\nC -1 ; WX 600 ; N dcaron ; B 45 -15 715 629 ;\r\nC -1 ; WX 600 ; N Umacron ; B 17 -18 583 698 ;\r\nC -1 ; WX 600 ; N uring ; B 21 -15 562 627 ;\r\nC -1 ; WX 600 ; N threesuperior ; B 155 240 406 622 ;\r\nC -1 ; WX 600 ; N Ograve ; B 43 -18 557 805 ;\r\nC -1 ; WX 600 ; N Agrave ; B 3 0 597 805 ;\r\nC -1 ; WX 600 ; N Abreve ; B 3 0 597 732 ;\r\nC -1 ; WX 600 ; N multiply ; B 87 43 515 470 ;\r\nC -1 ; WX 600 ; N uacute ; B 21 -15 562 672 ;\r\nC -1 ; WX 600 ; N Tcaron ; B 38 0 563 802 ;\r\nC -1 ; WX 600 ; N partialdiff ; B 17 -38 459 710 ;\r\nC -1 ; WX 600 ; N ydieresis ; B 7 -157 592 620 ;\r\nC -1 ; WX 600 ; N Nacute ; B 7 -13 593 805 ;\r\nC -1 ; WX 600 ; N icircumflex ; B 94 0 505 654 ;\r\nC -1 ; WX 600 ; N Ecircumflex ; B 53 0 550 787 ;\r\nC -1 ; WX 600 ; N adieresis ; B 53 -15 559 620 ;\r\nC -1 ; WX 600 ; N edieresis ; B 66 -15 548 620 ;\r\nC -1 ; WX 600 ; N cacute ; B 66 -15 529 672 ;\r\nC -1 ; WX 600 ; N nacute ; B 26 0 575 672 ;\r\nC -1 ; WX 600 ; N umacron ; B 21 -15 562 565 ;\r\nC -1 ; WX 600 ; N Ncaron ; B 7 -13 593 802 ;\r\nC -1 ; WX 600 ; N Iacute ; B 96 0 504 805 ;\r\nC -1 ; WX 600 ; N plusminus ; B 87 44 513 558 ;\r\nC -1 ; WX 600 ; N brokenbar ; B 275 -175 326 675 ;\r\nC -1 ; WX 600 ; N registered ; B 0 -18 600 580 ;\r\nC -1 ; WX 600 ; N Gbreve ; B 31 -18 575 732 ;\r\nC -1 ; WX 600 ; N Idotaccent ; B 96 0 504 753 ;\r\nC -1 ; WX 600 ; N summation ; B 15 -10 585 706 ;\r\nC -1 ; WX 600 ; N Egrave ; B 53 0 550 805 ;\r\nC -1 ; WX 600 ; N racute ; B 60 0 559 672 ;\r\nC -1 ; WX 600 ; N omacron ; B 62 -15 538 565 ;\r\nC -1 ; WX 600 ; N Zacute ; B 86 0 514 805 ;\r\nC -1 ; WX 600 ; N Zcaron ; B 86 0 514 802 ;\r\nC -1 ; WX 600 ; N greaterequal ; B 98 0 502 710 ;\r\nC -1 ; WX 600 ; N Eth ; B 30 0 574 562 ;\r\nC -1 ; WX 600 ; N Ccedilla ; B 41 -151 540 580 ;\r\nC -1 ; WX 600 ; N lcommaaccent ; B 95 -250 505 629 ;\r\nC -1 ; WX 600 ; N tcaron ; B 87 -15 530 717 ;\r\nC -1 ; WX 600 ; N eogonek ; B 66 -172 548 441 ;\r\nC -1 ; WX 600 ; N Uogonek ; B 17 -172 583 562 ;\r\nC -1 ; WX 600 ; N Aacute ; B 3 0 597 805 ;\r\nC -1 ; WX 600 ; N Adieresis ; B 3 0 597 753 ;\r\nC -1 ; WX 600 ; N egrave ; B 66 -15 548 672 ;\r\nC -1 ; WX 600 ; N zacute ; B 99 0 502 672 ;\r\nC -1 ; WX 600 ; N iogonek ; B 95 -172 505 657 ;\r\nC -1 ; WX 600 ; N Oacute ; B 43 -18 557 805 ;\r\nC -1 ; WX 600 ; N oacute ; B 62 -15 538 672 ;\r\nC -1 ; WX 600 ; N amacron ; B 53 -15 559 565 ;\r\nC -1 ; WX 600 ; N sacute ; B 80 -15 513 672 ;\r\nC -1 ; WX 600 ; N idieresis ; B 95 0 505 620 ;\r\nC -1 ; WX 600 ; N Ocircumflex ; B 43 -18 557 787 ;\r\nC -1 ; WX 600 ; N Ugrave ; B 17 -18 583 805 ;\r\nC -1 ; WX 600 ; N Delta ; B 6 0 598 688 ;\r\nC -1 ; WX 600 ; N thorn ; B -6 -157 555 629 ;\r\nC -1 ; WX 600 ; N twosuperior ; B 177 249 424 622 ;\r\nC -1 ; WX 600 ; N Odieresis ; B 43 -18 557 753 ;\r\nC -1 ; WX 600 ; N mu ; B 21 -157 562 426 ;\r\nC -1 ; WX 600 ; N igrave ; B 95 0 505 672 ;\r\nC -1 ; WX 600 ; N ohungarumlaut ; B 62 -15 580 672 ;\r\nC -1 ; WX 600 ; N Eogonek ; B 53 -172 561 562 ;\r\nC -1 ; WX 600 ; N dcroat ; B 45 -15 591 629 ;\r\nC -1 ; WX 600 ; N threequarters ; B 8 -56 593 666 ;\r\nC -1 ; WX 600 ; N Scedilla ; B 72 -151 529 580 ;\r\nC -1 ; WX 600 ; N lcaron ; B 95 0 533 629 ;\r\nC -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 582 562 ;\r\nC -1 ; WX 600 ; N Lacute ; B 47 0 554 805 ;\r\nC -1 ; WX 600 ; N trademark ; B -23 263 623 562 ;\r\nC -1 ; WX 600 ; N edotaccent ; B 66 -15 548 620 ;\r\nC -1 ; WX 600 ; N Igrave ; B 96 0 504 805 ;\r\nC -1 ; WX 600 ; N Imacron ; B 96 0 504 698 ;\r\nC -1 ; WX 600 ; N Lcaron ; B 47 0 554 562 ;\r\nC -1 ; WX 600 ; N onehalf ; B 0 -57 611 665 ;\r\nC -1 ; WX 600 ; N lessequal ; B 98 0 502 710 ;\r\nC -1 ; WX 600 ; N ocircumflex ; B 62 -15 538 654 ;\r\nC -1 ; WX 600 ; N ntilde ; B 26 0 575 606 ;\r\nC -1 ; WX 600 ; N Uhungarumlaut ; B 17 -18 590 805 ;\r\nC -1 ; WX 600 ; N Eacute ; B 53 0 550 805 ;\r\nC -1 ; WX 600 ; N emacron ; B 66 -15 548 565 ;\r\nC -1 ; WX 600 ; N gbreve ; B 45 -157 566 609 ;\r\nC -1 ; WX 600 ; N onequarter ; B 0 -57 600 665 ;\r\nC -1 ; WX 600 ; N Scaron ; B 72 -20 529 802 ;\r\nC -1 ; WX 600 ; N Scommaaccent ; B 72 -250 529 580 ;\r\nC -1 ; WX 600 ; N Ohungarumlaut ; B 43 -18 580 805 ;\r\nC -1 ; WX 600 ; N degree ; B 123 269 477 622 ;\r\nC -1 ; WX 600 ; N ograve ; B 62 -15 538 672 ;\r\nC -1 ; WX 600 ; N Ccaron ; B 41 -18 540 802 ;\r\nC -1 ; WX 600 ; N ugrave ; B 21 -15 562 672 ;\r\nC -1 ; WX 600 ; N radical ; B 3 -15 597 792 ;\r\nC -1 ; WX 600 ; N Dcaron ; B 43 0 574 802 ;\r\nC -1 ; WX 600 ; N rcommaaccent ; B 60 -250 559 441 ;\r\nC -1 ; WX 600 ; N Ntilde ; B 7 -13 593 729 ;\r\nC -1 ; WX 600 ; N otilde ; B 62 -15 538 606 ;\r\nC -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 588 562 ;\r\nC -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 554 562 ;\r\nC -1 ; WX 600 ; N Atilde ; B 3 0 597 729 ;\r\nC -1 ; WX 600 ; N Aogonek ; B 3 -172 608 562 ;\r\nC -1 ; WX 600 ; N Aring ; B 3 0 597 750 ;\r\nC -1 ; WX 600 ; N Otilde ; B 43 -18 557 729 ;\r\nC -1 ; WX 600 ; N zdotaccent ; B 99 0 502 620 ;\r\nC -1 ; WX 600 ; N Ecaron ; B 53 0 550 802 ;\r\nC -1 ; WX 600 ; N Iogonek ; B 96 -172 504 562 ;\r\nC -1 ; WX 600 ; N kcommaaccent ; B 43 -250 580 629 ;\r\nC -1 ; WX 600 ; N minus ; B 80 232 520 283 ;\r\nC -1 ; WX 600 ; N Icircumflex ; B 96 0 504 787 ;\r\nC -1 ; WX 600 ; N ncaron ; B 26 0 575 669 ;\r\nC -1 ; WX 600 ; N tcommaaccent ; B 87 -250 530 561 ;\r\nC -1 ; WX 600 ; N logicalnot ; B 87 108 513 369 ;\r\nC -1 ; WX 600 ; N odieresis ; B 62 -15 538 620 ;\r\nC -1 ; WX 600 ; N udieresis ; B 21 -15 562 620 ;\r\nC -1 ; WX 600 ; N notequal ; B 15 -16 540 529 ;\r\nC -1 ; WX 600 ; N gcommaaccent ; B 45 -157 566 708 ;\r\nC -1 ; WX 600 ; N eth ; B 62 -15 538 629 ;\r\nC -1 ; WX 600 ; N zcaron ; B 99 0 502 669 ;\r\nC -1 ; WX 600 ; N ncommaaccent ; B 26 -250 575 441 ;\r\nC -1 ; WX 600 ; N onesuperior ; B 172 249 428 622 ;\r\nC -1 ; WX 600 ; N imacron ; B 95 0 505 565 ;\r\nC -1 ; WX 600 ; N Euro ; B 0 0 0 0 ;\r\nEndCharMetrics\r\nEndFontMetrics\r\n";
@@ -2640,8 +2393,6 @@ class EmbeddedFont extends PDFFont {
2640
2393
  }
2641
2394
  layoutRun(text, features) {
2642
2395
  const run = this.font.layout(text, features);
2643
-
2644
- // Normalize position values
2645
2396
  for (let i = 0; i < run.positions.length; i++) {
2646
2397
  const position = run.positions[i];
2647
2398
  for (let key in position) {
@@ -2664,16 +2415,12 @@ class EmbeddedFont extends PDFFont {
2664
2415
  return run;
2665
2416
  }
2666
2417
  layout(text, features, onlyWidth) {
2667
- // Skip the cache if any user defined features are applied
2668
2418
  if (features) {
2669
2419
  return this.layoutRun(text, features);
2670
2420
  }
2671
2421
  let glyphs = onlyWidth ? null : [];
2672
2422
  let positions = onlyWidth ? null : [];
2673
2423
  let advanceWidth = 0;
2674
-
2675
- // Split the string by words to increase cache efficiency.
2676
- // For this purpose, spaces and tabs are a good enough delimeter.
2677
2424
  let last = 0;
2678
2425
  let index = 0;
2679
2426
  while (index <= text.length) {
@@ -2735,17 +2482,15 @@ class EmbeddedFont extends PDFFont {
2735
2482
  if (1 <= familyClass && familyClass <= 7) {
2736
2483
  flags |= 1 << 1;
2737
2484
  }
2738
- flags |= 1 << 2; // assume the font uses non-latin characters
2485
+ flags |= 1 << 2;
2739
2486
  if (familyClass === 10) {
2740
2487
  flags |= 1 << 3;
2741
2488
  }
2742
2489
  if (this.font.head.macStyle.italic) {
2743
2490
  flags |= 1 << 6;
2744
2491
  }
2745
-
2746
- // generate a tag (6 uppercase letters. 17 is the char code offset from '0' to 'A'. 73 will map to 'Z')
2747
2492
  const tag = [1, 2, 3, 4, 5, 6].map(i => String.fromCharCode((this.id.charCodeAt(i) || 73) + 17)).join('');
2748
- const name = tag + '+' + this.font.postscriptName.replaceAll(' ', '_');
2493
+ const name = tag + '+' + this.font.postscriptName?.replaceAll(' ', '_');
2749
2494
  const {
2750
2495
  bbox
2751
2496
  } = this.font;
@@ -2760,8 +2505,7 @@ class EmbeddedFont extends PDFFont {
2760
2505
  CapHeight: (this.font.capHeight || this.font.ascent) * this.scale,
2761
2506
  XHeight: (this.font.xHeight || 0) * this.scale,
2762
2507
  StemV: 0
2763
- }); // not sure how to calculate this
2764
-
2508
+ });
2765
2509
  if (isCFF) {
2766
2510
  descriptor.data.FontFile3 = fontFile;
2767
2511
  } else {
@@ -2803,17 +2547,11 @@ class EmbeddedFont extends PDFFont {
2803
2547
  };
2804
2548
  return this.dictionary.end();
2805
2549
  }
2806
-
2807
- // Maps the glyph ids encoded in the PDF back to unicode strings
2808
- // Because of ligature substitutions and the like, there may be one or more
2809
- // unicode characters represented by each glyph.
2810
2550
  toUnicodeCmap() {
2811
2551
  const cmap = this.document.ref();
2812
2552
  const entries = [];
2813
2553
  for (let codePoints of this.unicode) {
2814
2554
  const encoded = [];
2815
-
2816
- // encode codePoints to utf16
2817
2555
  for (let value of codePoints) {
2818
2556
  if (value > 0xffff) {
2819
2557
  value -= 0x10000;
@@ -2880,31 +2618,26 @@ class PDFFontFactory {
2880
2618
  }
2881
2619
 
2882
2620
  const isEqualFont = (font1, font2) => {
2883
- // compare font checksum
2884
2621
  if (font1.font._tables?.head?.checkSumAdjustment !== font2.font._tables?.head?.checkSumAdjustment) {
2885
2622
  return false;
2886
2623
  }
2887
-
2888
- // compare font name table
2889
2624
  if (JSON.stringify(font1.font._tables?.name?.records) !== JSON.stringify(font2.font._tables?.name?.records)) {
2890
2625
  return false;
2891
2626
  }
2892
2627
  return true;
2893
2628
  };
2894
2629
  var FontsMixin = {
2895
- initFonts(defaultFont = 'Helvetica') {
2896
- // Lookup table for embedded fonts
2630
+ initFonts(defaultFont = 'Helvetica', defaultFontFamily = null, defaultFontSize = 12) {
2897
2631
  this._fontFamilies = {};
2898
2632
  this._fontCount = 0;
2899
-
2900
- // Font state
2901
- this._fontSize = 12;
2633
+ this._fontSource = defaultFont;
2634
+ this._fontFamily = defaultFontFamily;
2635
+ this._fontSize = defaultFontSize;
2902
2636
  this._font = null;
2637
+ this._remSize = defaultFontSize;
2903
2638
  this._registeredFonts = {};
2904
-
2905
- // Set the default font
2906
2639
  if (defaultFont) {
2907
- this.font(defaultFont);
2640
+ this.font(defaultFont, defaultFontFamily);
2908
2641
  }
2909
2642
  },
2910
2643
  font(src, family, size) {
@@ -2913,8 +2646,6 @@ var FontsMixin = {
2913
2646
  size = family;
2914
2647
  family = null;
2915
2648
  }
2916
-
2917
- // check registered fonts if src is a string
2918
2649
  if (typeof src === 'string' && this._registeredFonts[src]) {
2919
2650
  cacheKey = src;
2920
2651
  ({
@@ -2927,28 +2658,21 @@ var FontsMixin = {
2927
2658
  cacheKey = null;
2928
2659
  }
2929
2660
  }
2661
+ this._fontSource = src;
2662
+ this._fontFamily = family;
2930
2663
  if (size != null) {
2931
2664
  this.fontSize(size);
2932
2665
  }
2933
-
2934
- // fast path: check if the font is already in the PDF
2935
2666
  if (font = this._fontFamilies[cacheKey]) {
2936
2667
  this._font = font;
2937
2668
  return this;
2938
2669
  }
2939
-
2940
- // load the font
2941
2670
  const id = `F${++this._fontCount}`;
2942
2671
  this._font = PDFFontFactory.open(this, src, family, id);
2943
-
2944
- // check for existing font familes with the same name already in the PDF
2945
- // useful if the font was passed as a buffer
2946
2672
  if ((font = this._fontFamilies[this._font.name]) && isEqualFont(this._font, font)) {
2947
2673
  this._font = font;
2948
2674
  return this;
2949
2675
  }
2950
-
2951
- // save the font for reuse later
2952
2676
  if (cacheKey) {
2953
2677
  this._fontFamilies[cacheKey] = this._font;
2954
2678
  }
@@ -2958,13 +2682,10 @@ var FontsMixin = {
2958
2682
  return this;
2959
2683
  },
2960
2684
  fontSize(_fontSize) {
2961
- this._fontSize = _fontSize;
2685
+ this._fontSize = this.sizeToPoint(_fontSize);
2962
2686
  return this;
2963
2687
  },
2964
2688
  currentLineHeight(includeGap) {
2965
- if (includeGap == null) {
2966
- includeGap = false;
2967
- }
2968
2689
  return this._font.lineHeight(this._fontSize, includeGap);
2969
2690
  },
2970
2691
  registerFont(name, src, family) {
@@ -2973,6 +2694,64 @@ var FontsMixin = {
2973
2694
  family
2974
2695
  };
2975
2696
  return this;
2697
+ },
2698
+ sizeToPoint(size, defaultValue = 0, page = this.page, percentageWidth = undefined) {
2699
+ if (!percentageWidth) percentageWidth = this._fontSize;
2700
+ if (typeof defaultValue !== 'number') defaultValue = this.sizeToPoint(defaultValue);
2701
+ if (size === undefined) return defaultValue;
2702
+ if (typeof size === 'number') return size;
2703
+ if (typeof size === 'boolean') return Number(size);
2704
+ const match = String(size).match(/((\d+)?(\.\d+)?)(em|in|px|cm|mm|pc|ex|ch|rem|vw|vh|vmin|vmax|%|pt)?/);
2705
+ if (!match) throw new Error(`Unsupported size '${size}'`);
2706
+ let multiplier;
2707
+ switch (match[4]) {
2708
+ case 'em':
2709
+ multiplier = this._fontSize;
2710
+ break;
2711
+ case 'in':
2712
+ multiplier = IN_TO_PT;
2713
+ break;
2714
+ case 'px':
2715
+ multiplier = PX_TO_IN * IN_TO_PT;
2716
+ break;
2717
+ case 'cm':
2718
+ multiplier = CM_TO_IN * IN_TO_PT;
2719
+ break;
2720
+ case 'mm':
2721
+ multiplier = MM_TO_CM * CM_TO_IN * IN_TO_PT;
2722
+ break;
2723
+ case 'pc':
2724
+ multiplier = PC_TO_PT;
2725
+ break;
2726
+ case 'ex':
2727
+ multiplier = this.currentLineHeight();
2728
+ break;
2729
+ case 'ch':
2730
+ multiplier = this.widthOfString('0');
2731
+ break;
2732
+ case 'rem':
2733
+ multiplier = this._remSize;
2734
+ break;
2735
+ case 'vw':
2736
+ multiplier = page.width / 100;
2737
+ break;
2738
+ case 'vh':
2739
+ multiplier = page.height / 100;
2740
+ break;
2741
+ case 'vmin':
2742
+ multiplier = Math.min(page.width, page.height) / 100;
2743
+ break;
2744
+ case 'vmax':
2745
+ multiplier = Math.max(page.width, page.height) / 100;
2746
+ break;
2747
+ case '%':
2748
+ multiplier = percentageWidth / 100;
2749
+ break;
2750
+ case 'pt':
2751
+ default:
2752
+ multiplier = 1;
2753
+ }
2754
+ return multiplier * Number(match[1]);
2976
2755
  }
2977
2756
  };
2978
2757
 
@@ -2987,7 +2766,7 @@ class LineWrapper extends events.EventEmitter {
2987
2766
  this.characterSpacing = (options.characterSpacing || 0) * this.horizontalScaling / 100;
2988
2767
  this.wordSpacing = (options.wordSpacing === 0) * this.horizontalScaling / 100;
2989
2768
  this.columns = options.columns || 1;
2990
- this.columnGap = (options.columnGap != null ? options.columnGap : 18) * this.horizontalScaling / 100; // 1/4 inch
2769
+ this.columnGap = (options.columnGap != null ? options.columnGap : 18) * this.horizontalScaling / 100;
2991
2770
  this.lineWidth = (options.width * this.horizontalScaling / 100 - this.columnGap * (this.columns - 1)) / this.columns;
2992
2771
  this.spaceLeft = this.lineWidth;
2993
2772
  this.startX = this.document.x;
@@ -2996,44 +2775,30 @@ class LineWrapper extends events.EventEmitter {
2996
2775
  this.ellipsis = options.ellipsis;
2997
2776
  this.continuedX = 0;
2998
2777
  this.features = options.features;
2999
-
3000
- // calculate the maximum Y position the text can appear at
3001
2778
  if (options.height != null) {
3002
2779
  this.height = options.height;
3003
- this.maxY = this.startY + options.height;
2780
+ this.maxY = PDFNumber(this.startY + options.height);
3004
2781
  } else {
3005
- this.maxY = this.document.page.maxY();
2782
+ this.maxY = PDFNumber(this.document.page.maxY());
3006
2783
  }
3007
-
3008
- // handle paragraph indents
3009
2784
  this.on('firstLine', options => {
3010
- // if this is the first line of the text segment, and
3011
- // we're continuing where we left off, indent that much
3012
- // otherwise use the user specified indent option
3013
2785
  const indent = this.continuedX || this.indent;
3014
2786
  this.document.x += indent;
3015
2787
  this.lineWidth -= indent;
3016
-
3017
- // if indentAllLines is set to true
3018
- // we're not resetting the indentation for this paragraph after the first line
3019
2788
  if (options.indentAllLines) {
3020
2789
  return;
3021
2790
  }
3022
-
3023
- // otherwise we start the next line without indent
3024
- return this.once('line', () => {
2791
+ this.once('line', () => {
3025
2792
  this.document.x -= indent;
3026
2793
  this.lineWidth += indent;
3027
2794
  if (options.continued && !this.continuedX) {
3028
2795
  this.continuedX = this.indent;
3029
2796
  }
3030
2797
  if (!options.continued) {
3031
- return this.continuedX = 0;
2798
+ this.continuedX = 0;
3032
2799
  }
3033
2800
  });
3034
2801
  });
3035
-
3036
- // handle left aligning last lines of paragraphs
3037
2802
  this.on('lastLine', options => {
3038
2803
  const {
3039
2804
  align
@@ -3042,7 +2807,7 @@ class LineWrapper extends events.EventEmitter {
3042
2807
  options.align = 'left';
3043
2808
  }
3044
2809
  this.lastLine = true;
3045
- return this.once('line', () => {
2810
+ this.once('line', () => {
3046
2811
  this.document.y += options.paragraphGap || 0;
3047
2812
  options.align = align;
3048
2813
  return this.lastLine = false;
@@ -3059,7 +2824,6 @@ class LineWrapper extends events.EventEmitter {
3059
2824
  return w + this.wordWidth(HYPHEN) <= this.spaceLeft;
3060
2825
  }
3061
2826
  eachWord(text, fn) {
3062
- // setup a unicode line breaker
3063
2827
  let bk;
3064
2828
  const breaker = new LineBreaker(text);
3065
2829
  let last = null;
@@ -3068,19 +2832,12 @@ class LineWrapper extends events.EventEmitter {
3068
2832
  var shouldContinue;
3069
2833
  let word = text.slice((last != null ? last.position : undefined) || 0, bk.position);
3070
2834
  let w = wordWidths[word] != null ? wordWidths[word] : wordWidths[word] = this.wordWidth(word);
3071
-
3072
- // if the word is longer than the whole line, chop it up
3073
- // TODO: break by grapheme clusters, not JS string characters
3074
2835
  if (w > this.lineWidth + this.continuedX) {
3075
- // make some fake break objects
3076
2836
  let lbk = last;
3077
2837
  const fbk = {};
3078
2838
  while (word.length) {
3079
- // fit as much of the word as possible into the space we have
3080
2839
  var l, mightGrow;
3081
2840
  if (w > this.spaceLeft) {
3082
- // start our check at the end of our available space - this method is faster than a loop of each character and it resolves
3083
- // an issue with long loops when processing massive words, such as a huge number of spaces
3084
2841
  l = Math.ceil(this.spaceLeft / (w / word.length));
3085
2842
  w = this.wordWidth(word.slice(0, l));
3086
2843
  mightGrow = w <= this.spaceLeft && l < word.length;
@@ -3088,7 +2845,6 @@ class LineWrapper extends events.EventEmitter {
3088
2845
  l = word.length;
3089
2846
  }
3090
2847
  let mustShrink = w > this.spaceLeft && l > 0;
3091
- // shrink or grow word as necessary after our near-guess above
3092
2848
  while (mustShrink || mightGrow) {
3093
2849
  if (mustShrink) {
3094
2850
  w = this.wordWidth(word.slice(0, --l));
@@ -3099,20 +2855,14 @@ class LineWrapper extends events.EventEmitter {
3099
2855
  mightGrow = w <= this.spaceLeft && l < word.length;
3100
2856
  }
3101
2857
  }
3102
-
3103
- // check for the edge case where a single character cannot fit into a line.
3104
2858
  if (l === 0 && this.spaceLeft === this.lineWidth) {
3105
2859
  l = 1;
3106
2860
  }
3107
-
3108
- // send a required break unless this is the last piece and a linebreak is not specified
3109
2861
  fbk.required = bk.required || l < word.length;
3110
2862
  shouldContinue = fn(word.slice(0, l), w, fbk, lbk);
3111
2863
  lbk = {
3112
2864
  required: false
3113
2865
  };
3114
-
3115
- // get the remaining piece of the word
3116
2866
  word = word.slice(l);
3117
2867
  w = this.wordWidth(word);
3118
2868
  if (shouldContinue === false) {
@@ -3120,7 +2870,6 @@ class LineWrapper extends events.EventEmitter {
3120
2870
  }
3121
2871
  }
3122
2872
  } else {
3123
- // otherwise just emit the break as it was given to us
3124
2873
  shouldContinue = fn(word, w, bk, last);
3125
2874
  }
3126
2875
  if (shouldContinue === false) {
@@ -3130,7 +2879,6 @@ class LineWrapper extends events.EventEmitter {
3130
2879
  }
3131
2880
  }
3132
2881
  wrap(text, options) {
3133
- // override options from previous continued fragments
3134
2882
  this.horizontalScaling = options.horizontalScaling || 100;
3135
2883
  if (options.indent != null) {
3136
2884
  this.indent = options.indent * this.horizontalScaling / 100;
@@ -3144,10 +2892,6 @@ class LineWrapper extends events.EventEmitter {
3144
2892
  if (options.ellipsis != null) {
3145
2893
  this.ellipsis = options.ellipsis;
3146
2894
  }
3147
-
3148
- // make sure we're actually on the page
3149
- // and that the first line of is never by
3150
- // itself at the bottom of a page (orphans)
3151
2895
  const nextY = this.document.y + this.document.currentLineHeight(true);
3152
2896
  if (this.document.y > this.maxY || nextY > this.maxY) {
3153
2897
  this.nextSection();
@@ -3158,7 +2902,7 @@ class LineWrapper extends events.EventEmitter {
3158
2902
  let lc = 0;
3159
2903
  let {
3160
2904
  y
3161
- } = this.document; // used to reset Y pos if options.continued (below)
2905
+ } = this.document;
3162
2906
  const emitLine = () => {
3163
2907
  options.textWidth = textWidth + this.wordSpacing * (wc - 1);
3164
2908
  options.wordCount = wc;
@@ -3181,23 +2925,17 @@ class LineWrapper extends events.EventEmitter {
3181
2925
  wc++;
3182
2926
  }
3183
2927
  if (bk.required || !this.canFit(word, w)) {
3184
- // if the user specified a max height and an ellipsis, and is about to pass the
3185
- // max height and max columns after the next line, append the ellipsis
3186
2928
  const lh = this.document.currentLineHeight(true);
3187
- if (this.height != null && this.ellipsis && this.document.y + lh * 2 > this.maxY && this.column >= this.columns) {
2929
+ if (this.height != null && this.ellipsis && PDFNumber(this.document.y + lh * 2) > this.maxY && this.column >= this.columns) {
3188
2930
  if (this.ellipsis === true) {
3189
2931
  this.ellipsis = '…';
3190
- } // map default ellipsis character
2932
+ }
3191
2933
  buffer = buffer.replace(/\s+$/, '');
3192
2934
  textWidth = this.wordWidth(buffer + this.ellipsis);
3193
-
3194
- // remove characters from the buffer until the ellipsis fits
3195
- // to avoid infinite loop need to stop while-loop if buffer is empty string
3196
2935
  while (buffer && textWidth > this.lineWidth) {
3197
2936
  buffer = buffer.slice(0, -1).replace(/\s+$/, '');
3198
2937
  textWidth = this.wordWidth(buffer + this.ellipsis);
3199
2938
  }
3200
- // need to add ellipsis only if there is enough space for it
3201
2939
  if (textWidth <= this.lineWidth) {
3202
2940
  buffer = buffer + this.ellipsis;
3203
2941
  }
@@ -3212,35 +2950,25 @@ class LineWrapper extends events.EventEmitter {
3212
2950
  }
3213
2951
  this.emit('lastLine', options, this);
3214
2952
  }
3215
-
3216
- // Previous entry is a soft hyphen - add visible hyphen.
3217
2953
  if (buffer[buffer.length - 1] == SOFT_HYPHEN) {
3218
2954
  buffer = buffer.slice(0, -1) + HYPHEN;
3219
2955
  this.spaceLeft -= this.wordWidth(HYPHEN);
3220
2956
  }
3221
2957
  emitLine();
3222
-
3223
- // if we've reached the edge of the page,
3224
- // continue on a new page or column
3225
- if (this.document.y + lh > this.maxY) {
2958
+ if (PDFNumber(this.document.y + lh) > this.maxY) {
3226
2959
  const shouldContinue = this.nextSection();
3227
-
3228
- // stop if we reached the maximum height
3229
2960
  if (!shouldContinue) {
3230
2961
  wc = 0;
3231
2962
  buffer = '';
3232
2963
  return false;
3233
2964
  }
3234
2965
  }
3235
-
3236
- // reset the space left and buffer
3237
2966
  if (bk.required) {
3238
2967
  this.spaceLeft = this.lineWidth;
3239
2968
  buffer = '';
3240
2969
  textWidth = 0;
3241
2970
  return wc = 0;
3242
2971
  } else {
3243
- // reset the space left and buffer
3244
2972
  this.spaceLeft = this.lineWidth - w;
3245
2973
  buffer = word;
3246
2974
  textWidth = w;
@@ -3255,25 +2983,19 @@ class LineWrapper extends events.EventEmitter {
3255
2983
  emitLine();
3256
2984
  }
3257
2985
  this.emit('sectionEnd', options, this);
3258
-
3259
- // if the wrap is set to be continued, save the X position
3260
- // to start the first line of the next segment at, and reset
3261
- // the y position
3262
2986
  if (options.continued === true) {
3263
2987
  if (lc > 1) {
3264
2988
  this.continuedX = 0;
3265
2989
  }
3266
2990
  this.continuedX += options.textWidth || 0;
3267
- return this.document.y = y;
2991
+ this.document.y = y;
3268
2992
  } else {
3269
- return this.document.x = this.startX;
2993
+ this.document.x = this.startX;
3270
2994
  }
3271
2995
  }
3272
2996
  nextSection(options) {
3273
2997
  this.emit('sectionEnd', options, this);
3274
2998
  if (++this.column > this.columns) {
3275
- // if a max height was specified by the user, we're done.
3276
- // otherwise, the default is to make a new page at the bottom.
3277
2999
  if (this.height != null) {
3278
3000
  return false;
3279
3001
  }
@@ -3302,10 +3024,9 @@ const {
3302
3024
  var TextMixin = {
3303
3025
  initText() {
3304
3026
  this._line = this._line.bind(this);
3305
- // Current coordinates
3306
3027
  this.x = 0;
3307
3028
  this.y = 0;
3308
- return this._lineGap = 0;
3029
+ this._lineGap = 0;
3309
3030
  },
3310
3031
  lineGap(_lineGap) {
3311
3032
  this._lineGap = _lineGap;
@@ -3327,11 +3048,7 @@ var TextMixin = {
3327
3048
  },
3328
3049
  _text(text, x, y, options, lineCallback) {
3329
3050
  options = this._initOptions(x, y, options);
3330
-
3331
- // Convert text to a string
3332
3051
  text = text == null ? '' : `${text}`;
3333
-
3334
- // if the wordSpacing option is specified, remove multiple consecutive spaces
3335
3052
  if (options.wordSpacing) {
3336
3053
  text = text.replace(/\s{2,}/g, ' ');
3337
3054
  }
@@ -3340,8 +3057,12 @@ var TextMixin = {
3340
3057
  options.structParent.add(this.struct(options.structType || 'P', [this.markStructureContent(options.structType || 'P')]));
3341
3058
  }
3342
3059
  };
3343
-
3344
- // word wrapping
3060
+ if (options.rotation !== 0) {
3061
+ this.save();
3062
+ this.rotate(-options.rotation, {
3063
+ origin: [this.x, this.y]
3064
+ });
3065
+ }
3345
3066
  if (options.width) {
3346
3067
  let wrapper = this._wrapper;
3347
3068
  if (!wrapper) {
@@ -3352,14 +3073,13 @@ var TextMixin = {
3352
3073
  this._wrapper = options.continued ? wrapper : null;
3353
3074
  this._textOptions = options.continued ? options : null;
3354
3075
  wrapper.wrap(text, options);
3355
-
3356
- // render paragraphs as single lines
3357
3076
  } else {
3358
3077
  for (let line of text.split('\n')) {
3359
3078
  addStructure();
3360
3079
  lineCallback(line, options);
3361
3080
  }
3362
3081
  }
3082
+ if (options.rotation !== 0) this.restore();
3363
3083
  return this;
3364
3084
  },
3365
3085
  text(text, x, y, options) {
@@ -3369,17 +3089,108 @@ var TextMixin = {
3369
3089
  const horizontalScaling = options.horizontalScaling || 100;
3370
3090
  return (this._font.widthOfString(string, this._fontSize, options.features) + (options.characterSpacing || 0) * (string.length - 1)) * horizontalScaling / 100;
3371
3091
  },
3092
+ boundsOfString(string, x, y, options) {
3093
+ options = this._initOptions(x, y, options);
3094
+ ({
3095
+ x,
3096
+ y
3097
+ } = this);
3098
+ const lineGap = options.lineGap ?? this._lineGap ?? 0;
3099
+ const lineHeight = this.currentLineHeight(true) + lineGap;
3100
+ let contentWidth = 0;
3101
+ string = String(string ?? '');
3102
+ if (options.wordSpacing) {
3103
+ string = string.replace(/\s{2,}/g, ' ');
3104
+ }
3105
+ if (options.width) {
3106
+ let wrapper = new LineWrapper(this, options);
3107
+ wrapper.on('line', (text, options) => {
3108
+ this.y += lineHeight;
3109
+ text = text.replace(/\n/g, '');
3110
+ if (text.length) {
3111
+ let wordSpacing = options.wordSpacing ?? 0;
3112
+ const characterSpacing = options.characterSpacing ?? 0;
3113
+ if (options.width && options.align === 'justify') {
3114
+ const words = text.trim().split(/\s+/);
3115
+ const textWidth = this.widthOfString(text.replace(/\s+/g, ''), options);
3116
+ const spaceWidth = this.widthOfString(' ') + characterSpacing;
3117
+ wordSpacing = Math.max(0, (options.lineWidth - textWidth) / Math.max(1, words.length - 1) - spaceWidth);
3118
+ }
3119
+ contentWidth = Math.max(contentWidth, options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1));
3120
+ }
3121
+ });
3122
+ wrapper.wrap(string, options);
3123
+ } else {
3124
+ for (let line of string.split('\n')) {
3125
+ const lineWidth = this.widthOfString(line, options);
3126
+ this.y += lineHeight;
3127
+ contentWidth = Math.max(contentWidth, lineWidth);
3128
+ }
3129
+ }
3130
+ let contentHeight = this.y - y;
3131
+ if (options.height) contentHeight = Math.min(contentHeight, options.height);
3132
+ this.x = x;
3133
+ this.y = y;
3134
+ if (options.rotation === 0) {
3135
+ return {
3136
+ x,
3137
+ y,
3138
+ width: contentWidth,
3139
+ height: contentHeight
3140
+ };
3141
+ } else if (options.rotation === 90) {
3142
+ return {
3143
+ x: x,
3144
+ y: y - contentWidth,
3145
+ width: contentHeight,
3146
+ height: contentWidth
3147
+ };
3148
+ } else if (options.rotation === 180) {
3149
+ return {
3150
+ x: x - contentWidth,
3151
+ y: y - contentHeight,
3152
+ width: contentWidth,
3153
+ height: contentHeight
3154
+ };
3155
+ } else if (options.rotation === 270) {
3156
+ return {
3157
+ x: x - contentHeight,
3158
+ y: y,
3159
+ width: contentHeight,
3160
+ height: contentWidth
3161
+ };
3162
+ }
3163
+ const cos = cosine(options.rotation);
3164
+ const sin = sine(options.rotation);
3165
+ const x1 = x;
3166
+ const y1 = y;
3167
+ const x2 = x + contentWidth * cos;
3168
+ const y2 = y - contentWidth * sin;
3169
+ const x3 = x + contentWidth * cos + contentHeight * sin;
3170
+ const y3 = y - contentWidth * sin + contentHeight * cos;
3171
+ const x4 = x + contentHeight * sin;
3172
+ const y4 = y + contentHeight * cos;
3173
+ const xMin = Math.min(x1, x2, x3, x4);
3174
+ const xMax = Math.max(x1, x2, x3, x4);
3175
+ const yMin = Math.min(y1, y2, y3, y4);
3176
+ const yMax = Math.max(y1, y2, y3, y4);
3177
+ return {
3178
+ x: xMin,
3179
+ y: yMin,
3180
+ width: xMax - xMin,
3181
+ height: yMax - yMin
3182
+ };
3183
+ },
3372
3184
  heightOfString(text, options) {
3373
3185
  const {
3374
3186
  x,
3375
3187
  y
3376
3188
  } = this;
3377
3189
  options = this._initOptions(options);
3378
- options.height = Infinity; // don't break pages
3379
-
3190
+ options.height = Infinity;
3380
3191
  const lineGap = options.lineGap || this._lineGap || 0;
3381
3192
  this._text(text, this.x, this.y, options, () => {
3382
- return this.y += this.currentLineHeight(true) + lineGap;
3193
+ this.y += this.currentLineHeight(true) + lineGap;
3383
3194
  });
3384
3195
  const height = this.y - y;
3385
3196
  this.x = x;
@@ -3477,12 +3288,12 @@ var TextMixin = {
3477
3288
  wrapper.on('sectionStart', () => {
3478
3289
  const pos = indent + itemIndent * (level - 1);
3479
3290
  this.x += pos;
3480
- return wrapper.lineWidth -= pos;
3291
+ wrapper.lineWidth -= pos;
3481
3292
  });
3482
3293
  wrapper.on('sectionEnd', () => {
3483
3294
  const pos = indent + itemIndent * (level - 1);
3484
3295
  this.x -= pos;
3485
- return wrapper.lineWidth += pos;
3296
+ wrapper.lineWidth += pos;
3486
3297
  });
3487
3298
  wrapper.wrap(listItem, options);
3488
3299
  };
@@ -3496,11 +3307,7 @@ var TextMixin = {
3496
3307
  options = x;
3497
3308
  x = null;
3498
3309
  }
3499
-
3500
- // clone options object
3501
3310
  const result = Object.assign({}, options);
3502
-
3503
- // extend options with previous values for continued text
3504
3311
  if (this._textOptions) {
3505
3312
  for (let key in this._textOptions) {
3506
3313
  const val = this._textOptions[key];
@@ -3511,16 +3318,12 @@ var TextMixin = {
3511
3318
  }
3512
3319
  }
3513
3320
  }
3514
-
3515
- // Update the current position
3516
3321
  if (x != null) {
3517
3322
  this.x = x;
3518
3323
  }
3519
3324
  if (y != null) {
3520
3325
  this.y = y;
3521
3326
  }
3522
-
3523
- // wrap to margins if no x or y position passed
3524
3327
  if (result.lineBreak !== false) {
3525
3328
  if (result.width == null) {
3526
3329
  result.width = this.page.width - this.x - this.page.margins.right;
@@ -3532,17 +3335,18 @@ var TextMixin = {
3532
3335
  }
3533
3336
  if (result.columnGap == null) {
3534
3337
  result.columnGap = 18;
3535
- } // 1/4 inch
3536
-
3338
+ }
3339
+ result.rotation = Number(options.rotation ?? 0) % 360;
3340
+ if (result.rotation < 0) result.rotation += 360;
3537
3341
  return result;
3538
3342
  },
3539
3343
  _line(text, options = {}, wrapper) {
3540
3344
  this._fragment(text, this.x, this.y, options);
3541
3345
  const lineGap = options.lineGap || this._lineGap || 0;
3542
3346
  if (!wrapper) {
3543
- return this.x += this.widthOfString(text, options);
3347
+ this.x += this.widthOfString(text, options);
3544
3348
  } else {
3545
- return this.y += this.currentLineHeight(true) + lineGap;
3349
+ this.y += this.currentLineHeight(true) + lineGap;
3546
3350
  }
3547
3351
  },
3548
3352
  _fragment(text, x, y, options) {
@@ -3551,14 +3355,10 @@ var TextMixin = {
3551
3355
  if (text.length === 0) {
3552
3356
  return;
3553
3357
  }
3554
-
3555
- // handle options
3556
3358
  const align = options.align || 'left';
3557
3359
  let wordSpacing = options.wordSpacing || 0;
3558
3360
  const characterSpacing = options.characterSpacing || 0;
3559
3361
  const horizontalScaling = options.horizontalScaling || 100;
3560
-
3561
- // text alignments
3562
3362
  if (options.width) {
3563
3363
  switch (align) {
3564
3364
  case 'right':
@@ -3569,7 +3369,6 @@ var TextMixin = {
3569
3369
  x += options.lineWidth / 2 - options.textWidth / 2;
3570
3370
  break;
3571
3371
  case 'justify':
3572
- // calculate the word spacing value
3573
3372
  words = text.trim().split(/\s+/);
3574
3373
  textWidth = this.widthOfString(text.replace(/\s+/g, ''), options);
3575
3374
  var spaceWidth = this.widthOfString(' ') + characterSpacing;
@@ -3577,8 +3376,6 @@ var TextMixin = {
3577
3376
  break;
3578
3377
  }
3579
3378
  }
3580
-
3581
- // text baseline alignments based on http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
3582
3379
  if (typeof options.baseline === 'number') {
3583
3380
  dy = -options.baseline;
3584
3381
  } else {
@@ -3611,11 +3408,7 @@ var TextMixin = {
3611
3408
  }
3612
3409
  dy = dy / 1000 * this._fontSize;
3613
3410
  }
3614
-
3615
- // calculate the actual rendered width of the string after word and character spacing
3616
3411
  const renderedWidth = options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1);
3617
-
3618
- // create link annotations if the link option is given
3619
3412
  if (options.link != null) {
3620
3413
  this.link(x, y, renderedWidth, this.currentLineHeight(), options.link);
3621
3414
  }
@@ -3625,8 +3418,6 @@ var TextMixin = {
3625
3418
  if (options.destination != null) {
3626
3419
  this.addNamedDestination(options.destination, 'XYZ', x, y, null);
3627
3420
  }
3628
-
3629
- // create underline
3630
3421
  if (options.underline) {
3631
3422
  this.save();
3632
3423
  if (!options.stroke) {
@@ -3640,8 +3431,6 @@ var TextMixin = {
3640
3431
  this.stroke();
3641
3432
  this.restore();
3642
3433
  }
3643
-
3644
- // create strikethrough line
3645
3434
  if (options.strike) {
3646
3435
  this.save();
3647
3436
  if (!options.stroke) {
@@ -3656,8 +3445,6 @@ var TextMixin = {
3656
3445
  this.restore();
3657
3446
  }
3658
3447
  this.save();
3659
-
3660
- // oblique (angle in degrees or boolean)
3661
3448
  if (options.oblique) {
3662
3449
  let skew;
3663
3450
  if (typeof options.oblique === 'number') {
@@ -3669,45 +3456,24 @@ var TextMixin = {
3669
3456
  this.transform(1, 0, skew, 1, -skew * dy, 0);
3670
3457
  this.transform(1, 0, 0, 1, -x, -y);
3671
3458
  }
3672
-
3673
- // flip coordinate system
3674
3459
  this.transform(1, 0, 0, -1, 0, this.page.height);
3675
3460
  y = this.page.height - y - dy;
3676
-
3677
- // add current font to page if necessary
3678
3461
  if (this.page.fonts[this._font.id] == null) {
3679
3462
  this.page.fonts[this._font.id] = this._font.ref();
3680
3463
  }
3681
-
3682
- // begin the text object
3683
3464
  this.addContent('BT');
3684
-
3685
- // text position
3686
3465
  this.addContent(`1 0 0 1 ${number(x)} ${number(y)} Tm`);
3687
-
3688
- // font and font size
3689
3466
  this.addContent(`/${this._font.id} ${number(this._fontSize)} Tf`);
3690
-
3691
- // rendering mode
3692
3467
  const mode = options.fill && options.stroke ? 2 : options.stroke ? 1 : 0;
3693
3468
  if (mode) {
3694
3469
  this.addContent(`${mode} Tr`);
3695
3470
  }
3696
-
3697
- // Character spacing
3698
3471
  if (characterSpacing) {
3699
3472
  this.addContent(`${number(characterSpacing)} Tc`);
3700
3473
  }
3701
-
3702
- // Horizontal scaling
3703
3474
  if (horizontalScaling !== 100) {
3704
3475
  this.addContent(`${horizontalScaling} Tz`);
3705
3476
  }
3706
-
3707
- // Add the actual text
3708
- // If we have a word spacing value, we need to encode each word separately
3709
- // since the normal Tw operator only works on character code 32, which isn't
3710
- // used for embedded fonts.
3711
3477
  if (wordSpacing) {
3712
3478
  words = text.trim().split(/\s+/);
3713
3479
  wordSpacing += this.widthOfString(' ') + characterSpacing;
@@ -3718,9 +3484,6 @@ var TextMixin = {
3718
3484
  const [encodedWord, positionsWord] = this._font.encode(word, options.features);
3719
3485
  encoded = encoded.concat(encodedWord);
3720
3486
  positions = positions.concat(positionsWord);
3721
-
3722
- // add the word spacing to the end of the word
3723
- // clone object because of cache
3724
3487
  const space = {};
3725
3488
  const object = positions[positions.length - 1];
3726
3489
  for (let key in object) {
@@ -3737,60 +3500,42 @@ var TextMixin = {
3737
3500
  const commands = [];
3738
3501
  let last = 0;
3739
3502
  let hadOffset = false;
3740
-
3741
- // Adds a segment of text to the TJ command buffer
3742
3503
  const addSegment = cur => {
3743
3504
  if (last < cur) {
3744
3505
  const hex = encoded.slice(last, cur).join('');
3745
3506
  const advance = positions[cur - 1].xAdvance - positions[cur - 1].advanceWidth;
3746
3507
  commands.push(`<${hex}> ${number(-advance)}`);
3747
3508
  }
3748
- return last = cur;
3509
+ last = cur;
3749
3510
  };
3750
-
3751
- // Flushes the current TJ commands to the output stream
3752
3511
  const flush = i => {
3753
3512
  addSegment(i);
3754
3513
  if (commands.length > 0) {
3755
3514
  this.addContent(`[${commands.join(' ')}] TJ`);
3756
- return commands.length = 0;
3515
+ commands.length = 0;
3757
3516
  }
3758
3517
  };
3759
3518
  for (i = 0; i < positions.length; i++) {
3760
- // If we have an x or y offset, we have to break out of the current TJ command
3761
- // so we can move the text position.
3762
3519
  const pos = positions[i];
3763
3520
  if (pos.xOffset || pos.yOffset) {
3764
- // Flush the current buffer
3765
3521
  flush(i);
3766
-
3767
- // Move the text position and flush just the current character
3768
3522
  this.addContent(`1 0 0 1 ${number(x + pos.xOffset * scale)} ${number(y + pos.yOffset * scale)} Tm`);
3769
3523
  flush(i + 1);
3770
3524
  hadOffset = true;
3771
3525
  } else {
3772
- // If the last character had an offset, reset the text position
3773
3526
  if (hadOffset) {
3774
3527
  this.addContent(`1 0 0 1 ${number(x)} ${number(y)} Tm`);
3775
3528
  hadOffset = false;
3776
3529
  }
3777
-
3778
- // Group segments that don't have any advance adjustments
3779
3530
  if (pos.xAdvance - pos.advanceWidth !== 0) {
3780
3531
  addSegment(i + 1);
3781
3532
  }
3782
3533
  }
3783
3534
  x += pos.xAdvance * scale;
3784
3535
  }
3785
-
3786
- // Flush any remaining commands
3787
3536
  flush(i);
3788
-
3789
- // end the text object
3790
3537
  this.addContent('ET');
3791
-
3792
- // restore flipped coordinate system
3793
- return this.restore();
3538
+ this.restore();
3794
3539
  }
3795
3540
  };
3796
3541
 
@@ -3808,8 +3553,6 @@ class JPEG {
3808
3553
  if (this.data.readUInt16BE(0) !== 0xffd8) {
3809
3554
  throw 'SOI not found in JPEG';
3810
3555
  }
3811
-
3812
- // Parse the EXIF orientation
3813
3556
  this.orientation = exif.fromBuffer(this.data).Orientation || 1;
3814
3557
  let pos = 2;
3815
3558
  while (pos < this.data.length) {
@@ -3846,16 +3589,10 @@ class JPEG {
3846
3589
  ColorSpace: this.colorSpace,
3847
3590
  Filter: 'DCTDecode'
3848
3591
  });
3849
-
3850
- // add extra decode params for CMYK images. By swapping the
3851
- // min and max values from the default, we invert the colors. See
3852
- // section 4.8.4 of the spec.
3853
3592
  if (this.colorSpace === 'DeviceCMYK') {
3854
3593
  this.obj.data['Decode'] = [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0];
3855
3594
  }
3856
3595
  this.obj.end(this.data);
3857
-
3858
- // free memory
3859
3596
  return this.data = null;
3860
3597
  }
3861
3598
  }
@@ -3898,24 +3635,14 @@ class PNGImage {
3898
3635
  if (this.image.palette.length === 0) {
3899
3636
  this.obj.data['ColorSpace'] = this.image.colorSpace;
3900
3637
  } else {
3901
- // embed the color palette in the PDF as an object stream
3902
3638
  const palette = this.document.ref();
3903
3639
  palette.end(Buffer.from(this.image.palette));
3904
-
3905
- // build the color space array for the image
3906
3640
  this.obj.data['ColorSpace'] = ['Indexed', 'DeviceRGB', this.image.palette.length / 3 - 1, palette];
3907
3641
  }
3908
-
3909
- // For PNG color types 0, 2 and 3, the transparency data is stored in
3910
- // a dedicated PNG chunk.
3911
3642
  if (this.image.transparency.grayscale != null) {
3912
- // Use Color Key Masking (spec section 4.8.5)
3913
- // An array with N elements, where N is two times the number of color components.
3914
3643
  const val = this.image.transparency.grayscale;
3915
3644
  this.obj.data['Mask'] = [val, val];
3916
3645
  } else if (this.image.transparency.rgb) {
3917
- // Use Color Key Masking (spec section 4.8.5)
3918
- // An array with N elements, where N is two times the number of color components.
3919
3646
  const {
3920
3647
  rgb
3921
3648
  } = this.image.transparency;
@@ -3925,14 +3652,9 @@ class PNGImage {
3925
3652
  }
3926
3653
  this.obj.data['Mask'] = mask;
3927
3654
  } else if (this.image.transparency.indexed) {
3928
- // Create a transparency SMask for the image based on the data
3929
- // in the PLTE and tRNS sections. See below for details on SMasks.
3930
3655
  dataDecoded = true;
3931
3656
  return this.loadIndexedAlphaChannel();
3932
3657
  } else if (hasAlphaChannel) {
3933
- // For PNG color types 4 and 6, the transparency data is stored as a alpha
3934
- // channel mixed in with the main image data. Separate this data out into an
3935
- // SMask object and store it separately in the PDF.
3936
3658
  dataDecoded = true;
3937
3659
  return this.splitAlphaChannel();
3938
3660
  }
@@ -3956,11 +3678,7 @@ class PNGImage {
3956
3678
  sMask.end(this.alphaChannel);
3957
3679
  this.obj.data['SMask'] = sMask;
3958
3680
  }
3959
-
3960
- // add the actual image data
3961
3681
  this.obj.end(this.imgData);
3962
-
3963
- // free memory
3964
3682
  this.image = null;
3965
3683
  return this.imgData = null;
3966
3684
  }
@@ -3973,7 +3691,6 @@ class PNGImage {
3973
3691
  const alphaChannel = Buffer.alloc(pixelCount);
3974
3692
  let i = p = a = 0;
3975
3693
  const len = pixels.length;
3976
- // For 16bit images copy only most significant byte (MSB) - PNG data is always stored in network byte order (MSB first)
3977
3694
  const skipByteCount = this.image.bits === 16 ? 1 : 0;
3978
3695
  while (i < len) {
3979
3696
  for (let colorIndex = 0; colorIndex < colorCount; colorIndex++) {
@@ -4008,10 +3725,6 @@ class PNGImage {
4008
3725
  }
4009
3726
  }
4010
3727
 
4011
- /*
4012
- PDFImage - embeds images in PDF documents
4013
- By Devon Govett
4014
- */
4015
3728
  class PDFImage {
4016
3729
  static open(src, label) {
4017
3730
  let data;
@@ -4043,17 +3756,16 @@ class PDFImage {
4043
3756
  var ImagesMixin = {
4044
3757
  initImages() {
4045
3758
  this._imageRegistry = {};
4046
- return this._imageCount = 0;
3759
+ this._imageCount = 0;
4047
3760
  },
4048
3761
  image(src, x, y, options = {}) {
4049
- let bh, bp, bw, image, ip, left, left1, rotateAngle, originX, originY;
3762
+ let bh, bp, bw, image, ip, left, left1, originX, originY;
4050
3763
  if (typeof x === 'object') {
4051
3764
  options = x;
4052
3765
  x = null;
4053
3766
  }
4054
-
4055
- // Ignore orientation based on document options or image options
4056
3767
  const ignoreOrientation = options.ignoreOrientation || options.ignoreOrientation !== false && this.options.ignoreOrientation;
3768
+ const inDocumentFlow = typeof y !== 'number';
4057
3769
  x = (left = x != null ? x : options.x) != null ? left : this.x;
4058
3770
  y = (left1 = y != null ? y : options.y) != null ? left1 : this.y;
4059
3771
  if (typeof src === 'string') {
@@ -4076,8 +3788,6 @@ var ImagesMixin = {
4076
3788
  width,
4077
3789
  height
4078
3790
  } = image;
4079
-
4080
- // If EXIF orientation calls for it, swap width and height
4081
3791
  if (!ignoreOrientation && image.orientation > 4) {
4082
3792
  [width, height] = [height, width];
4083
3793
  }
@@ -4129,80 +3839,70 @@ var ImagesMixin = {
4129
3839
  y = y + bh - h;
4130
3840
  }
4131
3841
  }
3842
+ let rotateAngle = 0;
3843
+ let xTransform = x;
3844
+ let yTransform = y;
3845
+ let hTransform = h;
3846
+ let wTransform = w;
4132
3847
  if (!ignoreOrientation) {
4133
3848
  switch (image.orientation) {
4134
- // No orientation (need to flip image, though, because of the default transform matrix on the document)
4135
3849
  default:
4136
3850
  case 1:
4137
- h = -h;
4138
- y -= h;
4139
- rotateAngle = 0;
3851
+ hTransform = -h;
3852
+ yTransform += h;
4140
3853
  break;
4141
- // Flip Horizontal
4142
3854
  case 2:
4143
- w = -w;
4144
- h = -h;
4145
- x -= w;
4146
- y -= h;
4147
- rotateAngle = 0;
3855
+ wTransform = -w;
3856
+ hTransform = -h;
3857
+ xTransform += w;
3858
+ yTransform += h;
4148
3859
  break;
4149
- // Rotate 180 degrees
4150
3860
  case 3:
4151
3861
  originX = x;
4152
3862
  originY = y;
4153
- h = -h;
4154
- x -= w;
3863
+ hTransform = -h;
3864
+ xTransform -= w;
4155
3865
  rotateAngle = 180;
4156
3866
  break;
4157
- // Flip vertical
4158
3867
  case 4:
4159
- // Do nothing, image will be flipped
4160
-
4161
3868
  break;
4162
- // Flip horizontally and rotate 270 degrees CW
4163
3869
  case 5:
4164
3870
  originX = x;
4165
3871
  originY = y;
4166
- [w, h] = [h, w];
4167
- y -= h;
3872
+ wTransform = h;
3873
+ hTransform = w;
3874
+ yTransform -= hTransform;
4168
3875
  rotateAngle = 90;
4169
3876
  break;
4170
- // Rotate 90 degrees CW
4171
3877
  case 6:
4172
3878
  originX = x;
4173
3879
  originY = y;
4174
- [w, h] = [h, w];
4175
- h = -h;
3880
+ wTransform = h;
3881
+ hTransform = -w;
4176
3882
  rotateAngle = 90;
4177
3883
  break;
4178
- // Flip horizontally and rotate 90 degrees CW
4179
3884
  case 7:
4180
3885
  originX = x;
4181
3886
  originY = y;
4182
- [w, h] = [h, w];
4183
- h = -h;
4184
- w = -w;
4185
- x -= w;
3887
+ hTransform = -w;
3888
+ wTransform = -h;
3889
+ xTransform += h;
4186
3890
  rotateAngle = 90;
4187
3891
  break;
4188
- // Rotate 270 degrees CW
4189
3892
  case 8:
4190
3893
  originX = x;
4191
3894
  originY = y;
4192
- [w, h] = [h, w];
4193
- h = -h;
4194
- x -= w;
4195
- y -= h;
3895
+ wTransform = h;
3896
+ hTransform = -w;
3897
+ xTransform -= h;
3898
+ yTransform += w;
4196
3899
  rotateAngle = -90;
4197
3900
  break;
4198
3901
  }
4199
3902
  } else {
4200
- h = -h;
4201
- y -= h;
4202
- rotateAngle = 0;
3903
+ hTransform = -h;
3904
+ yTransform += h;
4203
3905
  }
4204
-
4205
- // create link annotations if the link option is given
4206
3906
  if (options.link != null) {
4207
3907
  this.link(x, y, w, h, options.link);
4208
3908
  }
@@ -4212,9 +3912,7 @@ var ImagesMixin = {
4212
3912
  if (options.destination != null) {
4213
3913
  this.addNamedDestination(options.destination, 'XYZ', x, y, null);
4214
3914
  }
4215
-
4216
- // Set the current y position to below the image if it is in the document flow
4217
- if (this.y === y) {
3915
+ if (inDocumentFlow) {
4218
3916
  this.y += h;
4219
3917
  }
4220
3918
  this.save();
@@ -4223,7 +3921,7 @@ var ImagesMixin = {
4223
3921
  origin: [originX, originY]
4224
3922
  });
4225
3923
  }
4226
- this.transform(w, 0, 0, h, x, y);
3924
+ this.transform(wTransform, 0, 0, hTransform, xTransform, yTransform);
4227
3925
  this.addContent(`/${image.label} Do`);
4228
3926
  this.restore();
4229
3927
  return this;
@@ -4249,19 +3947,17 @@ var AnnotationsMixin = {
4249
3947
  options.Rect = this._convertRect(x, y, w, h);
4250
3948
  options.Border = [0, 0, 0];
4251
3949
  if (options.Subtype === 'Link' && typeof options.F === 'undefined') {
4252
- options.F = 1 << 2; // Print Annotation Flag
3950
+ options.F = 1 << 2;
4253
3951
  }
4254
3952
  if (options.Subtype !== 'Link') {
4255
3953
  if (options.C == null) {
4256
3954
  options.C = this._normalizeColor(options.color || [0, 0, 0]);
4257
3955
  }
4258
- } // convert colors
3956
+ }
4259
3957
  delete options.color;
4260
3958
  if (typeof options.Dest === 'string') {
4261
3959
  options.Dest = new String(options.Dest);
4262
3960
  }
4263
-
4264
- // Capitalize keys
4265
3961
  for (let key in options) {
4266
3962
  const val = options[key];
4267
3963
  options[key[0].toUpperCase() + key.slice(1)] = val;
@@ -4294,7 +3990,6 @@ var AnnotationsMixin = {
4294
3990
  link(x, y, w, h, url, options = {}) {
4295
3991
  options.Subtype = 'Link';
4296
3992
  if (typeof url === 'number') {
4297
- // Link to a page in the document (the page must already exist)
4298
3993
  const pages = this._root.data.Pages.data;
4299
3994
  if (url >= 0 && url < pages.Kids.length) {
4300
3995
  options.A = this.ref({
@@ -4306,7 +4001,6 @@ var AnnotationsMixin = {
4306
4001
  throw new Error(`The document has no page ${url}`);
4307
4002
  }
4308
4003
  } else {
4309
- // Link to an external url
4310
4004
  options.A = this.ref({
4311
4005
  S: 'URI',
4312
4006
  URI: new String(url)
@@ -4359,14 +4053,11 @@ var AnnotationsMixin = {
4359
4053
  return this.annotate(x, y, w, h, options);
4360
4054
  },
4361
4055
  fileAnnotation(x, y, w, h, file = {}, options = {}) {
4362
- // create hidden file
4363
4056
  const filespec = this.file(file.src, Object.assign({
4364
4057
  hidden: true
4365
4058
  }, file));
4366
4059
  options.Subtype = 'FileAttachment';
4367
4060
  options.FS = filespec;
4368
-
4369
- // add description from filespec unless description (Contents) has already been set
4370
4061
  if (options.Contents) {
4371
4062
  options.Contents = new String(options.Contents);
4372
4063
  } else if (filespec.data.Desc) {
@@ -4375,14 +4066,9 @@ var AnnotationsMixin = {
4375
4066
  return this.annotate(x, y, w, h, options);
4376
4067
  },
4377
4068
  _convertRect(x1, y1, w, h) {
4378
- // flip y1 and y2
4379
4069
  let y2 = y1;
4380
4070
  y1 += h;
4381
-
4382
- // make x2
4383
4071
  let x2 = x1 + w;
4384
-
4385
- // apply current transformation matrix to points
4386
4072
  const [m0, m1, m2, m3, m4, m5] = this._ctm;
4387
4073
  x1 = m0 * x1 + m2 * y1 + m4;
4388
4074
  y1 = m1 * x1 + m3 * y1 + m5;
@@ -4444,7 +4130,7 @@ class PDFOutline {
4444
4130
 
4445
4131
  var OutlineMixin = {
4446
4132
  initOutline() {
4447
- return this.outline = new PDFOutline(this, null, null, null);
4133
+ this.outline = new PDFOutline(this, null, null, null);
4448
4134
  },
4449
4135
  endOutline() {
4450
4136
  this.outline.endOutline();
@@ -4455,11 +4141,6 @@ var OutlineMixin = {
4455
4141
  }
4456
4142
  };
4457
4143
 
4458
- /*
4459
- PDFStructureContent - a reference to a marked structure content
4460
- By Ben Schmidt
4461
- */
4462
-
4463
4144
  class PDFStructureContent {
4464
4145
  constructor(pageRef, mcid) {
4465
4146
  this.refs = [{
@@ -4472,10 +4153,6 @@ class PDFStructureContent {
4472
4153
  }
4473
4154
  }
4474
4155
 
4475
- /*
4476
- PDFStructureElement - represents an element in the PDF logical structure tree
4477
- By Ben Schmidt
4478
- */
4479
4156
  class PDFStructureElement {
4480
4157
  constructor(document, type, options = {}, children = null) {
4481
4158
  this.document = document;
@@ -4483,7 +4160,6 @@ class PDFStructureElement {
4483
4160
  this._ended = false;
4484
4161
  this._flushed = false;
4485
4162
  this.dictionary = document.ref({
4486
- // Type: "StructElem",
4487
4163
  S: type
4488
4164
  });
4489
4165
  const data = this.dictionary.data;
@@ -4532,7 +4208,6 @@ class PDFStructureElement {
4532
4208
  this._addContentToParentTree(child);
4533
4209
  }
4534
4210
  if (typeof child === 'function' && this._attached) {
4535
- // _contentForClosure() adds the content to the parent tree
4536
4211
  child = this._contentForClosure(child);
4537
4212
  }
4538
4213
  this._children.push(child);
@@ -4608,10 +4283,6 @@ class PDFStructureElement {
4608
4283
  this.dictionary.data.K = [];
4609
4284
  this._children.forEach(child => this._flushChild(child));
4610
4285
  this.dictionary.end();
4611
-
4612
- // free memory used by children; the dictionary itself may still be
4613
- // referenced by a parent structure element or root, but we can
4614
- // at least trim the tree here
4615
4286
  this._children = [];
4616
4287
  this.dictionary.data.K = null;
4617
4288
  this._flushed = true;
@@ -4632,7 +4303,7 @@ class PDFStructureElement {
4632
4303
  this.dictionary.data.K.push(mcid);
4633
4304
  } else {
4634
4305
  this.dictionary.data.K.push({
4635
- Type: "MCR",
4306
+ Type: 'MCR',
4636
4307
  Pg: pageRef,
4637
4308
  MCID: mcid
4638
4309
  });
@@ -4642,25 +4313,18 @@ class PDFStructureElement {
4642
4313
  }
4643
4314
  }
4644
4315
 
4645
- /*
4646
- PDFNumberTree - represents a number tree object
4647
- */
4648
4316
  class PDFNumberTree extends PDFTree {
4649
4317
  _compareKeys(a, b) {
4650
4318
  return parseInt(a) - parseInt(b);
4651
4319
  }
4652
4320
  _keysName() {
4653
- return "Nums";
4321
+ return 'Nums';
4654
4322
  }
4655
4323
  _dataForKey(k) {
4656
4324
  return parseInt(k);
4657
4325
  }
4658
4326
  }
4659
4327
 
4660
- /*
4661
- Markings mixin - support marked content sequences in content streams
4662
- By Ben Schmidt
4663
- */
4664
4328
  var MarkingsMixin = {
4665
4329
  initMarkings(options) {
4666
4330
  this.structChildren = [];
@@ -4796,7 +4460,6 @@ var MarkingsMixin = {
4796
4460
  return this.getStructTreeRoot().data.ParentTree;
4797
4461
  },
4798
4462
  createStructParentTreeNextKey() {
4799
- // initialise the MarkInfo dictionary
4800
4463
  this.getMarkInfoDictionary();
4801
4464
  const structTreeRoot = this.getStructTreeRoot();
4802
4465
  const key = structTreeRoot.data.ParentTreeNextKey++;
@@ -4860,10 +4523,6 @@ const FORMAT_DEFAULT = {
4860
4523
  }
4861
4524
  };
4862
4525
  var AcroFormMixin = {
4863
- /**
4864
- * Must call if adding AcroForms to a document. Must also call font() before
4865
- * this method to set the default font.
4866
- */
4867
4526
  initForm() {
4868
4527
  if (!this._font) {
4869
4528
  throw new Error('Must set a font before calling initForm method');
@@ -4886,9 +4545,6 @@ var AcroFormMixin = {
4886
4545
  this._root.data.AcroForm = AcroForm;
4887
4546
  return this;
4888
4547
  },
4889
- /**
4890
- * Called automatically by document.js
4891
- */
4892
4548
  endAcroForm() {
4893
4549
  if (this._root.data.AcroForm) {
4894
4550
  if (!Object.keys(this._acroform.fonts).length && !this._acroform.defaultFont) {
@@ -4914,38 +4570,18 @@ var AcroFormMixin = {
4914
4570
  }
4915
4571
  return this;
4916
4572
  },
4917
- /**
4918
- * Creates and adds a form field to the document. Form fields are intermediate
4919
- * nodes in a PDF form that are used to specify form name heirarchy and form
4920
- * value defaults.
4921
- * @param {string} name - field name (T attribute in field dictionary)
4922
- * @param {object} options - other attributes to include in field dictionary
4923
- */
4924
4573
  formField(name, options = {}) {
4925
4574
  let fieldDict = this._fieldDict(name, null, options);
4926
4575
  let fieldRef = this.ref(fieldDict);
4927
4576
  this._addToParent(fieldRef);
4928
4577
  return fieldRef;
4929
4578
  },
4930
- /**
4931
- * Creates and adds a Form Annotation to the document. Form annotations are
4932
- * called Widget annotations internally within a PDF file.
4933
- * @param {string} name - form field name (T attribute of widget annotation
4934
- * dictionary)
4935
- * @param {number} x
4936
- * @param {number} y
4937
- * @param {number} w
4938
- * @param {number} h
4939
- * @param {object} options
4940
- */
4941
4579
  formAnnotation(name, type, x, y, w, h, options = {}) {
4942
4580
  let fieldDict = this._fieldDict(name, type, options);
4943
4581
  fieldDict.Subtype = 'Widget';
4944
4582
  if (fieldDict.F === undefined) {
4945
- fieldDict.F = 4; // print the annotation
4583
+ fieldDict.F = 4;
4946
4584
  }
4947
-
4948
- // Add Field annot to page, and get it's ref
4949
4585
  this.annotate(x, y, w, h, fieldDict);
4950
4586
  let annotRef = this.page.annotations[this.page.annotations.length - 1];
4951
4587
  return this._addToParent(annotRef);
@@ -5106,23 +4742,18 @@ var AcroFormMixin = {
5106
4742
  delete options.align;
5107
4743
  }
5108
4744
  if (result !== 0) {
5109
- options.Q = result; // default
4745
+ options.Q = result;
5110
4746
  }
5111
4747
  return options;
5112
4748
  },
5113
4749
  _resolveFont(options) {
5114
- // add current font to document-level AcroForm dict if necessary
5115
4750
  if (this._acroform.fonts[this._font.id] == null) {
5116
4751
  this._acroform.fonts[this._font.id] = this._font.ref();
5117
4752
  }
5118
-
5119
- // add current font to field's resource dict (RD) if not the default acroform font
5120
4753
  if (this._acroform.defaultFont !== this._font.name) {
5121
4754
  options.DR = {
5122
4755
  Font: {}
5123
4756
  };
5124
-
5125
- // Get the fontSize option. If not set use auto sizing
5126
4757
  const fontSize = options.fontSize || 0;
5127
4758
  options.DR.Font[this._font.id] = this._font.ref();
5128
4759
  options.DA = new String(`/${this._font.id} ${fontSize} Tf 0 g`);
@@ -5174,19 +4805,6 @@ var AcroFormMixin = {
5174
4805
  };
5175
4806
 
5176
4807
  var AttachmentsMixin = {
5177
- /**
5178
- * Embed contents of `src` in PDF
5179
- * @param {Buffer | ArrayBuffer | string} src input Buffer, ArrayBuffer, base64 encoded string or path to file
5180
- * @param {object} options
5181
- * * options.name: filename to be shown in PDF, will use `src` if none set
5182
- * * options.type: filetype to be shown in PDF
5183
- * * options.description: description to be shown in PDF
5184
- * * options.hidden: if true, do not add attachment to EmbeddedFiles dictionary. Useful for file attachment annotations
5185
- * * options.creationDate: override creation date
5186
- * * options.modifiedDate: override modified date
5187
- * * options.relationship: Relationship between the PDF document and its attached file. Can be 'Alternative', 'Data', 'Source', 'Supplement' or 'Unspecified'.
5188
- * @returns filespec reference
5189
- */
5190
4808
  file(src, options = {}) {
5191
4809
  options.name = options.name || src;
5192
4810
  options.relationship = options.relationship || 'Unspecified';
@@ -5214,8 +4832,6 @@ var AttachmentsMixin = {
5214
4832
  if (!data) {
5215
4833
  throw new Error(`Could not read contents of file at filepath ${src}`);
5216
4834
  }
5217
-
5218
- // update CreationDate and ModDate
5219
4835
  const {
5220
4836
  birthtime,
5221
4837
  ctime
@@ -5224,26 +4840,18 @@ var AttachmentsMixin = {
5224
4840
  refBody.Params.ModDate = ctime;
5225
4841
  }
5226
4842
  }
5227
-
5228
- // override creation date and modified date
5229
4843
  if (options.creationDate instanceof Date) {
5230
4844
  refBody.Params.CreationDate = options.creationDate;
5231
4845
  }
5232
4846
  if (options.modifiedDate instanceof Date) {
5233
4847
  refBody.Params.ModDate = options.modifiedDate;
5234
4848
  }
5235
- // add optional subtype
5236
4849
  if (options.type) {
5237
4850
  refBody.Subtype = options.type.replace('/', '#2F');
5238
4851
  }
5239
-
5240
- // add checksum and size information
5241
4852
  const checksum = CryptoJS.MD5(CryptoJS.lib.WordArray.create(new Uint8Array(data)));
5242
4853
  refBody.Params.CheckSum = new String(checksum);
5243
4854
  refBody.Params.Size = data.byteLength;
5244
-
5245
- // save some space when embedding the same file again
5246
- // if a file with the same name and metadata exists, reuse its reference
5247
4855
  let ref;
5248
4856
  if (!this._fileRegistry) this._fileRegistry = {};
5249
4857
  let file = this._fileRegistry[options.name];
@@ -5257,7 +4865,6 @@ var AttachmentsMixin = {
5257
4865
  ref
5258
4866
  };
5259
4867
  }
5260
- // add filespec for embedded file
5261
4868
  const fileSpecBody = {
5262
4869
  Type: 'Filespec',
5263
4870
  AFRelationship: options.relationship,
@@ -5275,8 +4882,6 @@ var AttachmentsMixin = {
5275
4882
  if (!options.hidden) {
5276
4883
  this.addNamedEmbeddedFile(options.name, filespec);
5277
4884
  }
5278
-
5279
- // Add file to the catalogue to be PDF/A3 compliant
5280
4885
  if (this._root.data.AF) {
5281
4886
  this._root.data.AF.push(filespec);
5282
4887
  } else {
@@ -5285,8 +4890,6 @@ var AttachmentsMixin = {
5285
4890
  return filespec;
5286
4891
  }
5287
4892
  };
5288
-
5289
- /** check two embedded file metadata objects for equality */
5290
4893
  function isEqual(a, b) {
5291
4894
  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());
5292
4895
  }
@@ -5297,7 +4900,6 @@ var PDFA = {
5297
4900
  this.subset_conformance = pSubset.charAt(pSubset.length - 1).toUpperCase();
5298
4901
  this.subset = parseInt(pSubset.charAt(pSubset.length - 2));
5299
4902
  } else {
5300
- // Default to Basic conformance when user doesn't specify
5301
4903
  this.subset_conformance = 'B';
5302
4904
  this.subset = parseInt(pSubset.charAt(pSubset.length - 1));
5303
4905
  }
@@ -5382,6 +4984,642 @@ var SubsetMixin = {
5382
4984
  }
5383
4985
  };
5384
4986
 
4987
+ const ROW_FIELDS = ['height', 'minHeight', 'maxHeight'];
4988
+ const COLUMN_FIELDS = ['width', 'minWidth', 'maxWidth'];
4989
+ function memoize(fn, maxSize) {
4990
+ const cache = new Map();
4991
+ return function (...args) {
4992
+ const key = args[0];
4993
+ if (!cache.has(key)) {
4994
+ cache.set(key, fn(...args));
4995
+ if (cache.size > maxSize) cache.delete(cache.keys().next());
4996
+ }
4997
+ return cache.get(key);
4998
+ };
4999
+ }
5000
+ function isObject(item) {
5001
+ return item && typeof item === 'object' && !Array.isArray(item);
5002
+ }
5003
+ function deepMerge(target, ...sources) {
5004
+ if (!isObject(target)) return target;
5005
+ target = deepClone(target);
5006
+ for (const source of sources) {
5007
+ if (isObject(source)) {
5008
+ for (const key in source) {
5009
+ if (isObject(source[key])) {
5010
+ if (!(key in target)) target[key] = {};
5011
+ target[key] = deepMerge(target[key], source[key]);
5012
+ } else if (source[key] !== undefined) {
5013
+ target[key] = deepClone(source[key]);
5014
+ }
5015
+ }
5016
+ }
5017
+ }
5018
+ return target;
5019
+ }
5020
+ function deepClone(obj) {
5021
+ let result = obj;
5022
+ if (typeof obj == 'object') {
5023
+ result = Array.isArray(obj) ? [] : {};
5024
+ for (const key in obj) result[key] = deepClone(obj[key]);
5025
+ }
5026
+ return result;
5027
+ }
5028
+
5029
+ function normalizedDefaultStyle(defaultStyleInternal) {
5030
+ let defaultStyle = defaultStyleInternal;
5031
+ if (typeof defaultStyle !== 'object') defaultStyle = {
5032
+ text: defaultStyle
5033
+ };
5034
+ const defaultRowStyle = Object.fromEntries(Object.entries(defaultStyle).filter(([k]) => ROW_FIELDS.includes(k)));
5035
+ const defaultColStyle = Object.fromEntries(Object.entries(defaultStyle).filter(([k]) => COLUMN_FIELDS.includes(k)));
5036
+ defaultStyle.padding = normalizeSides(defaultStyle.padding);
5037
+ defaultStyle.border = normalizeSides(defaultStyle.border);
5038
+ defaultStyle.borderColor = normalizeSides(defaultStyle.borderColor);
5039
+ defaultStyle.align = normalizeAlignment(defaultStyle.align);
5040
+ return {
5041
+ defaultStyle,
5042
+ defaultRowStyle,
5043
+ defaultColStyle
5044
+ };
5045
+ }
5046
+ function normalizedRowStyle(defaultRowStyle, rowStyleInternal, i) {
5047
+ let rowStyle = rowStyleInternal(i);
5048
+ if (rowStyle == null || typeof rowStyle !== 'object') {
5049
+ rowStyle = {
5050
+ height: rowStyle
5051
+ };
5052
+ }
5053
+ rowStyle.padding = normalizeSides(rowStyle.padding);
5054
+ rowStyle.border = normalizeSides(rowStyle.border);
5055
+ rowStyle.borderColor = normalizeSides(rowStyle.borderColor);
5056
+ rowStyle.align = normalizeAlignment(rowStyle.align);
5057
+ rowStyle = deepMerge(defaultRowStyle, rowStyle);
5058
+ const document = this.document;
5059
+ const page = document.page;
5060
+ const contentHeight = page.contentHeight;
5061
+ if (rowStyle.height == null || rowStyle.height === 'auto') {
5062
+ rowStyle.height = 'auto';
5063
+ } else {
5064
+ rowStyle.height = document.sizeToPoint(rowStyle.height, 0, page, contentHeight);
5065
+ }
5066
+ rowStyle.minHeight = document.sizeToPoint(rowStyle.minHeight, 0, page, contentHeight);
5067
+ rowStyle.maxHeight = document.sizeToPoint(rowStyle.maxHeight, 0, page, contentHeight);
5068
+ return rowStyle;
5069
+ }
5070
+ function normalizedColumnStyle(defaultColStyle, colStyleInternal, i) {
5071
+ let colStyle = colStyleInternal(i);
5072
+ if (colStyle == null || typeof colStyle !== 'object') {
5073
+ colStyle = {
5074
+ width: colStyle
5075
+ };
5076
+ }
5077
+ colStyle.padding = normalizeSides(colStyle.padding);
5078
+ colStyle.border = normalizeSides(colStyle.border);
5079
+ colStyle.borderColor = normalizeSides(colStyle.borderColor);
5080
+ colStyle.align = normalizeAlignment(colStyle.align);
5081
+ colStyle = deepMerge(defaultColStyle, colStyle);
5082
+ if (colStyle.width == null || colStyle.width === '*') {
5083
+ colStyle.width = '*';
5084
+ } else {
5085
+ colStyle.width = this.document.sizeToPoint(colStyle.width, 0, this.document.page, this._maxWidth);
5086
+ }
5087
+ colStyle.minWidth = this.document.sizeToPoint(colStyle.minWidth, 0, this.document.page, this._maxWidth);
5088
+ colStyle.maxWidth = this.document.sizeToPoint(colStyle.maxWidth, 0, this.document.page, this._maxWidth);
5089
+ return colStyle;
5090
+ }
5091
+ function normalizeAlignment(align) {
5092
+ return align == null || typeof align === 'string' ? {
5093
+ x: align,
5094
+ y: align
5095
+ } : align;
5096
+ }
5097
+
5098
+ function normalizeTable() {
5099
+ const doc = this.document;
5100
+ const opts = this.opts;
5101
+ let index = doc._tableIndex++;
5102
+ this._id = new String(opts.id ?? `table-${index}`);
5103
+ this._position = {
5104
+ x: doc.sizeToPoint(opts.position?.x, doc.x),
5105
+ y: doc.sizeToPoint(opts.position?.y, doc.y)
5106
+ };
5107
+ this._maxWidth = doc.sizeToPoint(opts.maxWidth, doc.page.width - doc.page.margins.right - this._position.x);
5108
+ const {
5109
+ defaultStyle,
5110
+ defaultColStyle,
5111
+ defaultRowStyle
5112
+ } = normalizedDefaultStyle(opts.defaultStyle);
5113
+ this._defaultStyle = defaultStyle;
5114
+ let colStyle;
5115
+ if (opts.columnStyles) {
5116
+ if (Array.isArray(opts.columnStyles)) {
5117
+ colStyle = i => opts.columnStyles[i];
5118
+ } else if (typeof opts.columnStyles === 'function') {
5119
+ colStyle = memoize(i => opts.columnStyles(i), Infinity);
5120
+ } else if (typeof opts.columnStyles === 'object') {
5121
+ colStyle = () => opts.columnStyles;
5122
+ }
5123
+ }
5124
+ if (!colStyle) colStyle = () => ({});
5125
+ this._colStyle = normalizedColumnStyle.bind(this, defaultColStyle, colStyle);
5126
+ let rowStyle;
5127
+ if (opts.rowStyles) {
5128
+ if (Array.isArray(opts.rowStyles)) {
5129
+ rowStyle = i => opts.rowStyles[i];
5130
+ } else if (typeof opts.rowStyles === 'function') {
5131
+ rowStyle = memoize(i => opts.rowStyles(i), 10);
5132
+ } else if (typeof opts.rowStyles === 'object') {
5133
+ rowStyle = () => opts.rowStyles;
5134
+ }
5135
+ }
5136
+ if (!rowStyle) rowStyle = () => ({});
5137
+ this._rowStyle = normalizedRowStyle.bind(this, defaultRowStyle, rowStyle);
5138
+ }
5139
+ function normalizeText(text) {
5140
+ if (text != null) text = `${text}`;
5141
+ return text;
5142
+ }
5143
+ function normalizeCell(cell, rowIndex, colIndex) {
5144
+ const colStyle = this._colStyle(colIndex);
5145
+ let rowStyle = this._rowStyle(rowIndex);
5146
+ const font = deepMerge({}, colStyle.font, rowStyle.font, cell.font);
5147
+ const customFont = Object.values(font).filter(v => v != null).length > 0;
5148
+ const doc = this.document;
5149
+ const rollbackFont = doc._fontSource;
5150
+ const rollbackFontSize = doc._fontSize;
5151
+ const rollbackFontFamily = doc._fontFamily;
5152
+ if (customFont) {
5153
+ if (font.src) doc.font(font.src, font.family);
5154
+ if (font.size) doc.fontSize(font.size);
5155
+ rowStyle = this._rowStyle(rowIndex);
5156
+ }
5157
+ cell.padding = normalizeSides(cell.padding);
5158
+ cell.border = normalizeSides(cell.border);
5159
+ cell.borderColor = normalizeSides(cell.borderColor);
5160
+ const config = deepMerge(this._defaultStyle, colStyle, rowStyle, cell);
5161
+ config.rowIndex = rowIndex;
5162
+ config.colIndex = colIndex;
5163
+ config.font = font ?? {};
5164
+ config.customFont = customFont;
5165
+ config.text = normalizeText(config.text);
5166
+ config.rowSpan = config.rowSpan ?? 1;
5167
+ config.colSpan = config.colSpan ?? 1;
5168
+ config.padding = normalizeSides(config.padding, '0.25em', x => doc.sizeToPoint(x, '0.25em'));
5169
+ config.border = normalizeSides(config.border, 1, x => doc.sizeToPoint(x, 1));
5170
+ config.borderColor = normalizeSides(config.borderColor, 'black', x => x ?? 'black');
5171
+ config.align = normalizeAlignment(config.align);
5172
+ config.align.x = config.align.x ?? 'left';
5173
+ config.align.y = config.align.y ?? 'top';
5174
+ config.textStroke = doc.sizeToPoint(config.textStroke, 0);
5175
+ config.textStrokeColor = config.textStrokeColor ?? 'black';
5176
+ config.textColor = config.textColor ?? 'black';
5177
+ config.textOptions = config.textOptions ?? {};
5178
+ config.id = new String(config.id ?? `${this._id}-${rowIndex}-${colIndex}`);
5179
+ config.type = config.type?.toUpperCase() === 'TH' ? 'TH' : 'TD';
5180
+ if (config.scope) {
5181
+ config.scope = config.scope.toLowerCase();
5182
+ if (config.scope === 'row') config.scope = 'Row';else if (config.scope === 'both') config.scope = 'Both';else if (config.scope === 'column') config.scope = 'Column';
5183
+ }
5184
+ if (typeof this.opts.debug === 'boolean') config.debug = this.opts.debug;
5185
+ if (customFont) doc.font(rollbackFont, rollbackFontFamily, rollbackFontSize);
5186
+ return config;
5187
+ }
5188
+ function normalizeRow(row, rowIndex) {
5189
+ if (!this._cellClaim) this._cellClaim = new Set();
5190
+ let colIndex = 0;
5191
+ return row.map(cell => {
5192
+ if (cell == null || typeof cell !== 'object') cell = {
5193
+ text: cell
5194
+ };
5195
+ while (this._cellClaim.has(`${rowIndex},${colIndex}`)) {
5196
+ colIndex++;
5197
+ }
5198
+ cell = normalizeCell.call(this, cell, rowIndex, colIndex);
5199
+ for (let i = 0; i < cell.rowSpan; i++) {
5200
+ for (let j = 0; j < cell.colSpan; j++) {
5201
+ this._cellClaim.add(`${rowIndex + i},${colIndex + j}`);
5202
+ }
5203
+ }
5204
+ colIndex += cell.colSpan;
5205
+ return cell;
5206
+ });
5207
+ }
5208
+
5209
+ function ensure(row) {
5210
+ this._columnWidths = [];
5211
+ ensureColumnWidths.call(this, row.reduce((a, cell) => a + cell.colSpan, 0));
5212
+ this._rowHeights = [];
5213
+ this._rowYPos = [this._position.y];
5214
+ this._rowBuffer = new Set();
5215
+ }
5216
+ function ensureColumnWidths(numCols) {
5217
+ let starColumnIndexes = [];
5218
+ let starMinAcc = 0;
5219
+ let unclaimedWidth = this._maxWidth;
5220
+ for (let i = 0; i < numCols; i++) {
5221
+ let col = this._colStyle(i);
5222
+ if (col.width === '*') {
5223
+ starColumnIndexes[i] = col;
5224
+ starMinAcc += col.minWidth;
5225
+ } else {
5226
+ unclaimedWidth -= col.width;
5227
+ this._columnWidths[i] = col.width;
5228
+ }
5229
+ }
5230
+ let starColCount = starColumnIndexes.reduce(x => x + 1, 0);
5231
+ if (starMinAcc >= unclaimedWidth) {
5232
+ starColumnIndexes.forEach((cell, i) => {
5233
+ this._columnWidths[i] = cell.minWidth;
5234
+ });
5235
+ } else if (starColCount > 0) {
5236
+ starColumnIndexes.forEach((col, i) => {
5237
+ let starSize = unclaimedWidth / starColCount;
5238
+ this._columnWidths[i] = Math.max(starSize, col.minWidth);
5239
+ if (col.maxWidth > 0) {
5240
+ this._columnWidths[i] = Math.min(this._columnWidths[i], col.maxWidth);
5241
+ }
5242
+ unclaimedWidth -= this._columnWidths[i];
5243
+ starColCount--;
5244
+ });
5245
+ }
5246
+ let tempX = this._position.x;
5247
+ this._columnXPos = Array.from(this._columnWidths, v => {
5248
+ const t = tempX;
5249
+ tempX += v;
5250
+ return t;
5251
+ });
5252
+ }
5253
+ function measure(row, rowIndex) {
5254
+ row.forEach(cell => this._rowBuffer.add(cell));
5255
+ if (rowIndex > 0) {
5256
+ this._rowYPos[rowIndex] = this._rowYPos[rowIndex - 1] + this._rowHeights[rowIndex - 1];
5257
+ }
5258
+ const rowStyle = this._rowStyle(rowIndex);
5259
+ let toRender = [];
5260
+ this._rowBuffer.forEach(cell => {
5261
+ if (cell.rowIndex + cell.rowSpan - 1 === rowIndex) {
5262
+ toRender.push(measureCell.call(this, cell, rowStyle.height));
5263
+ this._rowBuffer.delete(cell);
5264
+ }
5265
+ });
5266
+ let rowHeight = rowStyle.height;
5267
+ if (rowHeight === 'auto') {
5268
+ rowHeight = toRender.reduce((acc, cell) => {
5269
+ let minHeight = cell.textBounds.height + cell.padding.top + cell.padding.bottom;
5270
+ for (let i = 0; i < cell.rowSpan - 1; i++) {
5271
+ minHeight -= this._rowHeights[cell.rowIndex + i];
5272
+ }
5273
+ return Math.max(acc, minHeight);
5274
+ }, 0);
5275
+ }
5276
+ rowHeight = Math.max(rowHeight, rowStyle.minHeight);
5277
+ if (rowStyle.maxHeight > 0) {
5278
+ rowHeight = Math.min(rowHeight, rowStyle.maxHeight);
5279
+ }
5280
+ this._rowHeights[rowIndex] = rowHeight;
5281
+ let newPage = false;
5282
+ if (rowHeight > this.document.page.contentHeight) {
5283
+ console.warn(new Error(`Row ${rowIndex} requested more than the safe page height, row has been clamped`).stack.slice(7));
5284
+ this._rowHeights[rowIndex] = this.document.page.maxY() - this._rowYPos[rowIndex];
5285
+ } else if (this._rowYPos[rowIndex] + rowHeight >= this.document.page.maxY()) {
5286
+ this._rowYPos[rowIndex] = this.document.page.margins.top;
5287
+ newPage = true;
5288
+ }
5289
+ return {
5290
+ newPage,
5291
+ toRender: toRender.map(cell => measureCell.call(this, cell, rowHeight))
5292
+ };
5293
+ }
5294
+ function measureCell(cell, rowHeight) {
5295
+ let cellWidth = 0;
5296
+ for (let i = 0; i < cell.colSpan; i++) {
5297
+ cellWidth += this._columnWidths[cell.colIndex + i];
5298
+ }
5299
+ let cellHeight = rowHeight;
5300
+ if (cellHeight === 'auto') {
5301
+ cellHeight = this.document.page.contentHeight;
5302
+ } else {
5303
+ for (let i = 0; i < cell.rowSpan - 1; i++) {
5304
+ cellHeight += this._rowHeights[cell.rowIndex + i];
5305
+ }
5306
+ }
5307
+ const textAllocatedWidth = cellWidth - cell.padding.left - cell.padding.right;
5308
+ const textAllocatedHeight = cellHeight - cell.padding.top - cell.padding.bottom;
5309
+ const rotation = cell.textOptions.rotation ?? 0;
5310
+ const {
5311
+ width: textMaxWidth,
5312
+ height: textMaxHeight
5313
+ } = computeBounds(rotation, textAllocatedWidth, textAllocatedHeight);
5314
+ const textOptions = {
5315
+ align: cell.align.x,
5316
+ ellipsis: true,
5317
+ stroke: cell.textStroke > 0,
5318
+ fill: true,
5319
+ width: textMaxWidth,
5320
+ height: textMaxHeight,
5321
+ rotation,
5322
+ ...cell.textOptions
5323
+ };
5324
+ let textBounds = {
5325
+ x: 0,
5326
+ y: 0,
5327
+ width: 0,
5328
+ height: 0
5329
+ };
5330
+ if (cell.text) {
5331
+ const rollbackFont = this.document._fontSource;
5332
+ const rollbackFontSize = this.document._fontSize;
5333
+ const rollbackFontFamily = this.document._fontFamily;
5334
+ if (cell.font?.src) this.document.font(cell.font.src, cell.font?.family);
5335
+ if (cell.font?.size) this.document.fontSize(cell.font.size);
5336
+ const unRotatedTextBounds = this.document.boundsOfString(cell.text, 0, 0, {
5337
+ ...textOptions,
5338
+ rotation: 0
5339
+ });
5340
+ textOptions.width = unRotatedTextBounds.width;
5341
+ textOptions.height = unRotatedTextBounds.height;
5342
+ textBounds = this.document.boundsOfString(cell.text, 0, 0, textOptions);
5343
+ this.document.font(rollbackFont, rollbackFontFamily, rollbackFontSize);
5344
+ }
5345
+ return {
5346
+ ...cell,
5347
+ textOptions,
5348
+ x: this._columnXPos[cell.colIndex],
5349
+ y: this._rowYPos[cell.rowIndex],
5350
+ textX: this._columnXPos[cell.colIndex] + cell.padding.left,
5351
+ textY: this._rowYPos[cell.rowIndex] + cell.padding.top,
5352
+ width: cellWidth,
5353
+ height: cellHeight,
5354
+ textAllocatedHeight,
5355
+ textAllocatedWidth,
5356
+ textBounds
5357
+ };
5358
+ }
5359
+ function computeBounds(rotation, allocWidth, allocHeight) {
5360
+ let textMaxWidth, textMaxHeight;
5361
+ const cos = cosine(rotation);
5362
+ const sin = sine(rotation);
5363
+ if (rotation === 0 || rotation === 180) {
5364
+ textMaxWidth = allocWidth;
5365
+ textMaxHeight = allocHeight;
5366
+ } else if (rotation === 90 || rotation === 270) {
5367
+ textMaxWidth = allocHeight;
5368
+ textMaxHeight = allocWidth;
5369
+ } else if (rotation < 90 || rotation > 180 && rotation < 270) {
5370
+ textMaxWidth = allocWidth / (2 * cos);
5371
+ textMaxHeight = allocWidth / (2 * sin);
5372
+ } else {
5373
+ textMaxHeight = allocWidth / (2 * cos);
5374
+ textMaxWidth = allocWidth / (2 * sin);
5375
+ }
5376
+ const EF = sin * textMaxWidth;
5377
+ const FG = cos * textMaxHeight;
5378
+ if (EF + FG > allocHeight) {
5379
+ const denominator = cos * cos - sin * sin;
5380
+ if (rotation === 0 || rotation === 180) {
5381
+ textMaxWidth = allocWidth;
5382
+ textMaxHeight = allocHeight;
5383
+ } else if (rotation === 90 || rotation === 270) {
5384
+ textMaxWidth = allocHeight;
5385
+ textMaxHeight = allocWidth;
5386
+ } else if (rotation < 90 || rotation > 180 && rotation < 270) {
5387
+ textMaxWidth = (allocWidth * cos - allocHeight * sin) / denominator;
5388
+ textMaxHeight = (allocHeight * cos - allocWidth * sin) / denominator;
5389
+ } else {
5390
+ textMaxHeight = (allocWidth * cos - allocHeight * sin) / denominator;
5391
+ textMaxWidth = (allocHeight * cos - allocWidth * sin) / denominator;
5392
+ }
5393
+ }
5394
+ return {
5395
+ width: Math.abs(textMaxWidth),
5396
+ height: Math.abs(textMaxHeight)
5397
+ };
5398
+ }
5399
+
5400
+ function accommodateTable() {
5401
+ const structParent = this.opts.structParent;
5402
+ if (structParent) {
5403
+ this._tableStruct = this.document.struct('Table');
5404
+ this._tableStruct.dictionary.data.ID = this._id;
5405
+ if (structParent instanceof PDFStructureElement) {
5406
+ structParent.add(this._tableStruct);
5407
+ } else if (structParent instanceof PDFDocument) {
5408
+ structParent.addStructure(this._tableStruct);
5409
+ }
5410
+ this._headerRowLookup = {};
5411
+ this._headerColumnLookup = {};
5412
+ }
5413
+ }
5414
+ function accommodateCleanup() {
5415
+ if (this._tableStruct) this._tableStruct.end();
5416
+ }
5417
+ function accessibleRow(row, rowIndex, renderCell) {
5418
+ const rowStruct = this.document.struct('TR');
5419
+ rowStruct.dictionary.data.ID = new String(`${this._id}-${rowIndex}`);
5420
+ this._tableStruct.add(rowStruct);
5421
+ row.forEach(cell => renderCell(cell, rowStruct));
5422
+ rowStruct.end();
5423
+ }
5424
+ function accessibleCell(cell, rowStruct, callback) {
5425
+ const doc = this.document;
5426
+ const cellStruct = doc.struct(cell.type, {
5427
+ title: cell.title
5428
+ });
5429
+ cellStruct.dictionary.data.ID = cell.id;
5430
+ rowStruct.add(cellStruct);
5431
+ const padding = cell.padding;
5432
+ const border = cell.border;
5433
+ const attributes = {
5434
+ O: 'Table',
5435
+ Width: cell.width,
5436
+ Height: cell.height,
5437
+ Padding: [padding.top, padding.bottom, padding.left, padding.right],
5438
+ RowSpan: cell.rowSpan > 1 ? cell.rowSpan : undefined,
5439
+ ColSpan: cell.colSpan > 1 ? cell.colSpan : undefined,
5440
+ BorderThickness: [border.top, border.bottom, border.left, border.right]
5441
+ };
5442
+ if (cell.type === 'TH') {
5443
+ if (cell.scope === 'Row' || cell.scope === 'Both') {
5444
+ for (let i = 0; i < cell.rowSpan; i++) {
5445
+ if (!this._headerRowLookup[cell.rowIndex + i]) {
5446
+ this._headerRowLookup[cell.rowIndex + i] = [];
5447
+ }
5448
+ this._headerRowLookup[cell.rowIndex + i].push(cell.id);
5449
+ }
5450
+ attributes.Scope = cell.scope;
5451
+ }
5452
+ if (cell.scope === 'Column' || cell.scope === 'Both') {
5453
+ for (let i = 0; i < cell.colSpan; i++) {
5454
+ if (!this._headerColumnLookup[cell.colIndex + i]) {
5455
+ this._headerColumnLookup[cell.colIndex + i] = [];
5456
+ }
5457
+ this._headerColumnLookup[cell.colIndex + i].push(cell.id);
5458
+ }
5459
+ attributes.Scope = cell.scope;
5460
+ }
5461
+ }
5462
+ const Headers = new Set([...Array.from({
5463
+ length: cell.colSpan
5464
+ }, (_, i) => this._headerColumnLookup[cell.colIndex + i]).flat(), ...Array.from({
5465
+ length: cell.rowSpan
5466
+ }, (_, i) => this._headerRowLookup[cell.rowIndex + i]).flat()].filter(Boolean));
5467
+ if (Headers.size) attributes.Headers = Array.from(Headers);
5468
+ const normalizeColor = doc._normalizeColor;
5469
+ if (cell.backgroundColor != null) {
5470
+ attributes.BackgroundColor = normalizeColor(cell.backgroundColor);
5471
+ }
5472
+ const hasBorder = [border.top, border.bottom, border.left, border.right];
5473
+ if (hasBorder.some(x => x)) {
5474
+ const borderColor = cell.borderColor;
5475
+ 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];
5476
+ }
5477
+ Object.keys(attributes).forEach(key => attributes[key] === undefined && delete attributes[key]);
5478
+ cellStruct.dictionary.data.A = doc.ref(attributes);
5479
+ cellStruct.add(callback);
5480
+ cellStruct.end();
5481
+ cellStruct.dictionary.data.A.end();
5482
+ }
5483
+
5484
+ function renderRow(row, rowIndex) {
5485
+ if (this._tableStruct) {
5486
+ accessibleRow.call(this, row, rowIndex, renderCell.bind(this));
5487
+ } else {
5488
+ row.forEach(cell => renderCell.call(this, cell));
5489
+ }
5490
+ return this._rowYPos[rowIndex] + this._rowHeights[rowIndex];
5491
+ }
5492
+ function renderCell(cell, rowStruct) {
5493
+ const cellRenderer = () => {
5494
+ if (cell.backgroundColor != null) {
5495
+ this.document.save().rect(cell.x, cell.y, cell.width, cell.height).fill(cell.backgroundColor).restore();
5496
+ }
5497
+ renderBorder.call(this, cell.border, cell.borderColor, cell.x, cell.y, cell.width, cell.height);
5498
+ if (cell.debug) {
5499
+ this.document.save();
5500
+ this.document.dash(1, {
5501
+ space: 1
5502
+ }).lineWidth(1).strokeOpacity(0.3);
5503
+ this.document.rect(cell.x, cell.y, cell.width, cell.height).stroke('green');
5504
+ this.document.restore();
5505
+ }
5506
+ if (cell.text) renderCellText.call(this, cell);
5507
+ };
5508
+ if (rowStruct) accessibleCell.call(this, cell, rowStruct, cellRenderer);else cellRenderer();
5509
+ }
5510
+ function renderCellText(cell) {
5511
+ const doc = this.document;
5512
+ const rollbackFont = doc._fontSource;
5513
+ const rollbackFontSize = doc._fontSize;
5514
+ const rollbackFontFamily = doc._fontFamily;
5515
+ if (cell.customFont) {
5516
+ if (cell.font.src) doc.font(cell.font.src, cell.font.family);
5517
+ if (cell.font.size) doc.fontSize(cell.font.size);
5518
+ }
5519
+ const x = cell.textX;
5520
+ const y = cell.textY;
5521
+ const Ah = cell.textAllocatedHeight;
5522
+ const Aw = cell.textAllocatedWidth;
5523
+ const Cw = cell.textBounds.width;
5524
+ const Ch = cell.textBounds.height;
5525
+ const Ox = -cell.textBounds.x;
5526
+ const Oy = -cell.textBounds.y;
5527
+ const PxScale = cell.align.x === 'right' ? 1 : cell.align.x === 'center' ? 0.5 : 0;
5528
+ const Px = (Aw - Cw) * PxScale;
5529
+ const PyScale = cell.align.y === 'bottom' ? 1 : cell.align.y === 'center' ? 0.5 : 0;
5530
+ const Py = (Ah - Ch) * PyScale;
5531
+ const dx = Px + Ox;
5532
+ const dy = Py + Oy;
5533
+ if (cell.debug) {
5534
+ doc.save();
5535
+ doc.dash(1, {
5536
+ space: 1
5537
+ }).lineWidth(1).strokeOpacity(0.3);
5538
+ if (cell.text) {
5539
+ 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');
5540
+ }
5541
+ doc.rect(x, y, Aw, Ah).stroke('orange');
5542
+ doc.restore();
5543
+ }
5544
+ doc.save().rect(x, y, Aw, Ah).clip();
5545
+ doc.fillColor(cell.textColor).strokeColor(cell.textStrokeColor);
5546
+ if (cell.textStroke > 0) doc.lineWidth(cell.textStroke);
5547
+ doc.text(cell.text, x + dx, y + dy, cell.textOptions);
5548
+ doc.restore();
5549
+ if (cell.font) doc.font(rollbackFont, rollbackFontFamily, rollbackFontSize);
5550
+ }
5551
+ function renderBorder(border, borderColor, x, y, width, height, mask) {
5552
+ border = Object.fromEntries(Object.entries(border).map(([k, v]) => [k, mask && !mask[k] ? 0 : v]));
5553
+ const doc = this.document;
5554
+ if ([border.right, border.bottom, border.left].every(val => val === border.top)) {
5555
+ if (border.top > 0) {
5556
+ doc.save().lineWidth(border.top).rect(x, y, width, height).stroke(borderColor.top).restore();
5557
+ }
5558
+ } else {
5559
+ if (border.top > 0) {
5560
+ doc.save().lineWidth(border.top).moveTo(x, y).lineTo(x + width, y).stroke(borderColor.top).restore();
5561
+ }
5562
+ if (border.right > 0) {
5563
+ doc.save().lineWidth(border.right).moveTo(x + width, y).lineTo(x + width, y + height).stroke(borderColor.right).restore();
5564
+ }
5565
+ if (border.bottom > 0) {
5566
+ doc.save().lineWidth(border.bottom).moveTo(x + width, y + height).lineTo(x, y + height).stroke(borderColor.bottom).restore();
5567
+ }
5568
+ if (border.left > 0) {
5569
+ doc.save().lineWidth(border.left).moveTo(x, y + height).lineTo(x, y).stroke(borderColor.left).restore();
5570
+ }
5571
+ }
5572
+ }
5573
+
5574
+ class PDFTable {
5575
+ constructor(document, opts = {}) {
5576
+ this.document = document;
5577
+ this.opts = Object.freeze(opts);
5578
+ normalizeTable.call(this);
5579
+ accommodateTable.call(this);
5580
+ this._currRowIndex = 0;
5581
+ this._ended = false;
5582
+ if (opts.data) {
5583
+ for (const row of opts.data) this.row(row);
5584
+ return this.end();
5585
+ }
5586
+ }
5587
+ row(row, lastRow = false) {
5588
+ if (this._ended) {
5589
+ throw new Error(`Table was marked as ended on row ${this._currRowIndex}`);
5590
+ }
5591
+ row = Array.from(row);
5592
+ row = normalizeRow.call(this, row, this._currRowIndex);
5593
+ if (this._currRowIndex === 0) ensure.call(this, row);
5594
+ const {
5595
+ newPage,
5596
+ toRender
5597
+ } = measure.call(this, row, this._currRowIndex);
5598
+ if (newPage) this.document.continueOnNewPage();
5599
+ const yPos = renderRow.call(this, toRender, this._currRowIndex);
5600
+ this.document.x = this._position.x;
5601
+ this.document.y = yPos;
5602
+ if (lastRow) return this.end();
5603
+ this._currRowIndex++;
5604
+ return this;
5605
+ }
5606
+ end() {
5607
+ while (this._rowBuffer?.size) this.row([]);
5608
+ this._ended = true;
5609
+ accommodateCleanup.call(this);
5610
+ return this.document;
5611
+ }
5612
+ }
5613
+
5614
+ var TableMixin = {
5615
+ initTables() {
5616
+ this._tableIndex = 0;
5617
+ },
5618
+ table(opts) {
5619
+ return new PDFTable(this, opts);
5620
+ }
5621
+ };
5622
+
5385
5623
  class PDFMetadata {
5386
5624
  constructor() {
5387
5625
  this._metadata = `
@@ -5423,7 +5661,7 @@ var MetadataMixin = {
5423
5661
  _addInfo() {
5424
5662
  this.appendXML(`
5425
5663
  <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">
5426
- <xmp:CreateDate>${this.info.CreationDate.toISOString().split('.')[0] + "Z"}</xmp:CreateDate>
5664
+ <xmp:CreateDate>${this.info.CreationDate.toISOString().split('.')[0] + 'Z'}</xmp:CreateDate>
5427
5665
  <xmp:CreatorTool>${this.info.Creator}</xmp:CreatorTool>
5428
5666
  </rdf:Description>
5429
5667
  `);
@@ -5476,11 +5714,6 @@ var MetadataMixin = {
5476
5714
  endMetadata() {
5477
5715
  this._addInfo();
5478
5716
  this.metadata.end();
5479
-
5480
- /*
5481
- Metadata was introduced in PDF 1.4, so adding it to 1.3
5482
- will likely only take up more space.
5483
- */
5484
5717
  if (this.version != 1.3) {
5485
5718
  this.metadataRef = this.ref({
5486
5719
  length: this.metadata.getLength(),
@@ -5495,16 +5728,10 @@ var MetadataMixin = {
5495
5728
  }
5496
5729
  };
5497
5730
 
5498
- /*
5499
- PDFDocument - represents an entire PDF document
5500
- By Devon Govett
5501
- */
5502
5731
  class PDFDocument extends stream.Readable {
5503
5732
  constructor(options = {}) {
5504
5733
  super(options);
5505
5734
  this.options = options;
5506
-
5507
- // PDF version
5508
5735
  switch (options.pdfVersion) {
5509
5736
  case '1.4':
5510
5737
  this.version = 1.4;
@@ -5523,13 +5750,9 @@ class PDFDocument extends stream.Readable {
5523
5750
  this.version = 1.3;
5524
5751
  break;
5525
5752
  }
5526
-
5527
- // Whether streams should be compressed
5528
5753
  this.compress = this.options.compress != null ? this.options.compress : true;
5529
5754
  this._pageBuffer = [];
5530
5755
  this._pageBufferStart = 0;
5531
-
5532
- // The PDF object store
5533
5756
  this._offsets = [];
5534
5757
  this._waiting = 0;
5535
5758
  this._ended = false;
@@ -5550,11 +5773,7 @@ class PDFDocument extends stream.Readable {
5550
5773
  if (this.options.lang) {
5551
5774
  this._root.data.Lang = new String(this.options.lang);
5552
5775
  }
5553
-
5554
- // The current page
5555
5776
  this.page = null;
5556
-
5557
- // Initialize mixins
5558
5777
  this.initMetadata();
5559
5778
  this.initColor();
5560
5779
  this.initVector();
@@ -5563,9 +5782,8 @@ class PDFDocument extends stream.Readable {
5563
5782
  this.initImages();
5564
5783
  this.initOutline();
5565
5784
  this.initMarkings(options);
5785
+ this.initTables();
5566
5786
  this.initSubset(options);
5567
-
5568
- // Initialize the metadata
5569
5787
  this.info = {
5570
5788
  Producer: 'PDFKit',
5571
5789
  Creator: 'PDFKit',
@@ -5582,21 +5800,10 @@ class PDFDocument extends stream.Readable {
5582
5800
  DisplayDocTitle: true
5583
5801
  });
5584
5802
  }
5585
-
5586
- // Generate file ID
5587
5803
  this._id = PDFSecurity.generateFileID(this.info);
5588
-
5589
- // Initialize security settings
5590
5804
  this._security = PDFSecurity.create(this, options);
5591
-
5592
- // Write the header
5593
- // PDF version
5594
5805
  this._write(`%PDF-${this.version}`);
5595
-
5596
- // 4 binary chars, as recommended by the spec
5597
5806
  this._write('%\xFF\xFF\xFF\xFF');
5598
-
5599
- // Add the first page
5600
5807
  if (this.options.autoFirstPage !== false) {
5601
5808
  this.addPage();
5602
5809
  }
@@ -5607,27 +5814,16 @@ class PDFDocument extends stream.Readable {
5607
5814
  options
5608
5815
  } = this);
5609
5816
  }
5610
-
5611
- // end the current page if needed
5612
5817
  if (!this.options.bufferPages) {
5613
5818
  this.flushPages();
5614
5819
  }
5615
-
5616
- // create a page object
5617
5820
  this.page = new PDFPage(this, options);
5618
5821
  this._pageBuffer.push(this.page);
5619
-
5620
- // add the page to the object store
5621
5822
  const pages = this._root.data.Pages.data;
5622
5823
  pages.Kids.push(this.page.dictionary);
5623
5824
  pages.Count++;
5624
-
5625
- // reset x and y coordinates
5626
5825
  this.x = this.page.margins.left;
5627
5826
  this.y = this.page.margins.top;
5628
-
5629
- // flip PDF coordinate system so that the origin is in
5630
- // the top left rather than the bottom left
5631
5827
  this._ctm = [1, 0, 0, 1, 0, 0];
5632
5828
  this.transform(1, 0, 0, -1, 0, this.page.height);
5633
5829
  this.emit('pageAdded');
@@ -5635,7 +5831,7 @@ class PDFDocument extends stream.Readable {
5635
5831
  }
5636
5832
  continueOnNewPage(options) {
5637
5833
  const pageMarkings = this.endPageMarkings(this.page);
5638
- this.addPage(options);
5834
+ this.addPage(options ?? this.page._options);
5639
5835
  this.initPageMarkings(pageMarkings);
5640
5836
  return this;
5641
5837
  }
@@ -5653,8 +5849,6 @@ class PDFDocument extends stream.Readable {
5653
5849
  return this.page = page;
5654
5850
  }
5655
5851
  flushPages() {
5656
- // this local variable exists so we're future-proof against
5657
- // reentrant calls to flushPages.
5658
5852
  const pages = this._pageBuffer;
5659
5853
  this._pageBuffer = [];
5660
5854
  this._pageBufferStart += pages.length;
@@ -5675,13 +5869,10 @@ class PDFDocument extends stream.Readable {
5675
5869
  }
5676
5870
  addNamedEmbeddedFile(name, ref) {
5677
5871
  if (!this._root.data.Names.data.EmbeddedFiles) {
5678
- // disabling /Limits for this tree fixes attachments not showing in Adobe Reader
5679
5872
  this._root.data.Names.data.EmbeddedFiles = new PDFNameTree({
5680
5873
  limits: false
5681
5874
  });
5682
5875
  }
5683
-
5684
- // add filespec to EmbeddedFiles
5685
5876
  this._root.data.Names.data.EmbeddedFiles.add(name, ref);
5686
5877
  }
5687
5878
  addNamedJavaScript(name, js) {
@@ -5696,19 +5887,17 @@ class PDFDocument extends stream.Readable {
5696
5887
  }
5697
5888
  ref(data) {
5698
5889
  const ref = new PDFReference(this, this._offsets.length + 1, data);
5699
- this._offsets.push(null); // placeholder for this object's offset once it is finalized
5890
+ this._offsets.push(null);
5700
5891
  this._waiting++;
5701
5892
  return ref;
5702
5893
  }
5703
5894
  _read() {}
5704
- // do nothing, but this method is required by node
5705
-
5706
5895
  _write(data) {
5707
5896
  if (!Buffer.isBuffer(data)) {
5708
5897
  data = Buffer.from(data + '\n', 'binary');
5709
5898
  }
5710
5899
  this.push(data);
5711
- return this._offset += data.length;
5900
+ this._offset += data.length;
5712
5901
  }
5713
5902
  addContent(data) {
5714
5903
  this.page.write(data);
@@ -5718,7 +5907,7 @@ class PDFDocument extends stream.Readable {
5718
5907
  this._offsets[ref.id - 1] = ref.offset;
5719
5908
  if (--this._waiting === 0 && this._ended) {
5720
5909
  this._finalize();
5721
- return this._ended = false;
5910
+ this._ended = false;
5722
5911
  }
5723
5912
  }
5724
5913
  end() {
@@ -5755,13 +5944,12 @@ class PDFDocument extends stream.Readable {
5755
5944
  this._security.end();
5756
5945
  }
5757
5946
  if (this._waiting === 0) {
5758
- return this._finalize();
5947
+ this._finalize();
5759
5948
  } else {
5760
- return this._ended = true;
5949
+ this._ended = true;
5761
5950
  }
5762
5951
  }
5763
5952
  _finalize() {
5764
- // generate xref
5765
5953
  const xRefOffset = this._offset;
5766
5954
  this._write('xref');
5767
5955
  this._write(`0 ${this._offsets.length + 1}`);
@@ -5770,8 +5958,6 @@ class PDFDocument extends stream.Readable {
5770
5958
  offset = `0000000000${offset}`.slice(-10);
5771
5959
  this._write(offset + ' 00000 n ');
5772
5960
  }
5773
-
5774
- // trailer
5775
5961
  const trailer = {
5776
5962
  Size: this._offsets.length + 1,
5777
5963
  Root: this._root,
@@ -5786,9 +5972,7 @@ class PDFDocument extends stream.Readable {
5786
5972
  this._write('startxref');
5787
5973
  this._write(`${xRefOffset}`);
5788
5974
  this._write('%%EOF');
5789
-
5790
- // end the stream
5791
- return this.push(null);
5975
+ this.push(null);
5792
5976
  }
5793
5977
  toString() {
5794
5978
  return '[object PDFDocument]';
@@ -5809,6 +5993,7 @@ mixin(MarkingsMixin);
5809
5993
  mixin(AcroFormMixin);
5810
5994
  mixin(AttachmentsMixin);
5811
5995
  mixin(SubsetMixin);
5996
+ mixin(TableMixin);
5812
5997
  PDFDocument.LineWrapper = LineWrapper;
5813
5998
 
5814
5999
  module.exports = PDFDocument;