pdfkit 0.16.0 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,79 @@ 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
+ const fArray = new Float32Array(1);
227
+ const uArray = new Uint32Array(fArray.buffer);
228
+ function PDFNumber(n) {
229
+ const rounded = Math.fround(n);
230
+ if (rounded <= n) return rounded;
231
+ fArray[0] = n;
232
+ if (n <= 0) {
233
+ uArray[0] += 1;
234
+ } else {
235
+ uArray[0] -= 1;
236
+ }
237
+ return fArray[0];
238
+ }
239
+ function normalizeSides(sides, defaultDefinition = undefined, transformer = v => v) {
240
+ if (sides == null || typeof sides === 'object' && Object.keys(sides).length === 0) {
241
+ sides = defaultDefinition;
242
+ }
243
+ if (sides == null || typeof sides !== 'object') {
244
+ sides = {
245
+ top: sides,
246
+ right: sides,
247
+ bottom: sides,
248
+ left: sides
249
+ };
250
+ } else if (Array.isArray(sides)) {
251
+ if (sides.length === 2) {
252
+ sides = {
253
+ vertical: sides[0],
254
+ horizontal: sides[1]
255
+ };
256
+ } else {
257
+ sides = {
258
+ top: sides[0],
259
+ right: sides[1],
260
+ bottom: sides[2],
261
+ left: sides[3]
262
+ };
263
+ }
264
+ }
265
+ if ('vertical' in sides || 'horizontal' in sides) {
266
+ sides = {
267
+ top: sides.vertical,
268
+ right: sides.horizontal,
269
+ bottom: sides.vertical,
270
+ left: sides.horizontal
271
+ };
272
+ }
273
+ return {
274
+ top: transformer(sides.top),
275
+ right: transformer(sides.right),
276
+ bottom: transformer(sides.bottom),
277
+ left: transformer(sides.left)
278
+ };
279
+ }
280
+ const MM_TO_CM = 1 / 10;
281
+ const CM_TO_IN = 1 / 2.54;
282
+ const PX_TO_IN = 1 / 96;
283
+ const IN_TO_PT = 72;
284
+ const PC_TO_PT = 12;
285
+ function cosine(a) {
286
+ if (a === 0) return 1;
287
+ if (a === 90) return 0;
288
+ if (a === 180) return -1;
289
+ if (a === 270) return 0;
290
+ return Math.cos(a * Math.PI / 180);
291
+ }
292
+ function sine(a) {
293
+ if (a === 0) return 0;
294
+ if (a === 90) return 1;
295
+ if (a === 180) return 0;
296
+ if (a === 270) return -1;
297
+ return Math.sin(a * Math.PI / 180);
298
+ }
267
299
 
268
300
  const DEFAULT_MARGINS = {
269
301
  top: 72,
@@ -326,35 +358,19 @@ const SIZES = {
326
358
  class PDFPage {
327
359
  constructor(document, options = {}) {
328
360
  this.document = document;
361
+ this._options = options;
329
362
  this.size = options.size || 'letter';
330
363
  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
364
  const dimensions = Array.isArray(this.size) ? this.size : SIZES[this.size.toUpperCase()];
348
365
  this.width = dimensions[this.layout === 'portrait' ? 0 : 1];
349
366
  this.height = dimensions[this.layout === 'portrait' ? 1 : 0];
350
367
  this.content = this.document.ref();
351
-
352
- // Initialize the Font, XObject, and ExtGState dictionaries
368
+ if (options.font) document.font(options.font, options.fontFamily);
369
+ if (options.fontSize) document.fontSize(options.fontSize);
370
+ this.margins = normalizeSides(options.margin ?? options.margins, DEFAULT_MARGINS, x => document.sizeToPoint(x, 0, this));
353
371
  this.resources = this.document.ref({
354
372
  ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI']
355
373
  });
356
-
357
- // The page dictionary
358
374
  this.dictionary = this.document.ref({
359
375
  Type: 'Page',
360
376
  Parent: this.document._root.data.Pages,
@@ -364,8 +380,6 @@ class PDFPage {
364
380
  });
365
381
  this.markings = [];
366
382
  }
367
-
368
- // Lazily create these objects
369
383
  get fonts() {
370
384
  const data = this.resources.data;
371
385
  return data.Font != null ? data.Font : data.Font = {};
@@ -394,14 +408,18 @@ class PDFPage {
394
408
  const data = this.dictionary.data;
395
409
  return data.StructParents != null ? data.StructParents : data.StructParents = this.document.createStructParentTreeNextKey();
396
410
  }
411
+ get contentWidth() {
412
+ return this.width - this.margins.left - this.margins.right;
413
+ }
414
+ get contentHeight() {
415
+ return this.height - this.margins.top - this.margins.bottom;
416
+ }
397
417
  maxY() {
398
418
  return this.height - this.margins.bottom;
399
419
  }
400
420
  write(chunk) {
401
421
  return this.content.write(chunk);
402
422
  }
403
-
404
- // Set tab order if document is tagged for accessibility.
405
423
  _setTabOrder() {
406
424
  if (!this.dictionary.Tabs && this.document.hasMarkInfoDictionary()) {
407
425
  this.dictionary.data.Tabs = 'S';
@@ -419,191 +437,57 @@ class PDFPage {
419
437
  }
420
438
  }
421
439
 
422
- /*
423
- PDFNameTree - represents a name tree object
424
- */
425
440
  class PDFNameTree extends PDFTree {
426
441
  _compareKeys(a, b) {
427
442
  return a.localeCompare(b);
428
443
  }
429
444
  _keysName() {
430
- return "Names";
445
+ return 'Names';
431
446
  }
432
447
  _dataForKey(k) {
433
448
  return new String(k);
434
449
  }
435
450
  }
436
451
 
437
- /**
438
- * Check if value is in a range group.
439
- * @param {number} value
440
- * @param {number[]} rangeGroup
441
- * @returns {boolean}
442
- */
443
452
  function inRange(value, rangeGroup) {
444
453
  if (value < rangeGroup[0]) return false;
445
454
  let startRange = 0;
446
455
  let endRange = rangeGroup.length / 2;
447
456
  while (startRange <= endRange) {
448
457
  const middleRange = Math.floor((startRange + endRange) / 2);
449
-
450
- // actual array index
451
458
  const arrayIndex = middleRange * 2;
452
-
453
- // Check if value is in range pointed by actual index
454
459
  if (value >= rangeGroup[arrayIndex] && value <= rangeGroup[arrayIndex + 1]) {
455
460
  return true;
456
461
  }
457
462
  if (value > rangeGroup[arrayIndex + 1]) {
458
- // Search Right Side Of Array
459
463
  startRange = middleRange + 1;
460
464
  } else {
461
- // Search Left Side Of Array
462
465
  endRange = middleRange - 1;
463
466
  }
464
467
  }
465
468
  return false;
466
469
  }
467
470
 
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
471
  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
472
  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
473
  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
474
  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
-
475
+ 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
476
  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
-
477
+ 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];
478
+ 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];
479
+ 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
480
  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
481
  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
482
  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
483
  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
484
  const isBidirectionalL = character => inRange(character, bidirectional_l);
580
485
 
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
486
  const mapping2space = isNonASCIISpaceCharacter;
588
-
589
- /**
590
- * the "commonly mapped to nothing" characters [StringPrep, B.1]
591
- * that can be mapped to nothing.
592
- */
593
487
  const mapping2nothing = isCommonlyMappedToNothing;
594
-
595
- // utils
596
488
  const getCodePoint = character => character.codePointAt(0);
597
489
  const first = x => x[0];
598
490
  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
491
  function toCodePoints(input) {
608
492
  const codepoints = [];
609
493
  const size = input.length;
@@ -621,14 +505,6 @@ function toCodePoints(input) {
621
505
  }
622
506
  return codepoints;
623
507
  }
624
-
625
- /**
626
- * SASLprep.
627
- * @param {string} input
628
- * @param {Object} opts
629
- * @param {boolean} opts.allowUnassigned
630
- * @returns {string}
631
- */
632
508
  function saslprep(input, opts = {}) {
633
509
  if (typeof input !== 'string') {
634
510
  throw new TypeError('Expected string.');
@@ -636,49 +512,24 @@ function saslprep(input, opts = {}) {
636
512
  if (input.length === 0) {
637
513
  return '';
638
514
  }
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
515
+ const mapped_input = toCodePoints(input).map(character => mapping2space(character) ? 0x20 : character).filter(character => !mapping2nothing(character));
648
516
  const normalized_input = String.fromCodePoint.apply(null, mapped_input).normalize('NFKC');
649
517
  const normalized_map = toCodePoints(normalized_input);
650
-
651
- // 3. Prohibit
652
518
  const hasProhibited = normalized_map.some(isProhibitedCharacter);
653
519
  if (hasProhibited) {
654
520
  throw new Error('Prohibited character, see https://tools.ietf.org/html/rfc4013#section-2.3');
655
521
  }
656
-
657
- // Unassigned Code Points
658
522
  if (opts.allowUnassigned !== true) {
659
523
  const hasUnassigned = normalized_map.some(isUnassignedCodePoint);
660
524
  if (hasUnassigned) {
661
525
  throw new Error('Unassigned code point, see https://tools.ietf.org/html/rfc4013#section-2.5');
662
526
  }
663
527
  }
664
-
665
- // 4. check bidi
666
-
667
528
  const hasBidiRAL = normalized_map.some(isBidirectionalRAL);
668
529
  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
530
  if (hasBidiRAL && hasBidiL) {
673
531
  throw new Error('String must not contain RandALCat and LCat at the same time,' + ' see https://tools.ietf.org/html/rfc3454#section-6');
674
532
  }
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
533
  const isFirstBidiRAL = isBidirectionalRAL(getCodePoint(first(normalized_input)));
683
534
  const isLastBidiRAL = isBidirectionalRAL(getCodePoint(last(normalized_input)));
684
535
  if (hasBidiRAL && !(isFirstBidiRAL && isLastBidiRAL)) {
@@ -687,15 +538,10 @@ function saslprep(input, opts = {}) {
687
538
  return normalized_input;
688
539
  }
689
540
 
690
- /*
691
- PDFSecurity - represents PDF security settings
692
- By Yang Liu <hi@zesik.com>
693
- */
694
541
  class PDFSecurity {
695
542
  static generateFileID(info = {}) {
696
543
  let infoStr = `${info.CreationDate.getTime()}\n`;
697
544
  for (let key in info) {
698
- // eslint-disable-next-line no-prototype-builtins
699
545
  if (!info.hasOwnProperty(key)) {
700
546
  continue;
701
547
  }
@@ -1069,8 +915,6 @@ class PDFGradient$1 {
1069
915
  }
1070
916
  this.embedded = true;
1071
917
  this.matrix = m;
1072
-
1073
- // if the last stop comes before 100%, add a copy at 100%
1074
918
  const last = this.stops[stopsLength - 1];
1075
919
  if (last[0] < 1) {
1076
920
  this.stops.push([1, last[1], last[2]]);
@@ -1093,14 +937,11 @@ class PDFGradient$1 {
1093
937
  stops.push(fn);
1094
938
  fn.end();
1095
939
  }
1096
-
1097
- // if there are only two stops, we don't need a stitching function
1098
940
  if (stopsLength === 1) {
1099
941
  fn = stops[0];
1100
942
  } else {
1101
943
  fn = this.doc.ref({
1102
944
  FunctionType: 3,
1103
- // stitching function
1104
945
  Domain: [0, 1],
1105
946
  Functions: stops,
1106
947
  Bounds: bounds,
@@ -1181,7 +1022,6 @@ class PDFGradient$1 {
1181
1022
  return pattern;
1182
1023
  }
1183
1024
  apply(stroke) {
1184
- // apply gradient transform to existing document ctm
1185
1025
  const [m0, m1, m2, m3, m4, m5] = this.doc._ctm;
1186
1026
  const [m11, m12, m21, m22, dx, dy] = this.transform;
1187
1027
  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 +1084,6 @@ var Gradient = {
1244
1084
  PDFRadialGradient: PDFRadialGradient$1
1245
1085
  };
1246
1086
 
1247
- /*
1248
- PDF tiling pattern support. Uncolored only.
1249
- */
1250
-
1251
1087
  const underlyingColorSpaces = ['DeviceCMYK', 'DeviceRGB'];
1252
1088
  class PDFTilingPattern$1 {
1253
1089
  constructor(doc, bBox, xStep, yStep, stream) {
@@ -1258,23 +1094,16 @@ class PDFTilingPattern$1 {
1258
1094
  this.stream = stream;
1259
1095
  }
1260
1096
  createPattern() {
1261
- // no resources needed for our current usage
1262
- // required entry
1263
1097
  const resources = this.doc.ref();
1264
1098
  resources.end();
1265
- // apply default transform matrix (flipped in the default doc._ctm)
1266
- // see document.js & gradient.js
1267
1099
  const [m0, m1, m2, m3, m4, m5] = this.doc._ctm;
1268
1100
  const [m11, m12, m21, m22, dx, dy] = [1, 0, 0, 1, 0, 0];
1269
1101
  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
1102
  const pattern = this.doc.ref({
1271
1103
  Type: 'Pattern',
1272
1104
  PatternType: 1,
1273
- // tiling
1274
1105
  PaintType: 2,
1275
- // 1-colored, 2-uncolored
1276
1106
  TilingType: 2,
1277
- // 2-no distortion
1278
1107
  BBox: this.bBox,
1279
1108
  XStep: this.xStep,
1280
1109
  YStep: this.yStep,
@@ -1285,8 +1114,6 @@ class PDFTilingPattern$1 {
1285
1114
  return pattern;
1286
1115
  }
1287
1116
  embedPatternColorSpaces() {
1288
- // map each pattern to an underlying color space
1289
- // and embed on each page
1290
1117
  underlyingColorSpaces.forEach(csName => {
1291
1118
  const csId = this.getPatternColorSpaceId(csName);
1292
1119
  if (this.doc.page.colorSpaces[csId]) return;
@@ -1304,24 +1131,17 @@ class PDFTilingPattern$1 {
1304
1131
  this.id = 'P' + this.doc._patternCount;
1305
1132
  this.pattern = this.createPattern();
1306
1133
  }
1307
-
1308
- // patterns are embedded in each page
1309
1134
  if (!this.doc.page.patterns[this.id]) {
1310
1135
  this.doc.page.patterns[this.id] = this.pattern;
1311
1136
  }
1312
1137
  }
1313
1138
  apply(stroke, patternColor) {
1314
- // do any embedding/creating that might be needed
1315
1139
  this.embedPatternColorSpaces();
1316
1140
  this.embed();
1317
1141
  const normalizedColor = this.doc._normalizeColor(patternColor);
1318
1142
  if (!normalizedColor) throw Error(`invalid pattern color. (value: ${patternColor})`);
1319
-
1320
- // select one of the pattern color spaces
1321
1143
  const csId = this.getPatternColorSpaceId(this.doc._getColorSpace(normalizedColor));
1322
1144
  this.doc._setColorSpace(csId, stroke);
1323
-
1324
- // stroke/fill using the pattern and color (in the above underlying color space)
1325
1145
  const op = stroke ? 'SCN' : 'scn';
1326
1146
  return this.doc.addContent(`${normalizedColor.join(' ')} /${this.id} ${op}`);
1327
1147
  }
@@ -1341,11 +1161,10 @@ const {
1341
1161
  var ColorMixin = {
1342
1162
  initColor() {
1343
1163
  this.spotColors = {};
1344
- // The opacity dictionaries
1345
1164
  this._opacityRegistry = {};
1346
1165
  this._opacityCount = 0;
1347
1166
  this._patternCount = 0;
1348
- return this._gradCount = 0;
1167
+ this._gradCount = 0;
1349
1168
  },
1350
1169
  _normalizeColor(color) {
1351
1170
  if (typeof color === 'string') {
@@ -1362,10 +1181,8 @@ var ColorMixin = {
1362
1181
  }
1363
1182
  }
1364
1183
  if (Array.isArray(color)) {
1365
- // RGB
1366
1184
  if (color.length === 3) {
1367
1185
  color = color.map(part => part / 255);
1368
- // CMYK
1369
1186
  } else if (color.length === 4) {
1370
1187
  color = color.map(part => part / 100);
1371
1188
  }
@@ -1377,12 +1194,10 @@ var ColorMixin = {
1377
1194
  if (color instanceof PDFGradient) {
1378
1195
  color.apply(stroke);
1379
1196
  return true;
1380
- // see if tiling pattern, decode & apply it it
1381
1197
  } else if (Array.isArray(color) && color[0] instanceof PDFTilingPattern) {
1382
1198
  color[0].apply(stroke, color[1]);
1383
1199
  return true;
1384
1200
  }
1385
- // any other case should be a normal color and not a pattern
1386
1201
  return this._setColorCore(color, stroke);
1387
1202
  },
1388
1203
  _setColorCore(color, stroke) {
@@ -1416,9 +1231,6 @@ var ColorMixin = {
1416
1231
  if (set) {
1417
1232
  this.fillOpacity(opacity);
1418
1233
  }
1419
-
1420
- // save this for text wrapper, which needs to reset
1421
- // the fill color on new pages
1422
1234
  this._fillColor = [color, opacity];
1423
1235
  return this;
1424
1236
  },
@@ -1674,7 +1486,6 @@ const parse = function (path) {
1674
1486
  if (parameters[c] != null) {
1675
1487
  params = parameters[c];
1676
1488
  if (cmd) {
1677
- // save existing command
1678
1489
  if (curArg.length > 0) {
1679
1490
  args[args.length] = +curArg;
1680
1491
  }
@@ -1692,14 +1503,11 @@ const parse = function (path) {
1692
1503
  continue;
1693
1504
  }
1694
1505
  if (args.length === params) {
1695
- // handle reused commands
1696
1506
  ret[ret.length] = {
1697
1507
  cmd,
1698
1508
  args
1699
1509
  };
1700
1510
  args = [+curArg];
1701
-
1702
- // handle assumed commands
1703
1511
  if (cmd === 'M') {
1704
1512
  cmd = 'L';
1705
1513
  }
@@ -1710,8 +1518,6 @@ const parse = function (path) {
1710
1518
  args[args.length] = +curArg;
1711
1519
  }
1712
1520
  foundDecimal = c === '.';
1713
-
1714
- // fix for negative numbers or repeated decimals with no delimeter between commands
1715
1521
  curArg = ['-', '.'].includes(c) ? c : '';
1716
1522
  } else {
1717
1523
  curArg += c;
@@ -1720,18 +1526,13 @@ const parse = function (path) {
1720
1526
  }
1721
1527
  }
1722
1528
  }
1723
-
1724
- // add the last command
1725
1529
  if (curArg.length > 0) {
1726
1530
  if (args.length === params) {
1727
- // handle reused commands
1728
1531
  ret[ret.length] = {
1729
1532
  cmd,
1730
1533
  args
1731
1534
  };
1732
1535
  args = [+curArg];
1733
-
1734
- // handle assumed commands
1735
1536
  if (cmd === 'M') {
1736
1537
  cmd = 'L';
1737
1538
  }
@@ -1749,10 +1550,7 @@ const parse = function (path) {
1749
1550
  return ret;
1750
1551
  };
1751
1552
  const apply = function (commands, doc) {
1752
- // current point, control point, and subpath starting point
1753
1553
  cx = cy = px = py = sx = sy = 0;
1754
-
1755
- // run the commands
1756
1554
  for (let i = 0; i < commands.length; i++) {
1757
1555
  const c = commands[i];
1758
1556
  if (typeof runners[c.cmd] === 'function') {
@@ -1916,8 +1714,6 @@ const solveArc = function (doc, x, y, coords) {
1916
1714
  doc.bezierCurveTo(...bez);
1917
1715
  }
1918
1716
  };
1919
-
1920
- // from Inkscape svgtopdf, thanks!
1921
1717
  const arcToSegments = function (x, y, rx, ry, large, sweep, rotateX, ox, oy) {
1922
1718
  const th = rotateX * (Math.PI / 180);
1923
1719
  const sin_th = Math.sin(th);
@@ -1993,18 +1789,14 @@ class SVGPath {
1993
1789
  const {
1994
1790
  number: number$1
1995
1791
  } = PDFObject;
1996
-
1997
- // This constant is used to approximate a symmetrical arc using a cubic
1998
- // Bezier curve.
1999
1792
  const KAPPA = 4.0 * ((Math.sqrt(2) - 1.0) / 3.0);
2000
1793
  var VectorMixin = {
2001
1794
  initVector() {
2002
- this._ctm = [1, 0, 0, 1, 0, 0]; // current transformation matrix
2003
- return this._ctmStack = [];
1795
+ this._ctm = [1, 0, 0, 1, 0, 0];
1796
+ this._ctmStack = [];
2004
1797
  },
2005
1798
  save() {
2006
1799
  this._ctmStack.push(this._ctm.slice());
2007
- // TODO: save/restore colorspace and styles so not setting it unnessesarily all the time?
2008
1800
  return this.addContent('q');
2009
1801
  },
2010
1802
  restore() {
@@ -2077,8 +1869,6 @@ var VectorMixin = {
2077
1869
  r = 0;
2078
1870
  }
2079
1871
  r = Math.min(r, 0.5 * w, 0.5 * h);
2080
-
2081
- // amount to inset control points from corners (see `ellipse`)
2082
1872
  const c = r * (1.0 - KAPPA);
2083
1873
  this.moveTo(x + r, y);
2084
1874
  this.lineTo(x + w - r, y);
@@ -2092,7 +1882,6 @@ var VectorMixin = {
2092
1882
  return this.closePath();
2093
1883
  },
2094
1884
  ellipse(x, y, r1, r2) {
2095
- // based on http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas/2173084#2173084
2096
1885
  if (r2 == null) {
2097
1886
  r2 = r1;
2098
1887
  }
@@ -2122,10 +1911,8 @@ var VectorMixin = {
2122
1911
  const HALF_PI = 0.5 * Math.PI;
2123
1912
  let deltaAng = endAngle - startAngle;
2124
1913
  if (Math.abs(deltaAng) > TWO_PI) {
2125
- // draw only full circle if more than that is specified
2126
1914
  deltaAng = TWO_PI;
2127
1915
  } else if (deltaAng !== 0 && anticlockwise !== deltaAng < 0) {
2128
- // necessary to flip direction of rendering
2129
1916
  const dir = anticlockwise ? -1 : 1;
2130
1917
  deltaAng = dir * TWO_PI + deltaAng;
2131
1918
  }
@@ -2133,38 +1920,21 @@ var VectorMixin = {
2133
1920
  const segAng = deltaAng / numSegs;
2134
1921
  const handleLen = segAng / HALF_PI * KAPPA * radius;
2135
1922
  let curAng = startAngle;
2136
-
2137
- // component distances between anchor point and control point
2138
1923
  let deltaCx = -Math.sin(curAng) * handleLen;
2139
1924
  let deltaCy = Math.cos(curAng) * handleLen;
2140
-
2141
- // anchor point
2142
1925
  let ax = x + Math.cos(curAng) * radius;
2143
1926
  let ay = y + Math.sin(curAng) * radius;
2144
-
2145
- // calculate and render segments
2146
1927
  this.moveTo(ax, ay);
2147
1928
  for (let segIdx = 0; segIdx < numSegs; segIdx++) {
2148
- // starting control point
2149
1929
  const cp1x = ax + deltaCx;
2150
1930
  const cp1y = ay + deltaCy;
2151
-
2152
- // step angle
2153
1931
  curAng += segAng;
2154
-
2155
- // next anchor point
2156
1932
  ax = x + Math.cos(curAng) * radius;
2157
1933
  ay = y + Math.sin(curAng) * radius;
2158
-
2159
- // next control point delta
2160
1934
  deltaCx = -Math.sin(curAng) * handleLen;
2161
1935
  deltaCy = Math.cos(curAng) * handleLen;
2162
-
2163
- // ending control point
2164
1936
  const cp2x = ax - deltaCx;
2165
1937
  const cp2y = ay - deltaCy;
2166
-
2167
- // render segment
2168
1938
  this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, ax, ay);
2169
1939
  }
2170
1940
  return this;
@@ -2225,9 +1995,7 @@ var VectorMixin = {
2225
1995
  return this.addContent(`W${this._windingRule(rule)} n`);
2226
1996
  },
2227
1997
  transform(m11, m12, m21, m22, dx, dy) {
2228
- // keep track of the current transformation matrix
2229
1998
  if (m11 === 1 && m12 === 0 && m21 === 0 && m22 === 1 && dx === 0 && dy === 0) {
2230
- // Ignore identity transforms
2231
1999
  return this;
2232
2000
  }
2233
2001
  const m = this._ctm;
@@ -2381,21 +2149,12 @@ oslash ugrave uacute ucircumflex
2381
2149
  udieresis yacute thorn ydieresis\
2382
2150
  `.split(/\s+/);
2383
2151
  class AFMFont {
2384
- static open(filename) {
2385
- return new AFMFont(fs.readFileSync(filename, 'utf8'));
2386
- }
2387
2152
  constructor(contents) {
2388
- this.contents = contents;
2389
2153
  this.attributes = {};
2390
2154
  this.glyphWidths = {};
2391
2155
  this.boundingBoxes = {};
2392
2156
  this.kernPairs = {};
2393
- this.parse();
2394
- // todo: remove charWidths since appears to not be used
2395
- this.charWidths = new Array(256);
2396
- for (let char = 0; char <= 255; char++) {
2397
- this.charWidths[char] = this.glyphWidths[characters[char]];
2398
- }
2157
+ this.parse(contents);
2399
2158
  this.bbox = this.attributes['FontBBox'].split(/\s+/).map(e => +e);
2400
2159
  this.ascender = +(this.attributes['Ascender'] || 0);
2401
2160
  this.descender = +(this.attributes['Descender'] || 0);
@@ -2403,9 +2162,9 @@ class AFMFont {
2403
2162
  this.capHeight = +(this.attributes['CapHeight'] || 0);
2404
2163
  this.lineGap = this.bbox[3] - this.bbox[1] - (this.ascender - this.descender);
2405
2164
  }
2406
- parse() {
2165
+ parse(contents) {
2407
2166
  let section = '';
2408
- for (let line of this.contents.split('\n')) {
2167
+ for (let line of contents.split('\n')) {
2409
2168
  var match;
2410
2169
  var a;
2411
2170
  if (match = line.match(/^Start(\w+)/)) {
@@ -2498,21 +2257,17 @@ class PDFFont {
2498
2257
  return;
2499
2258
  }
2500
2259
  this.embed();
2501
- return this.embedded = true;
2260
+ this.embedded = true;
2502
2261
  }
2503
2262
  embed() {
2504
2263
  throw new Error('Must be implemented by subclasses');
2505
2264
  }
2506
- lineHeight(size, includeGap) {
2507
- if (includeGap == null) {
2508
- includeGap = false;
2509
- }
2265
+ lineHeight(size, includeGap = false) {
2510
2266
  const gap = includeGap ? this.lineGap : 0;
2511
2267
  return (this.ascender + gap - this.descender) / 1000 * size;
2512
2268
  }
2513
2269
  }
2514
2270
 
2515
- // This insanity is so bundlers can inline the font files
2516
2271
  const STANDARD_FONTS = {
2517
2272
  Courier() {
2518
2273
  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 +2395,6 @@ class EmbeddedFont extends PDFFont {
2640
2395
  }
2641
2396
  layoutRun(text, features) {
2642
2397
  const run = this.font.layout(text, features);
2643
-
2644
- // Normalize position values
2645
2398
  for (let i = 0; i < run.positions.length; i++) {
2646
2399
  const position = run.positions[i];
2647
2400
  for (let key in position) {
@@ -2664,16 +2417,12 @@ class EmbeddedFont extends PDFFont {
2664
2417
  return run;
2665
2418
  }
2666
2419
  layout(text, features, onlyWidth) {
2667
- // Skip the cache if any user defined features are applied
2668
2420
  if (features) {
2669
2421
  return this.layoutRun(text, features);
2670
2422
  }
2671
2423
  let glyphs = onlyWidth ? null : [];
2672
2424
  let positions = onlyWidth ? null : [];
2673
2425
  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
2426
  let last = 0;
2678
2427
  let index = 0;
2679
2428
  while (index <= text.length) {
@@ -2735,17 +2484,15 @@ class EmbeddedFont extends PDFFont {
2735
2484
  if (1 <= familyClass && familyClass <= 7) {
2736
2485
  flags |= 1 << 1;
2737
2486
  }
2738
- flags |= 1 << 2; // assume the font uses non-latin characters
2487
+ flags |= 1 << 2;
2739
2488
  if (familyClass === 10) {
2740
2489
  flags |= 1 << 3;
2741
2490
  }
2742
2491
  if (this.font.head.macStyle.italic) {
2743
2492
  flags |= 1 << 6;
2744
2493
  }
2745
-
2746
- // generate a tag (6 uppercase letters. 17 is the char code offset from '0' to 'A'. 73 will map to 'Z')
2747
2494
  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(' ', '_');
2495
+ const name = tag + '+' + this.font.postscriptName?.replaceAll(' ', '_');
2749
2496
  const {
2750
2497
  bbox
2751
2498
  } = this.font;
@@ -2760,8 +2507,7 @@ class EmbeddedFont extends PDFFont {
2760
2507
  CapHeight: (this.font.capHeight || this.font.ascent) * this.scale,
2761
2508
  XHeight: (this.font.xHeight || 0) * this.scale,
2762
2509
  StemV: 0
2763
- }); // not sure how to calculate this
2764
-
2510
+ });
2765
2511
  if (isCFF) {
2766
2512
  descriptor.data.FontFile3 = fontFile;
2767
2513
  } else {
@@ -2803,17 +2549,11 @@ class EmbeddedFont extends PDFFont {
2803
2549
  };
2804
2550
  return this.dictionary.end();
2805
2551
  }
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
2552
  toUnicodeCmap() {
2811
2553
  const cmap = this.document.ref();
2812
2554
  const entries = [];
2813
2555
  for (let codePoints of this.unicode) {
2814
2556
  const encoded = [];
2815
-
2816
- // encode codePoints to utf16
2817
2557
  for (let value of codePoints) {
2818
2558
  if (value > 0xffff) {
2819
2559
  value -= 0x10000;
@@ -2880,31 +2620,26 @@ class PDFFontFactory {
2880
2620
  }
2881
2621
 
2882
2622
  const isEqualFont = (font1, font2) => {
2883
- // compare font checksum
2884
2623
  if (font1.font._tables?.head?.checkSumAdjustment !== font2.font._tables?.head?.checkSumAdjustment) {
2885
2624
  return false;
2886
2625
  }
2887
-
2888
- // compare font name table
2889
2626
  if (JSON.stringify(font1.font._tables?.name?.records) !== JSON.stringify(font2.font._tables?.name?.records)) {
2890
2627
  return false;
2891
2628
  }
2892
2629
  return true;
2893
2630
  };
2894
2631
  var FontsMixin = {
2895
- initFonts(defaultFont = 'Helvetica') {
2896
- // Lookup table for embedded fonts
2632
+ initFonts(defaultFont = 'Helvetica', defaultFontFamily = null, defaultFontSize = 12) {
2897
2633
  this._fontFamilies = {};
2898
2634
  this._fontCount = 0;
2899
-
2900
- // Font state
2901
- this._fontSize = 12;
2635
+ this._fontSource = defaultFont;
2636
+ this._fontFamily = defaultFontFamily;
2637
+ this._fontSize = defaultFontSize;
2902
2638
  this._font = null;
2639
+ this._remSize = defaultFontSize;
2903
2640
  this._registeredFonts = {};
2904
-
2905
- // Set the default font
2906
2641
  if (defaultFont) {
2907
- this.font(defaultFont);
2642
+ this.font(defaultFont, defaultFontFamily);
2908
2643
  }
2909
2644
  },
2910
2645
  font(src, family, size) {
@@ -2913,8 +2648,6 @@ var FontsMixin = {
2913
2648
  size = family;
2914
2649
  family = null;
2915
2650
  }
2916
-
2917
- // check registered fonts if src is a string
2918
2651
  if (typeof src === 'string' && this._registeredFonts[src]) {
2919
2652
  cacheKey = src;
2920
2653
  ({
@@ -2927,28 +2660,21 @@ var FontsMixin = {
2927
2660
  cacheKey = null;
2928
2661
  }
2929
2662
  }
2663
+ this._fontSource = src;
2664
+ this._fontFamily = family;
2930
2665
  if (size != null) {
2931
2666
  this.fontSize(size);
2932
2667
  }
2933
-
2934
- // fast path: check if the font is already in the PDF
2935
2668
  if (font = this._fontFamilies[cacheKey]) {
2936
2669
  this._font = font;
2937
2670
  return this;
2938
2671
  }
2939
-
2940
- // load the font
2941
2672
  const id = `F${++this._fontCount}`;
2942
2673
  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
2674
  if ((font = this._fontFamilies[this._font.name]) && isEqualFont(this._font, font)) {
2947
2675
  this._font = font;
2948
2676
  return this;
2949
2677
  }
2950
-
2951
- // save the font for reuse later
2952
2678
  if (cacheKey) {
2953
2679
  this._fontFamilies[cacheKey] = this._font;
2954
2680
  }
@@ -2958,13 +2684,10 @@ var FontsMixin = {
2958
2684
  return this;
2959
2685
  },
2960
2686
  fontSize(_fontSize) {
2961
- this._fontSize = _fontSize;
2687
+ this._fontSize = this.sizeToPoint(_fontSize);
2962
2688
  return this;
2963
2689
  },
2964
2690
  currentLineHeight(includeGap) {
2965
- if (includeGap == null) {
2966
- includeGap = false;
2967
- }
2968
2691
  return this._font.lineHeight(this._fontSize, includeGap);
2969
2692
  },
2970
2693
  registerFont(name, src, family) {
@@ -2973,6 +2696,64 @@ var FontsMixin = {
2973
2696
  family
2974
2697
  };
2975
2698
  return this;
2699
+ },
2700
+ sizeToPoint(size, defaultValue = 0, page = this.page, percentageWidth = undefined) {
2701
+ if (!percentageWidth) percentageWidth = this._fontSize;
2702
+ if (typeof defaultValue !== 'number') defaultValue = this.sizeToPoint(defaultValue);
2703
+ if (size === undefined) return defaultValue;
2704
+ if (typeof size === 'number') return size;
2705
+ if (typeof size === 'boolean') return Number(size);
2706
+ const match = String(size).match(/((\d+)?(\.\d+)?)(em|in|px|cm|mm|pc|ex|ch|rem|vw|vh|vmin|vmax|%|pt)?/);
2707
+ if (!match) throw new Error(`Unsupported size '${size}'`);
2708
+ let multiplier;
2709
+ switch (match[4]) {
2710
+ case 'em':
2711
+ multiplier = this._fontSize;
2712
+ break;
2713
+ case 'in':
2714
+ multiplier = IN_TO_PT;
2715
+ break;
2716
+ case 'px':
2717
+ multiplier = PX_TO_IN * IN_TO_PT;
2718
+ break;
2719
+ case 'cm':
2720
+ multiplier = CM_TO_IN * IN_TO_PT;
2721
+ break;
2722
+ case 'mm':
2723
+ multiplier = MM_TO_CM * CM_TO_IN * IN_TO_PT;
2724
+ break;
2725
+ case 'pc':
2726
+ multiplier = PC_TO_PT;
2727
+ break;
2728
+ case 'ex':
2729
+ multiplier = this.currentLineHeight();
2730
+ break;
2731
+ case 'ch':
2732
+ multiplier = this.widthOfString('0');
2733
+ break;
2734
+ case 'rem':
2735
+ multiplier = this._remSize;
2736
+ break;
2737
+ case 'vw':
2738
+ multiplier = page.width / 100;
2739
+ break;
2740
+ case 'vh':
2741
+ multiplier = page.height / 100;
2742
+ break;
2743
+ case 'vmin':
2744
+ multiplier = Math.min(page.width, page.height) / 100;
2745
+ break;
2746
+ case 'vmax':
2747
+ multiplier = Math.max(page.width, page.height) / 100;
2748
+ break;
2749
+ case '%':
2750
+ multiplier = percentageWidth / 100;
2751
+ break;
2752
+ case 'pt':
2753
+ default:
2754
+ multiplier = 1;
2755
+ }
2756
+ return multiplier * Number(match[1]);
2976
2757
  }
2977
2758
  };
2978
2759
 
@@ -2987,7 +2768,7 @@ class LineWrapper extends events.EventEmitter {
2987
2768
  this.characterSpacing = (options.characterSpacing || 0) * this.horizontalScaling / 100;
2988
2769
  this.wordSpacing = (options.wordSpacing === 0) * this.horizontalScaling / 100;
2989
2770
  this.columns = options.columns || 1;
2990
- this.columnGap = (options.columnGap != null ? options.columnGap : 18) * this.horizontalScaling / 100; // 1/4 inch
2771
+ this.columnGap = (options.columnGap != null ? options.columnGap : 18) * this.horizontalScaling / 100;
2991
2772
  this.lineWidth = (options.width * this.horizontalScaling / 100 - this.columnGap * (this.columns - 1)) / this.columns;
2992
2773
  this.spaceLeft = this.lineWidth;
2993
2774
  this.startX = this.document.x;
@@ -2996,44 +2777,30 @@ class LineWrapper extends events.EventEmitter {
2996
2777
  this.ellipsis = options.ellipsis;
2997
2778
  this.continuedX = 0;
2998
2779
  this.features = options.features;
2999
-
3000
- // calculate the maximum Y position the text can appear at
3001
2780
  if (options.height != null) {
3002
2781
  this.height = options.height;
3003
- this.maxY = this.startY + options.height;
2782
+ this.maxY = PDFNumber(this.startY + options.height);
3004
2783
  } else {
3005
- this.maxY = this.document.page.maxY();
2784
+ this.maxY = PDFNumber(this.document.page.maxY());
3006
2785
  }
3007
-
3008
- // handle paragraph indents
3009
2786
  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
2787
  const indent = this.continuedX || this.indent;
3014
2788
  this.document.x += indent;
3015
2789
  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
2790
  if (options.indentAllLines) {
3020
2791
  return;
3021
2792
  }
3022
-
3023
- // otherwise we start the next line without indent
3024
- return this.once('line', () => {
2793
+ this.once('line', () => {
3025
2794
  this.document.x -= indent;
3026
2795
  this.lineWidth += indent;
3027
2796
  if (options.continued && !this.continuedX) {
3028
2797
  this.continuedX = this.indent;
3029
2798
  }
3030
2799
  if (!options.continued) {
3031
- return this.continuedX = 0;
2800
+ this.continuedX = 0;
3032
2801
  }
3033
2802
  });
3034
2803
  });
3035
-
3036
- // handle left aligning last lines of paragraphs
3037
2804
  this.on('lastLine', options => {
3038
2805
  const {
3039
2806
  align
@@ -3042,7 +2809,7 @@ class LineWrapper extends events.EventEmitter {
3042
2809
  options.align = 'left';
3043
2810
  }
3044
2811
  this.lastLine = true;
3045
- return this.once('line', () => {
2812
+ this.once('line', () => {
3046
2813
  this.document.y += options.paragraphGap || 0;
3047
2814
  options.align = align;
3048
2815
  return this.lastLine = false;
@@ -3050,7 +2817,7 @@ class LineWrapper extends events.EventEmitter {
3050
2817
  });
3051
2818
  }
3052
2819
  wordWidth(word) {
3053
- return this.document.widthOfString(word, this) + this.characterSpacing + this.wordSpacing;
2820
+ return PDFNumber(this.document.widthOfString(word, this) + this.characterSpacing + this.wordSpacing);
3054
2821
  }
3055
2822
  canFit(word, w) {
3056
2823
  if (word[word.length - 1] != SOFT_HYPHEN) {
@@ -3059,7 +2826,6 @@ class LineWrapper extends events.EventEmitter {
3059
2826
  return w + this.wordWidth(HYPHEN) <= this.spaceLeft;
3060
2827
  }
3061
2828
  eachWord(text, fn) {
3062
- // setup a unicode line breaker
3063
2829
  let bk;
3064
2830
  const breaker = new LineBreaker(text);
3065
2831
  let last = null;
@@ -3068,19 +2834,12 @@ class LineWrapper extends events.EventEmitter {
3068
2834
  var shouldContinue;
3069
2835
  let word = text.slice((last != null ? last.position : undefined) || 0, bk.position);
3070
2836
  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
2837
  if (w > this.lineWidth + this.continuedX) {
3075
- // make some fake break objects
3076
2838
  let lbk = last;
3077
2839
  const fbk = {};
3078
2840
  while (word.length) {
3079
- // fit as much of the word as possible into the space we have
3080
2841
  var l, mightGrow;
3081
2842
  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
2843
  l = Math.ceil(this.spaceLeft / (w / word.length));
3085
2844
  w = this.wordWidth(word.slice(0, l));
3086
2845
  mightGrow = w <= this.spaceLeft && l < word.length;
@@ -3088,7 +2847,6 @@ class LineWrapper extends events.EventEmitter {
3088
2847
  l = word.length;
3089
2848
  }
3090
2849
  let mustShrink = w > this.spaceLeft && l > 0;
3091
- // shrink or grow word as necessary after our near-guess above
3092
2850
  while (mustShrink || mightGrow) {
3093
2851
  if (mustShrink) {
3094
2852
  w = this.wordWidth(word.slice(0, --l));
@@ -3099,20 +2857,14 @@ class LineWrapper extends events.EventEmitter {
3099
2857
  mightGrow = w <= this.spaceLeft && l < word.length;
3100
2858
  }
3101
2859
  }
3102
-
3103
- // check for the edge case where a single character cannot fit into a line.
3104
2860
  if (l === 0 && this.spaceLeft === this.lineWidth) {
3105
2861
  l = 1;
3106
2862
  }
3107
-
3108
- // send a required break unless this is the last piece and a linebreak is not specified
3109
2863
  fbk.required = bk.required || l < word.length;
3110
2864
  shouldContinue = fn(word.slice(0, l), w, fbk, lbk);
3111
2865
  lbk = {
3112
2866
  required: false
3113
2867
  };
3114
-
3115
- // get the remaining piece of the word
3116
2868
  word = word.slice(l);
3117
2869
  w = this.wordWidth(word);
3118
2870
  if (shouldContinue === false) {
@@ -3120,7 +2872,6 @@ class LineWrapper extends events.EventEmitter {
3120
2872
  }
3121
2873
  }
3122
2874
  } else {
3123
- // otherwise just emit the break as it was given to us
3124
2875
  shouldContinue = fn(word, w, bk, last);
3125
2876
  }
3126
2877
  if (shouldContinue === false) {
@@ -3130,7 +2881,6 @@ class LineWrapper extends events.EventEmitter {
3130
2881
  }
3131
2882
  }
3132
2883
  wrap(text, options) {
3133
- // override options from previous continued fragments
3134
2884
  this.horizontalScaling = options.horizontalScaling || 100;
3135
2885
  if (options.indent != null) {
3136
2886
  this.indent = options.indent * this.horizontalScaling / 100;
@@ -3144,10 +2894,6 @@ class LineWrapper extends events.EventEmitter {
3144
2894
  if (options.ellipsis != null) {
3145
2895
  this.ellipsis = options.ellipsis;
3146
2896
  }
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
2897
  const nextY = this.document.y + this.document.currentLineHeight(true);
3152
2898
  if (this.document.y > this.maxY || nextY > this.maxY) {
3153
2899
  this.nextSection();
@@ -3158,7 +2904,7 @@ class LineWrapper extends events.EventEmitter {
3158
2904
  let lc = 0;
3159
2905
  let {
3160
2906
  y
3161
- } = this.document; // used to reset Y pos if options.continued (below)
2907
+ } = this.document;
3162
2908
  const emitLine = () => {
3163
2909
  options.textWidth = textWidth + this.wordSpacing * (wc - 1);
3164
2910
  options.wordCount = wc;
@@ -3181,23 +2927,17 @@ class LineWrapper extends events.EventEmitter {
3181
2927
  wc++;
3182
2928
  }
3183
2929
  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
2930
  const lh = this.document.currentLineHeight(true);
3187
- if (this.height != null && this.ellipsis && this.document.y + lh * 2 > this.maxY && this.column >= this.columns) {
2931
+ if (this.height != null && this.ellipsis && PDFNumber(this.document.y + lh * 2) > this.maxY && this.column >= this.columns) {
3188
2932
  if (this.ellipsis === true) {
3189
2933
  this.ellipsis = '…';
3190
- } // map default ellipsis character
2934
+ }
3191
2935
  buffer = buffer.replace(/\s+$/, '');
3192
2936
  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
2937
  while (buffer && textWidth > this.lineWidth) {
3197
2938
  buffer = buffer.slice(0, -1).replace(/\s+$/, '');
3198
2939
  textWidth = this.wordWidth(buffer + this.ellipsis);
3199
2940
  }
3200
- // need to add ellipsis only if there is enough space for it
3201
2941
  if (textWidth <= this.lineWidth) {
3202
2942
  buffer = buffer + this.ellipsis;
3203
2943
  }
@@ -3212,35 +2952,25 @@ class LineWrapper extends events.EventEmitter {
3212
2952
  }
3213
2953
  this.emit('lastLine', options, this);
3214
2954
  }
3215
-
3216
- // Previous entry is a soft hyphen - add visible hyphen.
3217
2955
  if (buffer[buffer.length - 1] == SOFT_HYPHEN) {
3218
2956
  buffer = buffer.slice(0, -1) + HYPHEN;
3219
2957
  this.spaceLeft -= this.wordWidth(HYPHEN);
3220
2958
  }
3221
2959
  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) {
2960
+ if (PDFNumber(this.document.y + lh) > this.maxY) {
3226
2961
  const shouldContinue = this.nextSection();
3227
-
3228
- // stop if we reached the maximum height
3229
2962
  if (!shouldContinue) {
3230
2963
  wc = 0;
3231
2964
  buffer = '';
3232
2965
  return false;
3233
2966
  }
3234
2967
  }
3235
-
3236
- // reset the space left and buffer
3237
2968
  if (bk.required) {
3238
2969
  this.spaceLeft = this.lineWidth;
3239
2970
  buffer = '';
3240
2971
  textWidth = 0;
3241
2972
  return wc = 0;
3242
2973
  } else {
3243
- // reset the space left and buffer
3244
2974
  this.spaceLeft = this.lineWidth - w;
3245
2975
  buffer = word;
3246
2976
  textWidth = w;
@@ -3255,25 +2985,19 @@ class LineWrapper extends events.EventEmitter {
3255
2985
  emitLine();
3256
2986
  }
3257
2987
  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
2988
  if (options.continued === true) {
3263
2989
  if (lc > 1) {
3264
2990
  this.continuedX = 0;
3265
2991
  }
3266
2992
  this.continuedX += options.textWidth || 0;
3267
- return this.document.y = y;
2993
+ this.document.y = y;
3268
2994
  } else {
3269
- return this.document.x = this.startX;
2995
+ this.document.x = this.startX;
3270
2996
  }
3271
2997
  }
3272
2998
  nextSection(options) {
3273
2999
  this.emit('sectionEnd', options, this);
3274
3000
  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
3001
  if (this.height != null) {
3278
3002
  return false;
3279
3003
  }
@@ -3302,10 +3026,9 @@ const {
3302
3026
  var TextMixin = {
3303
3027
  initText() {
3304
3028
  this._line = this._line.bind(this);
3305
- // Current coordinates
3306
3029
  this.x = 0;
3307
3030
  this.y = 0;
3308
- return this._lineGap = 0;
3031
+ this._lineGap = 0;
3309
3032
  },
3310
3033
  lineGap(_lineGap) {
3311
3034
  this._lineGap = _lineGap;
@@ -3327,11 +3050,7 @@ var TextMixin = {
3327
3050
  },
3328
3051
  _text(text, x, y, options, lineCallback) {
3329
3052
  options = this._initOptions(x, y, options);
3330
-
3331
- // Convert text to a string
3332
3053
  text = text == null ? '' : `${text}`;
3333
-
3334
- // if the wordSpacing option is specified, remove multiple consecutive spaces
3335
3054
  if (options.wordSpacing) {
3336
3055
  text = text.replace(/\s{2,}/g, ' ');
3337
3056
  }
@@ -3340,8 +3059,12 @@ var TextMixin = {
3340
3059
  options.structParent.add(this.struct(options.structType || 'P', [this.markStructureContent(options.structType || 'P')]));
3341
3060
  }
3342
3061
  };
3343
-
3344
- // word wrapping
3062
+ if (options.rotation !== 0) {
3063
+ this.save();
3064
+ this.rotate(-options.rotation, {
3065
+ origin: [this.x, this.y]
3066
+ });
3067
+ }
3345
3068
  if (options.width) {
3346
3069
  let wrapper = this._wrapper;
3347
3070
  if (!wrapper) {
@@ -3352,14 +3075,13 @@ var TextMixin = {
3352
3075
  this._wrapper = options.continued ? wrapper : null;
3353
3076
  this._textOptions = options.continued ? options : null;
3354
3077
  wrapper.wrap(text, options);
3355
-
3356
- // render paragraphs as single lines
3357
3078
  } else {
3358
3079
  for (let line of text.split('\n')) {
3359
3080
  addStructure();
3360
3081
  lineCallback(line, options);
3361
3082
  }
3362
3083
  }
3084
+ if (options.rotation !== 0) this.restore();
3363
3085
  return this;
3364
3086
  },
3365
3087
  text(text, x, y, options) {
@@ -3369,17 +3091,108 @@ var TextMixin = {
3369
3091
  const horizontalScaling = options.horizontalScaling || 100;
3370
3092
  return (this._font.widthOfString(string, this._fontSize, options.features) + (options.characterSpacing || 0) * (string.length - 1)) * horizontalScaling / 100;
3371
3093
  },
3094
+ boundsOfString(string, x, y, options) {
3095
+ options = this._initOptions(x, y, options);
3096
+ ({
3097
+ x,
3098
+ y
3099
+ } = this);
3100
+ const lineGap = options.lineGap ?? this._lineGap ?? 0;
3101
+ const lineHeight = this.currentLineHeight(true) + lineGap;
3102
+ let contentWidth = 0;
3103
+ string = String(string ?? '');
3104
+ if (options.wordSpacing) {
3105
+ string = string.replace(/\s{2,}/g, ' ');
3106
+ }
3107
+ if (options.width) {
3108
+ let wrapper = new LineWrapper(this, options);
3109
+ wrapper.on('line', (text, options) => {
3110
+ this.y += lineHeight;
3111
+ text = text.replace(/\n/g, '');
3112
+ if (text.length) {
3113
+ let wordSpacing = options.wordSpacing ?? 0;
3114
+ const characterSpacing = options.characterSpacing ?? 0;
3115
+ if (options.width && options.align === 'justify') {
3116
+ const words = text.trim().split(/\s+/);
3117
+ const textWidth = this.widthOfString(text.replace(/\s+/g, ''), options);
3118
+ const spaceWidth = this.widthOfString(' ') + characterSpacing;
3119
+ wordSpacing = Math.max(0, (options.lineWidth - textWidth) / Math.max(1, words.length - 1) - spaceWidth);
3120
+ }
3121
+ contentWidth = Math.max(contentWidth, options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1));
3122
+ }
3123
+ });
3124
+ wrapper.wrap(string, options);
3125
+ } else {
3126
+ for (let line of string.split('\n')) {
3127
+ const lineWidth = this.widthOfString(line, options);
3128
+ this.y += lineHeight;
3129
+ contentWidth = Math.max(contentWidth, lineWidth);
3130
+ }
3131
+ }
3132
+ let contentHeight = this.y - y;
3133
+ if (options.height) contentHeight = Math.min(contentHeight, options.height);
3134
+ this.x = x;
3135
+ this.y = y;
3136
+ if (options.rotation === 0) {
3137
+ return {
3138
+ x,
3139
+ y,
3140
+ width: contentWidth,
3141
+ height: contentHeight
3142
+ };
3143
+ } else if (options.rotation === 90) {
3144
+ return {
3145
+ x: x,
3146
+ y: y - contentWidth,
3147
+ width: contentHeight,
3148
+ height: contentWidth
3149
+ };
3150
+ } else if (options.rotation === 180) {
3151
+ return {
3152
+ x: x - contentWidth,
3153
+ y: y - contentHeight,
3154
+ width: contentWidth,
3155
+ height: contentHeight
3156
+ };
3157
+ } else if (options.rotation === 270) {
3158
+ return {
3159
+ x: x - contentHeight,
3160
+ y: y,
3161
+ width: contentHeight,
3162
+ height: contentWidth
3163
+ };
3164
+ }
3165
+ const cos = cosine(options.rotation);
3166
+ const sin = sine(options.rotation);
3167
+ const x1 = x;
3168
+ const y1 = y;
3169
+ const x2 = x + contentWidth * cos;
3170
+ const y2 = y - contentWidth * sin;
3171
+ const x3 = x + contentWidth * cos + contentHeight * sin;
3172
+ const y3 = y - contentWidth * sin + contentHeight * cos;
3173
+ const x4 = x + contentHeight * sin;
3174
+ const y4 = y + contentHeight * cos;
3175
+ const xMin = Math.min(x1, x2, x3, x4);
3176
+ const xMax = Math.max(x1, x2, x3, x4);
3177
+ const yMin = Math.min(y1, y2, y3, y4);
3178
+ const yMax = Math.max(y1, y2, y3, y4);
3179
+ return {
3180
+ x: xMin,
3181
+ y: yMin,
3182
+ width: xMax - xMin,
3183
+ height: yMax - yMin
3184
+ };
3185
+ },
3372
3186
  heightOfString(text, options) {
3373
3187
  const {
3374
3188
  x,
3375
3189
  y
3376
3190
  } = this;
3377
3191
  options = this._initOptions(options);
3378
- options.height = Infinity; // don't break pages
3379
-
3192
+ options.height = Infinity;
3380
3193
  const lineGap = options.lineGap || this._lineGap || 0;
3381
3194
  this._text(text, this.x, this.y, options, () => {
3382
- return this.y += this.currentLineHeight(true) + lineGap;
3195
+ this.y += this.currentLineHeight(true) + lineGap;
3383
3196
  });
3384
3197
  const height = this.y - y;
3385
3198
  this.x = x;
@@ -3477,12 +3290,12 @@ var TextMixin = {
3477
3290
  wrapper.on('sectionStart', () => {
3478
3291
  const pos = indent + itemIndent * (level - 1);
3479
3292
  this.x += pos;
3480
- return wrapper.lineWidth -= pos;
3293
+ wrapper.lineWidth -= pos;
3481
3294
  });
3482
3295
  wrapper.on('sectionEnd', () => {
3483
3296
  const pos = indent + itemIndent * (level - 1);
3484
3297
  this.x -= pos;
3485
- return wrapper.lineWidth += pos;
3298
+ wrapper.lineWidth += pos;
3486
3299
  });
3487
3300
  wrapper.wrap(listItem, options);
3488
3301
  };
@@ -3496,11 +3309,7 @@ var TextMixin = {
3496
3309
  options = x;
3497
3310
  x = null;
3498
3311
  }
3499
-
3500
- // clone options object
3501
3312
  const result = Object.assign({}, options);
3502
-
3503
- // extend options with previous values for continued text
3504
3313
  if (this._textOptions) {
3505
3314
  for (let key in this._textOptions) {
3506
3315
  const val = this._textOptions[key];
@@ -3511,16 +3320,12 @@ var TextMixin = {
3511
3320
  }
3512
3321
  }
3513
3322
  }
3514
-
3515
- // Update the current position
3516
3323
  if (x != null) {
3517
3324
  this.x = x;
3518
3325
  }
3519
3326
  if (y != null) {
3520
3327
  this.y = y;
3521
3328
  }
3522
-
3523
- // wrap to margins if no x or y position passed
3524
3329
  if (result.lineBreak !== false) {
3525
3330
  if (result.width == null) {
3526
3331
  result.width = this.page.width - this.x - this.page.margins.right;
@@ -3532,17 +3337,18 @@ var TextMixin = {
3532
3337
  }
3533
3338
  if (result.columnGap == null) {
3534
3339
  result.columnGap = 18;
3535
- } // 1/4 inch
3536
-
3340
+ }
3341
+ result.rotation = Number(options.rotation ?? 0) % 360;
3342
+ if (result.rotation < 0) result.rotation += 360;
3537
3343
  return result;
3538
3344
  },
3539
3345
  _line(text, options = {}, wrapper) {
3540
3346
  this._fragment(text, this.x, this.y, options);
3541
3347
  const lineGap = options.lineGap || this._lineGap || 0;
3542
3348
  if (!wrapper) {
3543
- return this.x += this.widthOfString(text, options);
3349
+ this.x += this.widthOfString(text, options);
3544
3350
  } else {
3545
- return this.y += this.currentLineHeight(true) + lineGap;
3351
+ this.y += this.currentLineHeight(true) + lineGap;
3546
3352
  }
3547
3353
  },
3548
3354
  _fragment(text, x, y, options) {
@@ -3551,14 +3357,10 @@ var TextMixin = {
3551
3357
  if (text.length === 0) {
3552
3358
  return;
3553
3359
  }
3554
-
3555
- // handle options
3556
3360
  const align = options.align || 'left';
3557
3361
  let wordSpacing = options.wordSpacing || 0;
3558
3362
  const characterSpacing = options.characterSpacing || 0;
3559
3363
  const horizontalScaling = options.horizontalScaling || 100;
3560
-
3561
- // text alignments
3562
3364
  if (options.width) {
3563
3365
  switch (align) {
3564
3366
  case 'right':
@@ -3569,7 +3371,6 @@ var TextMixin = {
3569
3371
  x += options.lineWidth / 2 - options.textWidth / 2;
3570
3372
  break;
3571
3373
  case 'justify':
3572
- // calculate the word spacing value
3573
3374
  words = text.trim().split(/\s+/);
3574
3375
  textWidth = this.widthOfString(text.replace(/\s+/g, ''), options);
3575
3376
  var spaceWidth = this.widthOfString(' ') + characterSpacing;
@@ -3577,8 +3378,6 @@ var TextMixin = {
3577
3378
  break;
3578
3379
  }
3579
3380
  }
3580
-
3581
- // text baseline alignments based on http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
3582
3381
  if (typeof options.baseline === 'number') {
3583
3382
  dy = -options.baseline;
3584
3383
  } else {
@@ -3611,11 +3410,7 @@ var TextMixin = {
3611
3410
  }
3612
3411
  dy = dy / 1000 * this._fontSize;
3613
3412
  }
3614
-
3615
- // calculate the actual rendered width of the string after word and character spacing
3616
3413
  const renderedWidth = options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1);
3617
-
3618
- // create link annotations if the link option is given
3619
3414
  if (options.link != null) {
3620
3415
  this.link(x, y, renderedWidth, this.currentLineHeight(), options.link);
3621
3416
  }
@@ -3625,8 +3420,6 @@ var TextMixin = {
3625
3420
  if (options.destination != null) {
3626
3421
  this.addNamedDestination(options.destination, 'XYZ', x, y, null);
3627
3422
  }
3628
-
3629
- // create underline
3630
3423
  if (options.underline) {
3631
3424
  this.save();
3632
3425
  if (!options.stroke) {
@@ -3640,8 +3433,6 @@ var TextMixin = {
3640
3433
  this.stroke();
3641
3434
  this.restore();
3642
3435
  }
3643
-
3644
- // create strikethrough line
3645
3436
  if (options.strike) {
3646
3437
  this.save();
3647
3438
  if (!options.stroke) {
@@ -3656,8 +3447,6 @@ var TextMixin = {
3656
3447
  this.restore();
3657
3448
  }
3658
3449
  this.save();
3659
-
3660
- // oblique (angle in degrees or boolean)
3661
3450
  if (options.oblique) {
3662
3451
  let skew;
3663
3452
  if (typeof options.oblique === 'number') {
@@ -3669,45 +3458,24 @@ var TextMixin = {
3669
3458
  this.transform(1, 0, skew, 1, -skew * dy, 0);
3670
3459
  this.transform(1, 0, 0, 1, -x, -y);
3671
3460
  }
3672
-
3673
- // flip coordinate system
3674
3461
  this.transform(1, 0, 0, -1, 0, this.page.height);
3675
3462
  y = this.page.height - y - dy;
3676
-
3677
- // add current font to page if necessary
3678
3463
  if (this.page.fonts[this._font.id] == null) {
3679
3464
  this.page.fonts[this._font.id] = this._font.ref();
3680
3465
  }
3681
-
3682
- // begin the text object
3683
3466
  this.addContent('BT');
3684
-
3685
- // text position
3686
3467
  this.addContent(`1 0 0 1 ${number(x)} ${number(y)} Tm`);
3687
-
3688
- // font and font size
3689
3468
  this.addContent(`/${this._font.id} ${number(this._fontSize)} Tf`);
3690
-
3691
- // rendering mode
3692
3469
  const mode = options.fill && options.stroke ? 2 : options.stroke ? 1 : 0;
3693
3470
  if (mode) {
3694
3471
  this.addContent(`${mode} Tr`);
3695
3472
  }
3696
-
3697
- // Character spacing
3698
3473
  if (characterSpacing) {
3699
3474
  this.addContent(`${number(characterSpacing)} Tc`);
3700
3475
  }
3701
-
3702
- // Horizontal scaling
3703
3476
  if (horizontalScaling !== 100) {
3704
3477
  this.addContent(`${horizontalScaling} Tz`);
3705
3478
  }
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
3479
  if (wordSpacing) {
3712
3480
  words = text.trim().split(/\s+/);
3713
3481
  wordSpacing += this.widthOfString(' ') + characterSpacing;
@@ -3718,9 +3486,6 @@ var TextMixin = {
3718
3486
  const [encodedWord, positionsWord] = this._font.encode(word, options.features);
3719
3487
  encoded = encoded.concat(encodedWord);
3720
3488
  positions = positions.concat(positionsWord);
3721
-
3722
- // add the word spacing to the end of the word
3723
- // clone object because of cache
3724
3489
  const space = {};
3725
3490
  const object = positions[positions.length - 1];
3726
3491
  for (let key in object) {
@@ -3737,60 +3502,42 @@ var TextMixin = {
3737
3502
  const commands = [];
3738
3503
  let last = 0;
3739
3504
  let hadOffset = false;
3740
-
3741
- // Adds a segment of text to the TJ command buffer
3742
3505
  const addSegment = cur => {
3743
3506
  if (last < cur) {
3744
3507
  const hex = encoded.slice(last, cur).join('');
3745
3508
  const advance = positions[cur - 1].xAdvance - positions[cur - 1].advanceWidth;
3746
3509
  commands.push(`<${hex}> ${number(-advance)}`);
3747
3510
  }
3748
- return last = cur;
3511
+ last = cur;
3749
3512
  };
3750
-
3751
- // Flushes the current TJ commands to the output stream
3752
3513
  const flush = i => {
3753
3514
  addSegment(i);
3754
3515
  if (commands.length > 0) {
3755
3516
  this.addContent(`[${commands.join(' ')}] TJ`);
3756
- return commands.length = 0;
3517
+ commands.length = 0;
3757
3518
  }
3758
3519
  };
3759
3520
  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
3521
  const pos = positions[i];
3763
3522
  if (pos.xOffset || pos.yOffset) {
3764
- // Flush the current buffer
3765
3523
  flush(i);
3766
-
3767
- // Move the text position and flush just the current character
3768
3524
  this.addContent(`1 0 0 1 ${number(x + pos.xOffset * scale)} ${number(y + pos.yOffset * scale)} Tm`);
3769
3525
  flush(i + 1);
3770
3526
  hadOffset = true;
3771
3527
  } else {
3772
- // If the last character had an offset, reset the text position
3773
3528
  if (hadOffset) {
3774
3529
  this.addContent(`1 0 0 1 ${number(x)} ${number(y)} Tm`);
3775
3530
  hadOffset = false;
3776
3531
  }
3777
-
3778
- // Group segments that don't have any advance adjustments
3779
3532
  if (pos.xAdvance - pos.advanceWidth !== 0) {
3780
3533
  addSegment(i + 1);
3781
3534
  }
3782
3535
  }
3783
3536
  x += pos.xAdvance * scale;
3784
3537
  }
3785
-
3786
- // Flush any remaining commands
3787
3538
  flush(i);
3788
-
3789
- // end the text object
3790
3539
  this.addContent('ET');
3791
-
3792
- // restore flipped coordinate system
3793
- return this.restore();
3540
+ this.restore();
3794
3541
  }
3795
3542
  };
3796
3543
 
@@ -3808,8 +3555,6 @@ class JPEG {
3808
3555
  if (this.data.readUInt16BE(0) !== 0xffd8) {
3809
3556
  throw 'SOI not found in JPEG';
3810
3557
  }
3811
-
3812
- // Parse the EXIF orientation
3813
3558
  this.orientation = exif.fromBuffer(this.data).Orientation || 1;
3814
3559
  let pos = 2;
3815
3560
  while (pos < this.data.length) {
@@ -3846,16 +3591,10 @@ class JPEG {
3846
3591
  ColorSpace: this.colorSpace,
3847
3592
  Filter: 'DCTDecode'
3848
3593
  });
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
3594
  if (this.colorSpace === 'DeviceCMYK') {
3854
3595
  this.obj.data['Decode'] = [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0];
3855
3596
  }
3856
3597
  this.obj.end(this.data);
3857
-
3858
- // free memory
3859
3598
  return this.data = null;
3860
3599
  }
3861
3600
  }
@@ -3898,24 +3637,14 @@ class PNGImage {
3898
3637
  if (this.image.palette.length === 0) {
3899
3638
  this.obj.data['ColorSpace'] = this.image.colorSpace;
3900
3639
  } else {
3901
- // embed the color palette in the PDF as an object stream
3902
3640
  const palette = this.document.ref();
3903
3641
  palette.end(Buffer.from(this.image.palette));
3904
-
3905
- // build the color space array for the image
3906
3642
  this.obj.data['ColorSpace'] = ['Indexed', 'DeviceRGB', this.image.palette.length / 3 - 1, palette];
3907
3643
  }
3908
-
3909
- // For PNG color types 0, 2 and 3, the transparency data is stored in
3910
- // a dedicated PNG chunk.
3911
3644
  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
3645
  const val = this.image.transparency.grayscale;
3915
3646
  this.obj.data['Mask'] = [val, val];
3916
3647
  } 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
3648
  const {
3920
3649
  rgb
3921
3650
  } = this.image.transparency;
@@ -3925,14 +3654,9 @@ class PNGImage {
3925
3654
  }
3926
3655
  this.obj.data['Mask'] = mask;
3927
3656
  } 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
3657
  dataDecoded = true;
3931
3658
  return this.loadIndexedAlphaChannel();
3932
3659
  } 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
3660
  dataDecoded = true;
3937
3661
  return this.splitAlphaChannel();
3938
3662
  }
@@ -3956,11 +3680,7 @@ class PNGImage {
3956
3680
  sMask.end(this.alphaChannel);
3957
3681
  this.obj.data['SMask'] = sMask;
3958
3682
  }
3959
-
3960
- // add the actual image data
3961
3683
  this.obj.end(this.imgData);
3962
-
3963
- // free memory
3964
3684
  this.image = null;
3965
3685
  return this.imgData = null;
3966
3686
  }
@@ -3973,7 +3693,6 @@ class PNGImage {
3973
3693
  const alphaChannel = Buffer.alloc(pixelCount);
3974
3694
  let i = p = a = 0;
3975
3695
  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
3696
  const skipByteCount = this.image.bits === 16 ? 1 : 0;
3978
3697
  while (i < len) {
3979
3698
  for (let colorIndex = 0; colorIndex < colorCount; colorIndex++) {
@@ -4008,10 +3727,6 @@ class PNGImage {
4008
3727
  }
4009
3728
  }
4010
3729
 
4011
- /*
4012
- PDFImage - embeds images in PDF documents
4013
- By Devon Govett
4014
- */
4015
3730
  class PDFImage {
4016
3731
  static open(src, label) {
4017
3732
  let data;
@@ -4020,8 +3735,8 @@ class PDFImage {
4020
3735
  } else if (src instanceof ArrayBuffer) {
4021
3736
  data = Buffer.from(new Uint8Array(src));
4022
3737
  } else {
4023
- let match;
4024
- if (match = /^data:.+?;base64,(.*)$/.exec(src)) {
3738
+ const match = /^data:.+?;base64,(.*)$/.exec(src);
3739
+ if (match) {
4025
3740
  data = Buffer.from(match[1], 'base64');
4026
3741
  } else {
4027
3742
  data = fs.readFileSync(src);
@@ -4043,17 +3758,16 @@ class PDFImage {
4043
3758
  var ImagesMixin = {
4044
3759
  initImages() {
4045
3760
  this._imageRegistry = {};
4046
- return this._imageCount = 0;
3761
+ this._imageCount = 0;
4047
3762
  },
4048
3763
  image(src, x, y, options = {}) {
4049
- let bh, bp, bw, image, ip, left, left1, rotateAngle, originX, originY;
3764
+ let bh, bp, bw, image, ip, left, left1, originX, originY;
4050
3765
  if (typeof x === 'object') {
4051
3766
  options = x;
4052
3767
  x = null;
4053
3768
  }
4054
-
4055
- // Ignore orientation based on document options or image options
4056
3769
  const ignoreOrientation = options.ignoreOrientation || options.ignoreOrientation !== false && this.options.ignoreOrientation;
3770
+ const inDocumentFlow = typeof y !== 'number';
4057
3771
  x = (left = x != null ? x : options.x) != null ? left : this.x;
4058
3772
  y = (left1 = y != null ? y : options.y) != null ? left1 : this.y;
4059
3773
  if (typeof src === 'string') {
@@ -4076,8 +3790,6 @@ var ImagesMixin = {
4076
3790
  width,
4077
3791
  height
4078
3792
  } = image;
4079
-
4080
- // If EXIF orientation calls for it, swap width and height
4081
3793
  if (!ignoreOrientation && image.orientation > 4) {
4082
3794
  [width, height] = [height, width];
4083
3795
  }
@@ -4129,80 +3841,70 @@ var ImagesMixin = {
4129
3841
  y = y + bh - h;
4130
3842
  }
4131
3843
  }
3844
+ let rotateAngle = 0;
3845
+ let xTransform = x;
3846
+ let yTransform = y;
3847
+ let hTransform = h;
3848
+ let wTransform = w;
4132
3849
  if (!ignoreOrientation) {
4133
3850
  switch (image.orientation) {
4134
- // No orientation (need to flip image, though, because of the default transform matrix on the document)
4135
3851
  default:
4136
3852
  case 1:
4137
- h = -h;
4138
- y -= h;
4139
- rotateAngle = 0;
3853
+ hTransform = -h;
3854
+ yTransform += h;
4140
3855
  break;
4141
- // Flip Horizontal
4142
3856
  case 2:
4143
- w = -w;
4144
- h = -h;
4145
- x -= w;
4146
- y -= h;
4147
- rotateAngle = 0;
3857
+ wTransform = -w;
3858
+ hTransform = -h;
3859
+ xTransform += w;
3860
+ yTransform += h;
4148
3861
  break;
4149
- // Rotate 180 degrees
4150
3862
  case 3:
4151
3863
  originX = x;
4152
3864
  originY = y;
4153
- h = -h;
4154
- x -= w;
3865
+ hTransform = -h;
3866
+ xTransform -= w;
4155
3867
  rotateAngle = 180;
4156
3868
  break;
4157
- // Flip vertical
4158
3869
  case 4:
4159
- // Do nothing, image will be flipped
4160
-
4161
3870
  break;
4162
- // Flip horizontally and rotate 270 degrees CW
4163
3871
  case 5:
4164
3872
  originX = x;
4165
3873
  originY = y;
4166
- [w, h] = [h, w];
4167
- y -= h;
3874
+ wTransform = h;
3875
+ hTransform = w;
3876
+ yTransform -= hTransform;
4168
3877
  rotateAngle = 90;
4169
3878
  break;
4170
- // Rotate 90 degrees CW
4171
3879
  case 6:
4172
3880
  originX = x;
4173
3881
  originY = y;
4174
- [w, h] = [h, w];
4175
- h = -h;
3882
+ wTransform = h;
3883
+ hTransform = -w;
4176
3884
  rotateAngle = 90;
4177
3885
  break;
4178
- // Flip horizontally and rotate 90 degrees CW
4179
3886
  case 7:
4180
3887
  originX = x;
4181
3888
  originY = y;
4182
- [w, h] = [h, w];
4183
- h = -h;
4184
- w = -w;
4185
- x -= w;
3889
+ hTransform = -w;
3890
+ wTransform = -h;
3891
+ xTransform += h;
4186
3892
  rotateAngle = 90;
4187
3893
  break;
4188
- // Rotate 270 degrees CW
4189
3894
  case 8:
4190
3895
  originX = x;
4191
3896
  originY = y;
4192
- [w, h] = [h, w];
4193
- h = -h;
4194
- x -= w;
4195
- y -= h;
3897
+ wTransform = h;
3898
+ hTransform = -w;
3899
+ xTransform -= h;
3900
+ yTransform += w;
4196
3901
  rotateAngle = -90;
4197
3902
  break;
4198
3903
  }
4199
3904
  } else {
4200
- h = -h;
4201
- y -= h;
4202
- rotateAngle = 0;
3905
+ hTransform = -h;
3906
+ yTransform += h;
4203
3907
  }
4204
-
4205
- // create link annotations if the link option is given
4206
3908
  if (options.link != null) {
4207
3909
  this.link(x, y, w, h, options.link);
4208
3910
  }
@@ -4212,9 +3914,7 @@ var ImagesMixin = {
4212
3914
  if (options.destination != null) {
4213
3915
  this.addNamedDestination(options.destination, 'XYZ', x, y, null);
4214
3916
  }
4215
-
4216
- // Set the current y position to below the image if it is in the document flow
4217
- if (this.y === y) {
3917
+ if (inDocumentFlow) {
4218
3918
  this.y += h;
4219
3919
  }
4220
3920
  this.save();
@@ -4223,7 +3923,7 @@ var ImagesMixin = {
4223
3923
  origin: [originX, originY]
4224
3924
  });
4225
3925
  }
4226
- this.transform(w, 0, 0, h, x, y);
3926
+ this.transform(wTransform, 0, 0, hTransform, xTransform, yTransform);
4227
3927
  this.addContent(`/${image.label} Do`);
4228
3928
  this.restore();
4229
3929
  return this;
@@ -4249,19 +3949,17 @@ var AnnotationsMixin = {
4249
3949
  options.Rect = this._convertRect(x, y, w, h);
4250
3950
  options.Border = [0, 0, 0];
4251
3951
  if (options.Subtype === 'Link' && typeof options.F === 'undefined') {
4252
- options.F = 1 << 2; // Print Annotation Flag
3952
+ options.F = 1 << 2;
4253
3953
  }
4254
3954
  if (options.Subtype !== 'Link') {
4255
3955
  if (options.C == null) {
4256
3956
  options.C = this._normalizeColor(options.color || [0, 0, 0]);
4257
3957
  }
4258
- } // convert colors
3958
+ }
4259
3959
  delete options.color;
4260
3960
  if (typeof options.Dest === 'string') {
4261
3961
  options.Dest = new String(options.Dest);
4262
3962
  }
4263
-
4264
- // Capitalize keys
4265
3963
  for (let key in options) {
4266
3964
  const val = options[key];
4267
3965
  options[key[0].toUpperCase() + key.slice(1)] = val;
@@ -4294,7 +3992,6 @@ var AnnotationsMixin = {
4294
3992
  link(x, y, w, h, url, options = {}) {
4295
3993
  options.Subtype = 'Link';
4296
3994
  if (typeof url === 'number') {
4297
- // Link to a page in the document (the page must already exist)
4298
3995
  const pages = this._root.data.Pages.data;
4299
3996
  if (url >= 0 && url < pages.Kids.length) {
4300
3997
  options.A = this.ref({
@@ -4306,7 +4003,6 @@ var AnnotationsMixin = {
4306
4003
  throw new Error(`The document has no page ${url}`);
4307
4004
  }
4308
4005
  } else {
4309
- // Link to an external url
4310
4006
  options.A = this.ref({
4311
4007
  S: 'URI',
4312
4008
  URI: new String(url)
@@ -4359,14 +4055,11 @@ var AnnotationsMixin = {
4359
4055
  return this.annotate(x, y, w, h, options);
4360
4056
  },
4361
4057
  fileAnnotation(x, y, w, h, file = {}, options = {}) {
4362
- // create hidden file
4363
4058
  const filespec = this.file(file.src, Object.assign({
4364
4059
  hidden: true
4365
4060
  }, file));
4366
4061
  options.Subtype = 'FileAttachment';
4367
4062
  options.FS = filespec;
4368
-
4369
- // add description from filespec unless description (Contents) has already been set
4370
4063
  if (options.Contents) {
4371
4064
  options.Contents = new String(options.Contents);
4372
4065
  } else if (filespec.data.Desc) {
@@ -4375,14 +4068,9 @@ var AnnotationsMixin = {
4375
4068
  return this.annotate(x, y, w, h, options);
4376
4069
  },
4377
4070
  _convertRect(x1, y1, w, h) {
4378
- // flip y1 and y2
4379
4071
  let y2 = y1;
4380
4072
  y1 += h;
4381
-
4382
- // make x2
4383
4073
  let x2 = x1 + w;
4384
-
4385
- // apply current transformation matrix to points
4386
4074
  const [m0, m1, m2, m3, m4, m5] = this._ctm;
4387
4075
  x1 = m0 * x1 + m2 * y1 + m4;
4388
4076
  y1 = m1 * x1 + m3 * y1 + m5;
@@ -4444,7 +4132,7 @@ class PDFOutline {
4444
4132
 
4445
4133
  var OutlineMixin = {
4446
4134
  initOutline() {
4447
- return this.outline = new PDFOutline(this, null, null, null);
4135
+ this.outline = new PDFOutline(this, null, null, null);
4448
4136
  },
4449
4137
  endOutline() {
4450
4138
  this.outline.endOutline();
@@ -4455,11 +4143,6 @@ var OutlineMixin = {
4455
4143
  }
4456
4144
  };
4457
4145
 
4458
- /*
4459
- PDFStructureContent - a reference to a marked structure content
4460
- By Ben Schmidt
4461
- */
4462
-
4463
4146
  class PDFStructureContent {
4464
4147
  constructor(pageRef, mcid) {
4465
4148
  this.refs = [{
@@ -4472,10 +4155,6 @@ class PDFStructureContent {
4472
4155
  }
4473
4156
  }
4474
4157
 
4475
- /*
4476
- PDFStructureElement - represents an element in the PDF logical structure tree
4477
- By Ben Schmidt
4478
- */
4479
4158
  class PDFStructureElement {
4480
4159
  constructor(document, type, options = {}, children = null) {
4481
4160
  this.document = document;
@@ -4483,7 +4162,6 @@ class PDFStructureElement {
4483
4162
  this._ended = false;
4484
4163
  this._flushed = false;
4485
4164
  this.dictionary = document.ref({
4486
- // Type: "StructElem",
4487
4165
  S: type
4488
4166
  });
4489
4167
  const data = this.dictionary.data;
@@ -4532,7 +4210,6 @@ class PDFStructureElement {
4532
4210
  this._addContentToParentTree(child);
4533
4211
  }
4534
4212
  if (typeof child === 'function' && this._attached) {
4535
- // _contentForClosure() adds the content to the parent tree
4536
4213
  child = this._contentForClosure(child);
4537
4214
  }
4538
4215
  this._children.push(child);
@@ -4608,10 +4285,6 @@ class PDFStructureElement {
4608
4285
  this.dictionary.data.K = [];
4609
4286
  this._children.forEach(child => this._flushChild(child));
4610
4287
  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
4288
  this._children = [];
4616
4289
  this.dictionary.data.K = null;
4617
4290
  this._flushed = true;
@@ -4632,7 +4305,7 @@ class PDFStructureElement {
4632
4305
  this.dictionary.data.K.push(mcid);
4633
4306
  } else {
4634
4307
  this.dictionary.data.K.push({
4635
- Type: "MCR",
4308
+ Type: 'MCR',
4636
4309
  Pg: pageRef,
4637
4310
  MCID: mcid
4638
4311
  });
@@ -4642,25 +4315,18 @@ class PDFStructureElement {
4642
4315
  }
4643
4316
  }
4644
4317
 
4645
- /*
4646
- PDFNumberTree - represents a number tree object
4647
- */
4648
4318
  class PDFNumberTree extends PDFTree {
4649
4319
  _compareKeys(a, b) {
4650
4320
  return parseInt(a) - parseInt(b);
4651
4321
  }
4652
4322
  _keysName() {
4653
- return "Nums";
4323
+ return 'Nums';
4654
4324
  }
4655
4325
  _dataForKey(k) {
4656
4326
  return parseInt(k);
4657
4327
  }
4658
4328
  }
4659
4329
 
4660
- /*
4661
- Markings mixin - support marked content sequences in content streams
4662
- By Ben Schmidt
4663
- */
4664
4330
  var MarkingsMixin = {
4665
4331
  initMarkings(options) {
4666
4332
  this.structChildren = [];
@@ -4796,7 +4462,6 @@ var MarkingsMixin = {
4796
4462
  return this.getStructTreeRoot().data.ParentTree;
4797
4463
  },
4798
4464
  createStructParentTreeNextKey() {
4799
- // initialise the MarkInfo dictionary
4800
4465
  this.getMarkInfoDictionary();
4801
4466
  const structTreeRoot = this.getStructTreeRoot();
4802
4467
  const key = structTreeRoot.data.ParentTreeNextKey++;
@@ -4860,10 +4525,6 @@ const FORMAT_DEFAULT = {
4860
4525
  }
4861
4526
  };
4862
4527
  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
4528
  initForm() {
4868
4529
  if (!this._font) {
4869
4530
  throw new Error('Must set a font before calling initForm method');
@@ -4886,9 +4547,6 @@ var AcroFormMixin = {
4886
4547
  this._root.data.AcroForm = AcroForm;
4887
4548
  return this;
4888
4549
  },
4889
- /**
4890
- * Called automatically by document.js
4891
- */
4892
4550
  endAcroForm() {
4893
4551
  if (this._root.data.AcroForm) {
4894
4552
  if (!Object.keys(this._acroform.fonts).length && !this._acroform.defaultFont) {
@@ -4914,38 +4572,18 @@ var AcroFormMixin = {
4914
4572
  }
4915
4573
  return this;
4916
4574
  },
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
4575
  formField(name, options = {}) {
4925
4576
  let fieldDict = this._fieldDict(name, null, options);
4926
4577
  let fieldRef = this.ref(fieldDict);
4927
4578
  this._addToParent(fieldRef);
4928
4579
  return fieldRef;
4929
4580
  },
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
4581
  formAnnotation(name, type, x, y, w, h, options = {}) {
4942
4582
  let fieldDict = this._fieldDict(name, type, options);
4943
4583
  fieldDict.Subtype = 'Widget';
4944
4584
  if (fieldDict.F === undefined) {
4945
- fieldDict.F = 4; // print the annotation
4585
+ fieldDict.F = 4;
4946
4586
  }
4947
-
4948
- // Add Field annot to page, and get it's ref
4949
4587
  this.annotate(x, y, w, h, fieldDict);
4950
4588
  let annotRef = this.page.annotations[this.page.annotations.length - 1];
4951
4589
  return this._addToParent(annotRef);
@@ -5106,23 +4744,18 @@ var AcroFormMixin = {
5106
4744
  delete options.align;
5107
4745
  }
5108
4746
  if (result !== 0) {
5109
- options.Q = result; // default
4747
+ options.Q = result;
5110
4748
  }
5111
4749
  return options;
5112
4750
  },
5113
4751
  _resolveFont(options) {
5114
- // add current font to document-level AcroForm dict if necessary
5115
4752
  if (this._acroform.fonts[this._font.id] == null) {
5116
4753
  this._acroform.fonts[this._font.id] = this._font.ref();
5117
4754
  }
5118
-
5119
- // add current font to field's resource dict (RD) if not the default acroform font
5120
4755
  if (this._acroform.defaultFont !== this._font.name) {
5121
4756
  options.DR = {
5122
4757
  Font: {}
5123
4758
  };
5124
-
5125
- // Get the fontSize option. If not set use auto sizing
5126
4759
  const fontSize = options.fontSize || 0;
5127
4760
  options.DR.Font[this._font.id] = this._font.ref();
5128
4761
  options.DA = new String(`/${this._font.id} ${fontSize} Tf 0 g`);
@@ -5174,19 +4807,6 @@ var AcroFormMixin = {
5174
4807
  };
5175
4808
 
5176
4809
  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
4810
  file(src, options = {}) {
5191
4811
  options.name = options.name || src;
5192
4812
  options.relationship = options.relationship || 'Unspecified';
@@ -5203,8 +4823,8 @@ var AttachmentsMixin = {
5203
4823
  } else if (src instanceof ArrayBuffer) {
5204
4824
  data = Buffer.from(new Uint8Array(src));
5205
4825
  } else {
5206
- let match;
5207
- if (match = /^data:(.*?);base64,(.*)$/.exec(src)) {
4826
+ const match = /^data:(.*?);base64,(.*)$/.exec(src);
4827
+ if (match) {
5208
4828
  if (match[1]) {
5209
4829
  refBody.Subtype = match[1].replace('/', '#2F');
5210
4830
  }
@@ -5214,8 +4834,6 @@ var AttachmentsMixin = {
5214
4834
  if (!data) {
5215
4835
  throw new Error(`Could not read contents of file at filepath ${src}`);
5216
4836
  }
5217
-
5218
- // update CreationDate and ModDate
5219
4837
  const {
5220
4838
  birthtime,
5221
4839
  ctime
@@ -5224,26 +4842,18 @@ var AttachmentsMixin = {
5224
4842
  refBody.Params.ModDate = ctime;
5225
4843
  }
5226
4844
  }
5227
-
5228
- // override creation date and modified date
5229
4845
  if (options.creationDate instanceof Date) {
5230
4846
  refBody.Params.CreationDate = options.creationDate;
5231
4847
  }
5232
4848
  if (options.modifiedDate instanceof Date) {
5233
4849
  refBody.Params.ModDate = options.modifiedDate;
5234
4850
  }
5235
- // add optional subtype
5236
4851
  if (options.type) {
5237
4852
  refBody.Subtype = options.type.replace('/', '#2F');
5238
4853
  }
5239
-
5240
- // add checksum and size information
5241
4854
  const checksum = CryptoJS.MD5(CryptoJS.lib.WordArray.create(new Uint8Array(data)));
5242
4855
  refBody.Params.CheckSum = new String(checksum);
5243
4856
  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
4857
  let ref;
5248
4858
  if (!this._fileRegistry) this._fileRegistry = {};
5249
4859
  let file = this._fileRegistry[options.name];
@@ -5257,7 +4867,6 @@ var AttachmentsMixin = {
5257
4867
  ref
5258
4868
  };
5259
4869
  }
5260
- // add filespec for embedded file
5261
4870
  const fileSpecBody = {
5262
4871
  Type: 'Filespec',
5263
4872
  AFRelationship: options.relationship,
@@ -5275,8 +4884,6 @@ var AttachmentsMixin = {
5275
4884
  if (!options.hidden) {
5276
4885
  this.addNamedEmbeddedFile(options.name, filespec);
5277
4886
  }
5278
-
5279
- // Add file to the catalogue to be PDF/A3 compliant
5280
4887
  if (this._root.data.AF) {
5281
4888
  this._root.data.AF.push(filespec);
5282
4889
  } else {
@@ -5285,8 +4892,6 @@ var AttachmentsMixin = {
5285
4892
  return filespec;
5286
4893
  }
5287
4894
  };
5288
-
5289
- /** check two embedded file metadata objects for equality */
5290
4895
  function isEqual(a, b) {
5291
4896
  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
4897
  }
@@ -5297,7 +4902,6 @@ var PDFA = {
5297
4902
  this.subset_conformance = pSubset.charAt(pSubset.length - 1).toUpperCase();
5298
4903
  this.subset = parseInt(pSubset.charAt(pSubset.length - 2));
5299
4904
  } else {
5300
- // Default to Basic conformance when user doesn't specify
5301
4905
  this.subset_conformance = 'B';
5302
4906
  this.subset = parseInt(pSubset.charAt(pSubset.length - 1));
5303
4907
  }
@@ -5382,6 +4986,642 @@ var SubsetMixin = {
5382
4986
  }
5383
4987
  };
5384
4988
 
4989
+ const ROW_FIELDS = ['height', 'minHeight', 'maxHeight'];
4990
+ const COLUMN_FIELDS = ['width', 'minWidth', 'maxWidth'];
4991
+ function memoize(fn, maxSize) {
4992
+ const cache = new Map();
4993
+ return function (...args) {
4994
+ const key = args[0];
4995
+ if (!cache.has(key)) {
4996
+ cache.set(key, fn(...args));
4997
+ if (cache.size > maxSize) cache.delete(cache.keys().next());
4998
+ }
4999
+ return cache.get(key);
5000
+ };
5001
+ }
5002
+ function isObject(item) {
5003
+ return item && typeof item === 'object' && !Array.isArray(item);
5004
+ }
5005
+ function deepMerge(target, ...sources) {
5006
+ if (!isObject(target)) return target;
5007
+ target = deepClone(target);
5008
+ for (const source of sources) {
5009
+ if (isObject(source)) {
5010
+ for (const key in source) {
5011
+ if (isObject(source[key])) {
5012
+ if (!(key in target)) target[key] = {};
5013
+ target[key] = deepMerge(target[key], source[key]);
5014
+ } else if (source[key] !== undefined) {
5015
+ target[key] = deepClone(source[key]);
5016
+ }
5017
+ }
5018
+ }
5019
+ }
5020
+ return target;
5021
+ }
5022
+ function deepClone(obj) {
5023
+ let result = obj;
5024
+ if (obj && typeof obj == 'object') {
5025
+ result = Array.isArray(obj) ? [] : {};
5026
+ for (const key in obj) result[key] = deepClone(obj[key]);
5027
+ }
5028
+ return result;
5029
+ }
5030
+
5031
+ function normalizedDefaultStyle(defaultStyleInternal) {
5032
+ let defaultStyle = defaultStyleInternal;
5033
+ if (typeof defaultStyle !== 'object') defaultStyle = {
5034
+ text: defaultStyle
5035
+ };
5036
+ const defaultRowStyle = Object.fromEntries(Object.entries(defaultStyle).filter(([k]) => ROW_FIELDS.includes(k)));
5037
+ const defaultColStyle = Object.fromEntries(Object.entries(defaultStyle).filter(([k]) => COLUMN_FIELDS.includes(k)));
5038
+ defaultStyle.padding = normalizeSides(defaultStyle.padding);
5039
+ defaultStyle.border = normalizeSides(defaultStyle.border);
5040
+ defaultStyle.borderColor = normalizeSides(defaultStyle.borderColor);
5041
+ defaultStyle.align = normalizeAlignment(defaultStyle.align);
5042
+ return {
5043
+ defaultStyle,
5044
+ defaultRowStyle,
5045
+ defaultColStyle
5046
+ };
5047
+ }
5048
+ function normalizedRowStyle(defaultRowStyle, rowStyleInternal, i) {
5049
+ let rowStyle = rowStyleInternal(i);
5050
+ if (rowStyle == null || typeof rowStyle !== 'object') {
5051
+ rowStyle = {
5052
+ height: rowStyle
5053
+ };
5054
+ }
5055
+ rowStyle.padding = normalizeSides(rowStyle.padding);
5056
+ rowStyle.border = normalizeSides(rowStyle.border);
5057
+ rowStyle.borderColor = normalizeSides(rowStyle.borderColor);
5058
+ rowStyle.align = normalizeAlignment(rowStyle.align);
5059
+ rowStyle = deepMerge(defaultRowStyle, rowStyle);
5060
+ const document = this.document;
5061
+ const page = document.page;
5062
+ const contentHeight = page.contentHeight;
5063
+ if (rowStyle.height == null || rowStyle.height === 'auto') {
5064
+ rowStyle.height = 'auto';
5065
+ } else {
5066
+ rowStyle.height = document.sizeToPoint(rowStyle.height, 0, page, contentHeight);
5067
+ }
5068
+ rowStyle.minHeight = document.sizeToPoint(rowStyle.minHeight, 0, page, contentHeight);
5069
+ rowStyle.maxHeight = document.sizeToPoint(rowStyle.maxHeight, 0, page, contentHeight);
5070
+ return rowStyle;
5071
+ }
5072
+ function normalizedColumnStyle(defaultColStyle, colStyleInternal, i) {
5073
+ let colStyle = colStyleInternal(i);
5074
+ if (colStyle == null || typeof colStyle !== 'object') {
5075
+ colStyle = {
5076
+ width: colStyle
5077
+ };
5078
+ }
5079
+ colStyle.padding = normalizeSides(colStyle.padding);
5080
+ colStyle.border = normalizeSides(colStyle.border);
5081
+ colStyle.borderColor = normalizeSides(colStyle.borderColor);
5082
+ colStyle.align = normalizeAlignment(colStyle.align);
5083
+ colStyle = deepMerge(defaultColStyle, colStyle);
5084
+ if (colStyle.width == null || colStyle.width === '*') {
5085
+ colStyle.width = '*';
5086
+ } else {
5087
+ colStyle.width = this.document.sizeToPoint(colStyle.width, 0, this.document.page, this._maxWidth);
5088
+ }
5089
+ colStyle.minWidth = this.document.sizeToPoint(colStyle.minWidth, 0, this.document.page, this._maxWidth);
5090
+ colStyle.maxWidth = this.document.sizeToPoint(colStyle.maxWidth, 0, this.document.page, this._maxWidth);
5091
+ return colStyle;
5092
+ }
5093
+ function normalizeAlignment(align) {
5094
+ return align == null || typeof align === 'string' ? {
5095
+ x: align,
5096
+ y: align
5097
+ } : align;
5098
+ }
5099
+
5100
+ function normalizeTable() {
5101
+ const doc = this.document;
5102
+ const opts = this.opts;
5103
+ let index = doc._tableIndex++;
5104
+ this._id = new String(opts.id ?? `table-${index}`);
5105
+ this._position = {
5106
+ x: doc.sizeToPoint(opts.position?.x, doc.x),
5107
+ y: doc.sizeToPoint(opts.position?.y, doc.y)
5108
+ };
5109
+ this._maxWidth = doc.sizeToPoint(opts.maxWidth, doc.page.width - doc.page.margins.right - this._position.x);
5110
+ const {
5111
+ defaultStyle,
5112
+ defaultColStyle,
5113
+ defaultRowStyle
5114
+ } = normalizedDefaultStyle(opts.defaultStyle);
5115
+ this._defaultStyle = defaultStyle;
5116
+ let colStyle;
5117
+ if (opts.columnStyles) {
5118
+ if (Array.isArray(opts.columnStyles)) {
5119
+ colStyle = i => opts.columnStyles[i];
5120
+ } else if (typeof opts.columnStyles === 'function') {
5121
+ colStyle = memoize(i => opts.columnStyles(i), Infinity);
5122
+ } else if (typeof opts.columnStyles === 'object') {
5123
+ colStyle = () => opts.columnStyles;
5124
+ }
5125
+ }
5126
+ if (!colStyle) colStyle = () => ({});
5127
+ this._colStyle = normalizedColumnStyle.bind(this, defaultColStyle, colStyle);
5128
+ let rowStyle;
5129
+ if (opts.rowStyles) {
5130
+ if (Array.isArray(opts.rowStyles)) {
5131
+ rowStyle = i => opts.rowStyles[i];
5132
+ } else if (typeof opts.rowStyles === 'function') {
5133
+ rowStyle = memoize(i => opts.rowStyles(i), 10);
5134
+ } else if (typeof opts.rowStyles === 'object') {
5135
+ rowStyle = () => opts.rowStyles;
5136
+ }
5137
+ }
5138
+ if (!rowStyle) rowStyle = () => ({});
5139
+ this._rowStyle = normalizedRowStyle.bind(this, defaultRowStyle, rowStyle);
5140
+ }
5141
+ function normalizeText(text) {
5142
+ if (text != null) text = `${text}`;
5143
+ return text;
5144
+ }
5145
+ function normalizeCell(cell, rowIndex, colIndex) {
5146
+ const colStyle = this._colStyle(colIndex);
5147
+ let rowStyle = this._rowStyle(rowIndex);
5148
+ const font = deepMerge({}, colStyle.font, rowStyle.font, cell.font);
5149
+ const customFont = Object.values(font).filter(v => v != null).length > 0;
5150
+ const doc = this.document;
5151
+ const rollbackFont = doc._fontSource;
5152
+ const rollbackFontSize = doc._fontSize;
5153
+ const rollbackFontFamily = doc._fontFamily;
5154
+ if (customFont) {
5155
+ if (font.src) doc.font(font.src, font.family);
5156
+ if (font.size) doc.fontSize(font.size);
5157
+ rowStyle = this._rowStyle(rowIndex);
5158
+ }
5159
+ cell.padding = normalizeSides(cell.padding);
5160
+ cell.border = normalizeSides(cell.border);
5161
+ cell.borderColor = normalizeSides(cell.borderColor);
5162
+ const config = deepMerge(this._defaultStyle, colStyle, rowStyle, cell);
5163
+ config.rowIndex = rowIndex;
5164
+ config.colIndex = colIndex;
5165
+ config.font = font ?? {};
5166
+ config.customFont = customFont;
5167
+ config.text = normalizeText(config.text);
5168
+ config.rowSpan = config.rowSpan ?? 1;
5169
+ config.colSpan = config.colSpan ?? 1;
5170
+ config.padding = normalizeSides(config.padding, '0.25em', x => doc.sizeToPoint(x, '0.25em'));
5171
+ config.border = normalizeSides(config.border, 1, x => doc.sizeToPoint(x, 1));
5172
+ config.borderColor = normalizeSides(config.borderColor, 'black', x => x ?? 'black');
5173
+ config.align = normalizeAlignment(config.align);
5174
+ config.align.x = config.align.x ?? 'left';
5175
+ config.align.y = config.align.y ?? 'top';
5176
+ config.textStroke = doc.sizeToPoint(config.textStroke, 0);
5177
+ config.textStrokeColor = config.textStrokeColor ?? 'black';
5178
+ config.textColor = config.textColor ?? 'black';
5179
+ config.textOptions = config.textOptions ?? {};
5180
+ config.id = new String(config.id ?? `${this._id}-${rowIndex}-${colIndex}`);
5181
+ config.type = config.type?.toUpperCase() === 'TH' ? 'TH' : 'TD';
5182
+ if (config.scope) {
5183
+ config.scope = config.scope.toLowerCase();
5184
+ if (config.scope === 'row') config.scope = 'Row';else if (config.scope === 'both') config.scope = 'Both';else if (config.scope === 'column') config.scope = 'Column';
5185
+ }
5186
+ if (typeof this.opts.debug === 'boolean') config.debug = this.opts.debug;
5187
+ if (customFont) doc.font(rollbackFont, rollbackFontFamily, rollbackFontSize);
5188
+ return config;
5189
+ }
5190
+ function normalizeRow(row, rowIndex) {
5191
+ if (!this._cellClaim) this._cellClaim = new Set();
5192
+ let colIndex = 0;
5193
+ return row.map(cell => {
5194
+ if (cell == null || typeof cell !== 'object') cell = {
5195
+ text: cell
5196
+ };
5197
+ while (this._cellClaim.has(`${rowIndex},${colIndex}`)) {
5198
+ colIndex++;
5199
+ }
5200
+ cell = normalizeCell.call(this, cell, rowIndex, colIndex);
5201
+ for (let i = 0; i < cell.rowSpan; i++) {
5202
+ for (let j = 0; j < cell.colSpan; j++) {
5203
+ this._cellClaim.add(`${rowIndex + i},${colIndex + j}`);
5204
+ }
5205
+ }
5206
+ colIndex += cell.colSpan;
5207
+ return cell;
5208
+ });
5209
+ }
5210
+
5211
+ function ensure(row) {
5212
+ this._columnWidths = [];
5213
+ ensureColumnWidths.call(this, row.reduce((a, cell) => a + cell.colSpan, 0));
5214
+ this._rowHeights = [];
5215
+ this._rowYPos = [this._position.y];
5216
+ this._rowBuffer = new Set();
5217
+ }
5218
+ function ensureColumnWidths(numCols) {
5219
+ let starColumnIndexes = [];
5220
+ let starMinAcc = 0;
5221
+ let unclaimedWidth = this._maxWidth;
5222
+ for (let i = 0; i < numCols; i++) {
5223
+ let col = this._colStyle(i);
5224
+ if (col.width === '*') {
5225
+ starColumnIndexes[i] = col;
5226
+ starMinAcc += col.minWidth;
5227
+ } else {
5228
+ unclaimedWidth -= col.width;
5229
+ this._columnWidths[i] = col.width;
5230
+ }
5231
+ }
5232
+ let starColCount = starColumnIndexes.reduce(x => x + 1, 0);
5233
+ if (starMinAcc >= unclaimedWidth) {
5234
+ starColumnIndexes.forEach((cell, i) => {
5235
+ this._columnWidths[i] = cell.minWidth;
5236
+ });
5237
+ } else if (starColCount > 0) {
5238
+ starColumnIndexes.forEach((col, i) => {
5239
+ let starSize = unclaimedWidth / starColCount;
5240
+ this._columnWidths[i] = Math.max(starSize, col.minWidth);
5241
+ if (col.maxWidth > 0) {
5242
+ this._columnWidths[i] = Math.min(this._columnWidths[i], col.maxWidth);
5243
+ }
5244
+ unclaimedWidth -= this._columnWidths[i];
5245
+ starColCount--;
5246
+ });
5247
+ }
5248
+ let tempX = this._position.x;
5249
+ this._columnXPos = Array.from(this._columnWidths, v => {
5250
+ const t = tempX;
5251
+ tempX += v;
5252
+ return t;
5253
+ });
5254
+ }
5255
+ function measure(row, rowIndex) {
5256
+ row.forEach(cell => this._rowBuffer.add(cell));
5257
+ if (rowIndex > 0) {
5258
+ this._rowYPos[rowIndex] = this._rowYPos[rowIndex - 1] + this._rowHeights[rowIndex - 1];
5259
+ }
5260
+ const rowStyle = this._rowStyle(rowIndex);
5261
+ let toRender = [];
5262
+ this._rowBuffer.forEach(cell => {
5263
+ if (cell.rowIndex + cell.rowSpan - 1 === rowIndex) {
5264
+ toRender.push(measureCell.call(this, cell, rowStyle.height));
5265
+ this._rowBuffer.delete(cell);
5266
+ }
5267
+ });
5268
+ let rowHeight = rowStyle.height;
5269
+ if (rowHeight === 'auto') {
5270
+ rowHeight = toRender.reduce((acc, cell) => {
5271
+ let minHeight = cell.textBounds.height + cell.padding.top + cell.padding.bottom;
5272
+ for (let i = 0; i < cell.rowSpan - 1; i++) {
5273
+ minHeight -= this._rowHeights[cell.rowIndex + i];
5274
+ }
5275
+ return Math.max(acc, minHeight);
5276
+ }, 0);
5277
+ }
5278
+ rowHeight = Math.max(rowHeight, rowStyle.minHeight);
5279
+ if (rowStyle.maxHeight > 0) {
5280
+ rowHeight = Math.min(rowHeight, rowStyle.maxHeight);
5281
+ }
5282
+ this._rowHeights[rowIndex] = rowHeight;
5283
+ let newPage = false;
5284
+ if (rowHeight > this.document.page.contentHeight) {
5285
+ console.warn(new Error(`Row ${rowIndex} requested more than the safe page height, row has been clamped`).stack.slice(7));
5286
+ this._rowHeights[rowIndex] = this.document.page.maxY() - this._rowYPos[rowIndex];
5287
+ } else if (this._rowYPos[rowIndex] + rowHeight >= this.document.page.maxY()) {
5288
+ this._rowYPos[rowIndex] = this.document.page.margins.top;
5289
+ newPage = true;
5290
+ }
5291
+ return {
5292
+ newPage,
5293
+ toRender: toRender.map(cell => measureCell.call(this, cell, rowHeight))
5294
+ };
5295
+ }
5296
+ function measureCell(cell, rowHeight) {
5297
+ let cellWidth = 0;
5298
+ for (let i = 0; i < cell.colSpan; i++) {
5299
+ cellWidth += this._columnWidths[cell.colIndex + i];
5300
+ }
5301
+ let cellHeight = rowHeight;
5302
+ if (cellHeight === 'auto') {
5303
+ cellHeight = this.document.page.contentHeight;
5304
+ } else {
5305
+ for (let i = 0; i < cell.rowSpan - 1; i++) {
5306
+ cellHeight += this._rowHeights[cell.rowIndex + i];
5307
+ }
5308
+ }
5309
+ const textAllocatedWidth = cellWidth - cell.padding.left - cell.padding.right;
5310
+ const textAllocatedHeight = cellHeight - cell.padding.top - cell.padding.bottom;
5311
+ const rotation = cell.textOptions.rotation ?? 0;
5312
+ const {
5313
+ width: textMaxWidth,
5314
+ height: textMaxHeight
5315
+ } = computeBounds(rotation, textAllocatedWidth, textAllocatedHeight);
5316
+ const textOptions = {
5317
+ align: cell.align.x,
5318
+ ellipsis: true,
5319
+ stroke: cell.textStroke > 0,
5320
+ fill: true,
5321
+ width: textMaxWidth,
5322
+ height: textMaxHeight,
5323
+ rotation,
5324
+ ...cell.textOptions
5325
+ };
5326
+ let textBounds = {
5327
+ x: 0,
5328
+ y: 0,
5329
+ width: 0,
5330
+ height: 0
5331
+ };
5332
+ if (cell.text) {
5333
+ const rollbackFont = this.document._fontSource;
5334
+ const rollbackFontSize = this.document._fontSize;
5335
+ const rollbackFontFamily = this.document._fontFamily;
5336
+ if (cell.font?.src) this.document.font(cell.font.src, cell.font?.family);
5337
+ if (cell.font?.size) this.document.fontSize(cell.font.size);
5338
+ const unRotatedTextBounds = this.document.boundsOfString(cell.text, 0, 0, {
5339
+ ...textOptions,
5340
+ rotation: 0
5341
+ });
5342
+ textOptions.width = unRotatedTextBounds.width;
5343
+ textOptions.height = unRotatedTextBounds.height;
5344
+ textBounds = this.document.boundsOfString(cell.text, 0, 0, textOptions);
5345
+ this.document.font(rollbackFont, rollbackFontFamily, rollbackFontSize);
5346
+ }
5347
+ return {
5348
+ ...cell,
5349
+ textOptions,
5350
+ x: this._columnXPos[cell.colIndex],
5351
+ y: this._rowYPos[cell.rowIndex],
5352
+ textX: this._columnXPos[cell.colIndex] + cell.padding.left,
5353
+ textY: this._rowYPos[cell.rowIndex] + cell.padding.top,
5354
+ width: cellWidth,
5355
+ height: cellHeight,
5356
+ textAllocatedHeight,
5357
+ textAllocatedWidth,
5358
+ textBounds
5359
+ };
5360
+ }
5361
+ function computeBounds(rotation, allocWidth, allocHeight) {
5362
+ let textMaxWidth, textMaxHeight;
5363
+ const cos = cosine(rotation);
5364
+ const sin = sine(rotation);
5365
+ if (rotation === 0 || rotation === 180) {
5366
+ textMaxWidth = allocWidth;
5367
+ textMaxHeight = allocHeight;
5368
+ } else if (rotation === 90 || rotation === 270) {
5369
+ textMaxWidth = allocHeight;
5370
+ textMaxHeight = allocWidth;
5371
+ } else if (rotation < 90 || rotation > 180 && rotation < 270) {
5372
+ textMaxWidth = allocWidth / (2 * cos);
5373
+ textMaxHeight = allocWidth / (2 * sin);
5374
+ } else {
5375
+ textMaxHeight = allocWidth / (2 * cos);
5376
+ textMaxWidth = allocWidth / (2 * sin);
5377
+ }
5378
+ const EF = sin * textMaxWidth;
5379
+ const FG = cos * textMaxHeight;
5380
+ if (EF + FG > allocHeight) {
5381
+ const denominator = cos * cos - sin * sin;
5382
+ if (rotation === 0 || rotation === 180) {
5383
+ textMaxWidth = allocWidth;
5384
+ textMaxHeight = allocHeight;
5385
+ } else if (rotation === 90 || rotation === 270) {
5386
+ textMaxWidth = allocHeight;
5387
+ textMaxHeight = allocWidth;
5388
+ } else if (rotation < 90 || rotation > 180 && rotation < 270) {
5389
+ textMaxWidth = (allocWidth * cos - allocHeight * sin) / denominator;
5390
+ textMaxHeight = (allocHeight * cos - allocWidth * sin) / denominator;
5391
+ } else {
5392
+ textMaxHeight = (allocWidth * cos - allocHeight * sin) / denominator;
5393
+ textMaxWidth = (allocHeight * cos - allocWidth * sin) / denominator;
5394
+ }
5395
+ }
5396
+ return {
5397
+ width: Math.abs(textMaxWidth),
5398
+ height: Math.abs(textMaxHeight)
5399
+ };
5400
+ }
5401
+
5402
+ function accommodateTable() {
5403
+ const structParent = this.opts.structParent;
5404
+ if (structParent) {
5405
+ this._tableStruct = this.document.struct('Table');
5406
+ this._tableStruct.dictionary.data.ID = this._id;
5407
+ if (structParent instanceof PDFStructureElement) {
5408
+ structParent.add(this._tableStruct);
5409
+ } else if (structParent instanceof PDFDocument) {
5410
+ structParent.addStructure(this._tableStruct);
5411
+ }
5412
+ this._headerRowLookup = {};
5413
+ this._headerColumnLookup = {};
5414
+ }
5415
+ }
5416
+ function accommodateCleanup() {
5417
+ if (this._tableStruct) this._tableStruct.end();
5418
+ }
5419
+ function accessibleRow(row, rowIndex, renderCell) {
5420
+ const rowStruct = this.document.struct('TR');
5421
+ rowStruct.dictionary.data.ID = new String(`${this._id}-${rowIndex}`);
5422
+ this._tableStruct.add(rowStruct);
5423
+ row.forEach(cell => renderCell(cell, rowStruct));
5424
+ rowStruct.end();
5425
+ }
5426
+ function accessibleCell(cell, rowStruct, callback) {
5427
+ const doc = this.document;
5428
+ const cellStruct = doc.struct(cell.type, {
5429
+ title: cell.title
5430
+ });
5431
+ cellStruct.dictionary.data.ID = cell.id;
5432
+ rowStruct.add(cellStruct);
5433
+ const padding = cell.padding;
5434
+ const border = cell.border;
5435
+ const attributes = {
5436
+ O: 'Table',
5437
+ Width: cell.width,
5438
+ Height: cell.height,
5439
+ Padding: [padding.top, padding.bottom, padding.left, padding.right],
5440
+ RowSpan: cell.rowSpan > 1 ? cell.rowSpan : undefined,
5441
+ ColSpan: cell.colSpan > 1 ? cell.colSpan : undefined,
5442
+ BorderThickness: [border.top, border.bottom, border.left, border.right]
5443
+ };
5444
+ if (cell.type === 'TH') {
5445
+ if (cell.scope === 'Row' || cell.scope === 'Both') {
5446
+ for (let i = 0; i < cell.rowSpan; i++) {
5447
+ if (!this._headerRowLookup[cell.rowIndex + i]) {
5448
+ this._headerRowLookup[cell.rowIndex + i] = [];
5449
+ }
5450
+ this._headerRowLookup[cell.rowIndex + i].push(cell.id);
5451
+ }
5452
+ attributes.Scope = cell.scope;
5453
+ }
5454
+ if (cell.scope === 'Column' || cell.scope === 'Both') {
5455
+ for (let i = 0; i < cell.colSpan; i++) {
5456
+ if (!this._headerColumnLookup[cell.colIndex + i]) {
5457
+ this._headerColumnLookup[cell.colIndex + i] = [];
5458
+ }
5459
+ this._headerColumnLookup[cell.colIndex + i].push(cell.id);
5460
+ }
5461
+ attributes.Scope = cell.scope;
5462
+ }
5463
+ }
5464
+ const Headers = new Set([...Array.from({
5465
+ length: cell.colSpan
5466
+ }, (_, i) => this._headerColumnLookup[cell.colIndex + i]).flat(), ...Array.from({
5467
+ length: cell.rowSpan
5468
+ }, (_, i) => this._headerRowLookup[cell.rowIndex + i]).flat()].filter(Boolean));
5469
+ if (Headers.size) attributes.Headers = Array.from(Headers);
5470
+ const normalizeColor = doc._normalizeColor;
5471
+ if (cell.backgroundColor != null) {
5472
+ attributes.BackgroundColor = normalizeColor(cell.backgroundColor);
5473
+ }
5474
+ const hasBorder = [border.top, border.bottom, border.left, border.right];
5475
+ if (hasBorder.some(x => x)) {
5476
+ const borderColor = cell.borderColor;
5477
+ 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];
5478
+ }
5479
+ Object.keys(attributes).forEach(key => attributes[key] === undefined && delete attributes[key]);
5480
+ cellStruct.dictionary.data.A = doc.ref(attributes);
5481
+ cellStruct.add(callback);
5482
+ cellStruct.end();
5483
+ cellStruct.dictionary.data.A.end();
5484
+ }
5485
+
5486
+ function renderRow(row, rowIndex) {
5487
+ if (this._tableStruct) {
5488
+ accessibleRow.call(this, row, rowIndex, renderCell.bind(this));
5489
+ } else {
5490
+ row.forEach(cell => renderCell.call(this, cell));
5491
+ }
5492
+ return this._rowYPos[rowIndex] + this._rowHeights[rowIndex];
5493
+ }
5494
+ function renderCell(cell, rowStruct) {
5495
+ const cellRenderer = () => {
5496
+ if (cell.backgroundColor != null) {
5497
+ this.document.save().rect(cell.x, cell.y, cell.width, cell.height).fill(cell.backgroundColor).restore();
5498
+ }
5499
+ renderBorder.call(this, cell.border, cell.borderColor, cell.x, cell.y, cell.width, cell.height);
5500
+ if (cell.debug) {
5501
+ this.document.save();
5502
+ this.document.dash(1, {
5503
+ space: 1
5504
+ }).lineWidth(1).strokeOpacity(0.3);
5505
+ this.document.rect(cell.x, cell.y, cell.width, cell.height).stroke('green');
5506
+ this.document.restore();
5507
+ }
5508
+ if (cell.text) renderCellText.call(this, cell);
5509
+ };
5510
+ if (rowStruct) accessibleCell.call(this, cell, rowStruct, cellRenderer);else cellRenderer();
5511
+ }
5512
+ function renderCellText(cell) {
5513
+ const doc = this.document;
5514
+ const rollbackFont = doc._fontSource;
5515
+ const rollbackFontSize = doc._fontSize;
5516
+ const rollbackFontFamily = doc._fontFamily;
5517
+ if (cell.customFont) {
5518
+ if (cell.font.src) doc.font(cell.font.src, cell.font.family);
5519
+ if (cell.font.size) doc.fontSize(cell.font.size);
5520
+ }
5521
+ const x = cell.textX;
5522
+ const y = cell.textY;
5523
+ const Ah = cell.textAllocatedHeight;
5524
+ const Aw = cell.textAllocatedWidth;
5525
+ const Cw = cell.textBounds.width;
5526
+ const Ch = cell.textBounds.height;
5527
+ const Ox = -cell.textBounds.x;
5528
+ const Oy = -cell.textBounds.y;
5529
+ const PxScale = cell.align.x === 'right' ? 1 : cell.align.x === 'center' ? 0.5 : 0;
5530
+ const Px = (Aw - Cw) * PxScale;
5531
+ const PyScale = cell.align.y === 'bottom' ? 1 : cell.align.y === 'center' ? 0.5 : 0;
5532
+ const Py = (Ah - Ch) * PyScale;
5533
+ const dx = Px + Ox;
5534
+ const dy = Py + Oy;
5535
+ if (cell.debug) {
5536
+ doc.save();
5537
+ doc.dash(1, {
5538
+ space: 1
5539
+ }).lineWidth(1).strokeOpacity(0.3);
5540
+ if (cell.text) {
5541
+ 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');
5542
+ }
5543
+ doc.rect(x, y, Aw, Ah).stroke('orange');
5544
+ doc.restore();
5545
+ }
5546
+ doc.save().rect(x, y, Aw, Ah).clip();
5547
+ doc.fillColor(cell.textColor).strokeColor(cell.textStrokeColor);
5548
+ if (cell.textStroke > 0) doc.lineWidth(cell.textStroke);
5549
+ doc.text(cell.text, x + dx, y + dy, cell.textOptions);
5550
+ doc.restore();
5551
+ if (cell.font) doc.font(rollbackFont, rollbackFontFamily, rollbackFontSize);
5552
+ }
5553
+ function renderBorder(border, borderColor, x, y, width, height, mask) {
5554
+ border = Object.fromEntries(Object.entries(border).map(([k, v]) => [k, mask && !mask[k] ? 0 : v]));
5555
+ const doc = this.document;
5556
+ if ([border.right, border.bottom, border.left].every(val => val === border.top)) {
5557
+ if (border.top > 0) {
5558
+ doc.save().lineWidth(border.top).rect(x, y, width, height).stroke(borderColor.top).restore();
5559
+ }
5560
+ } else {
5561
+ if (border.top > 0) {
5562
+ doc.save().lineWidth(border.top).moveTo(x, y).lineTo(x + width, y).stroke(borderColor.top).restore();
5563
+ }
5564
+ if (border.right > 0) {
5565
+ doc.save().lineWidth(border.right).moveTo(x + width, y).lineTo(x + width, y + height).stroke(borderColor.right).restore();
5566
+ }
5567
+ if (border.bottom > 0) {
5568
+ doc.save().lineWidth(border.bottom).moveTo(x + width, y + height).lineTo(x, y + height).stroke(borderColor.bottom).restore();
5569
+ }
5570
+ if (border.left > 0) {
5571
+ doc.save().lineWidth(border.left).moveTo(x, y + height).lineTo(x, y).stroke(borderColor.left).restore();
5572
+ }
5573
+ }
5574
+ }
5575
+
5576
+ class PDFTable {
5577
+ constructor(document, opts = {}) {
5578
+ this.document = document;
5579
+ this.opts = Object.freeze(opts);
5580
+ normalizeTable.call(this);
5581
+ accommodateTable.call(this);
5582
+ this._currRowIndex = 0;
5583
+ this._ended = false;
5584
+ if (opts.data) {
5585
+ for (const row of opts.data) this.row(row);
5586
+ return this.end();
5587
+ }
5588
+ }
5589
+ row(row, lastRow = false) {
5590
+ if (this._ended) {
5591
+ throw new Error(`Table was marked as ended on row ${this._currRowIndex}`);
5592
+ }
5593
+ row = Array.from(row);
5594
+ row = normalizeRow.call(this, row, this._currRowIndex);
5595
+ if (this._currRowIndex === 0) ensure.call(this, row);
5596
+ const {
5597
+ newPage,
5598
+ toRender
5599
+ } = measure.call(this, row, this._currRowIndex);
5600
+ if (newPage) this.document.continueOnNewPage();
5601
+ const yPos = renderRow.call(this, toRender, this._currRowIndex);
5602
+ this.document.x = this._position.x;
5603
+ this.document.y = yPos;
5604
+ if (lastRow) return this.end();
5605
+ this._currRowIndex++;
5606
+ return this;
5607
+ }
5608
+ end() {
5609
+ while (this._rowBuffer?.size) this.row([]);
5610
+ this._ended = true;
5611
+ accommodateCleanup.call(this);
5612
+ return this.document;
5613
+ }
5614
+ }
5615
+
5616
+ var TableMixin = {
5617
+ initTables() {
5618
+ this._tableIndex = 0;
5619
+ },
5620
+ table(opts) {
5621
+ return new PDFTable(this, opts);
5622
+ }
5623
+ };
5624
+
5385
5625
  class PDFMetadata {
5386
5626
  constructor() {
5387
5627
  this._metadata = `
@@ -5423,7 +5663,7 @@ var MetadataMixin = {
5423
5663
  _addInfo() {
5424
5664
  this.appendXML(`
5425
5665
  <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>
5666
+ <xmp:CreateDate>${this.info.CreationDate.toISOString().split('.')[0] + 'Z'}</xmp:CreateDate>
5427
5667
  <xmp:CreatorTool>${this.info.Creator}</xmp:CreatorTool>
5428
5668
  </rdf:Description>
5429
5669
  `);
@@ -5476,11 +5716,6 @@ var MetadataMixin = {
5476
5716
  endMetadata() {
5477
5717
  this._addInfo();
5478
5718
  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
5719
  if (this.version != 1.3) {
5485
5720
  this.metadataRef = this.ref({
5486
5721
  length: this.metadata.getLength(),
@@ -5495,16 +5730,10 @@ var MetadataMixin = {
5495
5730
  }
5496
5731
  };
5497
5732
 
5498
- /*
5499
- PDFDocument - represents an entire PDF document
5500
- By Devon Govett
5501
- */
5502
5733
  class PDFDocument extends stream.Readable {
5503
5734
  constructor(options = {}) {
5504
5735
  super(options);
5505
5736
  this.options = options;
5506
-
5507
- // PDF version
5508
5737
  switch (options.pdfVersion) {
5509
5738
  case '1.4':
5510
5739
  this.version = 1.4;
@@ -5523,13 +5752,9 @@ class PDFDocument extends stream.Readable {
5523
5752
  this.version = 1.3;
5524
5753
  break;
5525
5754
  }
5526
-
5527
- // Whether streams should be compressed
5528
5755
  this.compress = this.options.compress != null ? this.options.compress : true;
5529
5756
  this._pageBuffer = [];
5530
5757
  this._pageBufferStart = 0;
5531
-
5532
- // The PDF object store
5533
5758
  this._offsets = [];
5534
5759
  this._waiting = 0;
5535
5760
  this._ended = false;
@@ -5550,11 +5775,7 @@ class PDFDocument extends stream.Readable {
5550
5775
  if (this.options.lang) {
5551
5776
  this._root.data.Lang = new String(this.options.lang);
5552
5777
  }
5553
-
5554
- // The current page
5555
5778
  this.page = null;
5556
-
5557
- // Initialize mixins
5558
5779
  this.initMetadata();
5559
5780
  this.initColor();
5560
5781
  this.initVector();
@@ -5563,9 +5784,8 @@ class PDFDocument extends stream.Readable {
5563
5784
  this.initImages();
5564
5785
  this.initOutline();
5565
5786
  this.initMarkings(options);
5787
+ this.initTables();
5566
5788
  this.initSubset(options);
5567
-
5568
- // Initialize the metadata
5569
5789
  this.info = {
5570
5790
  Producer: 'PDFKit',
5571
5791
  Creator: 'PDFKit',
@@ -5582,21 +5802,10 @@ class PDFDocument extends stream.Readable {
5582
5802
  DisplayDocTitle: true
5583
5803
  });
5584
5804
  }
5585
-
5586
- // Generate file ID
5587
5805
  this._id = PDFSecurity.generateFileID(this.info);
5588
-
5589
- // Initialize security settings
5590
5806
  this._security = PDFSecurity.create(this, options);
5591
-
5592
- // Write the header
5593
- // PDF version
5594
5807
  this._write(`%PDF-${this.version}`);
5595
-
5596
- // 4 binary chars, as recommended by the spec
5597
5808
  this._write('%\xFF\xFF\xFF\xFF');
5598
-
5599
- // Add the first page
5600
5809
  if (this.options.autoFirstPage !== false) {
5601
5810
  this.addPage();
5602
5811
  }
@@ -5607,27 +5816,16 @@ class PDFDocument extends stream.Readable {
5607
5816
  options
5608
5817
  } = this);
5609
5818
  }
5610
-
5611
- // end the current page if needed
5612
5819
  if (!this.options.bufferPages) {
5613
5820
  this.flushPages();
5614
5821
  }
5615
-
5616
- // create a page object
5617
5822
  this.page = new PDFPage(this, options);
5618
5823
  this._pageBuffer.push(this.page);
5619
-
5620
- // add the page to the object store
5621
5824
  const pages = this._root.data.Pages.data;
5622
5825
  pages.Kids.push(this.page.dictionary);
5623
5826
  pages.Count++;
5624
-
5625
- // reset x and y coordinates
5626
5827
  this.x = this.page.margins.left;
5627
5828
  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
5829
  this._ctm = [1, 0, 0, 1, 0, 0];
5632
5830
  this.transform(1, 0, 0, -1, 0, this.page.height);
5633
5831
  this.emit('pageAdded');
@@ -5635,7 +5833,7 @@ class PDFDocument extends stream.Readable {
5635
5833
  }
5636
5834
  continueOnNewPage(options) {
5637
5835
  const pageMarkings = this.endPageMarkings(this.page);
5638
- this.addPage(options);
5836
+ this.addPage(options ?? this.page._options);
5639
5837
  this.initPageMarkings(pageMarkings);
5640
5838
  return this;
5641
5839
  }
@@ -5653,8 +5851,6 @@ class PDFDocument extends stream.Readable {
5653
5851
  return this.page = page;
5654
5852
  }
5655
5853
  flushPages() {
5656
- // this local variable exists so we're future-proof against
5657
- // reentrant calls to flushPages.
5658
5854
  const pages = this._pageBuffer;
5659
5855
  this._pageBuffer = [];
5660
5856
  this._pageBufferStart += pages.length;
@@ -5675,13 +5871,10 @@ class PDFDocument extends stream.Readable {
5675
5871
  }
5676
5872
  addNamedEmbeddedFile(name, ref) {
5677
5873
  if (!this._root.data.Names.data.EmbeddedFiles) {
5678
- // disabling /Limits for this tree fixes attachments not showing in Adobe Reader
5679
5874
  this._root.data.Names.data.EmbeddedFiles = new PDFNameTree({
5680
5875
  limits: false
5681
5876
  });
5682
5877
  }
5683
-
5684
- // add filespec to EmbeddedFiles
5685
5878
  this._root.data.Names.data.EmbeddedFiles.add(name, ref);
5686
5879
  }
5687
5880
  addNamedJavaScript(name, js) {
@@ -5696,19 +5889,17 @@ class PDFDocument extends stream.Readable {
5696
5889
  }
5697
5890
  ref(data) {
5698
5891
  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
5892
+ this._offsets.push(null);
5700
5893
  this._waiting++;
5701
5894
  return ref;
5702
5895
  }
5703
5896
  _read() {}
5704
- // do nothing, but this method is required by node
5705
-
5706
5897
  _write(data) {
5707
5898
  if (!Buffer.isBuffer(data)) {
5708
5899
  data = Buffer.from(data + '\n', 'binary');
5709
5900
  }
5710
5901
  this.push(data);
5711
- return this._offset += data.length;
5902
+ this._offset += data.length;
5712
5903
  }
5713
5904
  addContent(data) {
5714
5905
  this.page.write(data);
@@ -5718,7 +5909,7 @@ class PDFDocument extends stream.Readable {
5718
5909
  this._offsets[ref.id - 1] = ref.offset;
5719
5910
  if (--this._waiting === 0 && this._ended) {
5720
5911
  this._finalize();
5721
- return this._ended = false;
5912
+ this._ended = false;
5722
5913
  }
5723
5914
  }
5724
5915
  end() {
@@ -5755,13 +5946,12 @@ class PDFDocument extends stream.Readable {
5755
5946
  this._security.end();
5756
5947
  }
5757
5948
  if (this._waiting === 0) {
5758
- return this._finalize();
5949
+ this._finalize();
5759
5950
  } else {
5760
- return this._ended = true;
5951
+ this._ended = true;
5761
5952
  }
5762
5953
  }
5763
5954
  _finalize() {
5764
- // generate xref
5765
5955
  const xRefOffset = this._offset;
5766
5956
  this._write('xref');
5767
5957
  this._write(`0 ${this._offsets.length + 1}`);
@@ -5770,8 +5960,6 @@ class PDFDocument extends stream.Readable {
5770
5960
  offset = `0000000000${offset}`.slice(-10);
5771
5961
  this._write(offset + ' 00000 n ');
5772
5962
  }
5773
-
5774
- // trailer
5775
5963
  const trailer = {
5776
5964
  Size: this._offsets.length + 1,
5777
5965
  Root: this._root,
@@ -5786,9 +5974,7 @@ class PDFDocument extends stream.Readable {
5786
5974
  this._write('startxref');
5787
5975
  this._write(`${xRefOffset}`);
5788
5976
  this._write('%%EOF');
5789
-
5790
- // end the stream
5791
- return this.push(null);
5977
+ this.push(null);
5792
5978
  }
5793
5979
  toString() {
5794
5980
  return '[object PDFDocument]';
@@ -5809,6 +5995,7 @@ mixin(MarkingsMixin);
5809
5995
  mixin(AcroFormMixin);
5810
5996
  mixin(AttachmentsMixin);
5811
5997
  mixin(SubsetMixin);
5998
+ mixin(TableMixin);
5812
5999
  PDFDocument.LineWrapper = LineWrapper;
5813
6000
 
5814
6001
  module.exports = PDFDocument;