pdfkit 0.15.2 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/js/pdfkit.es.js CHANGED
@@ -2,30 +2,22 @@ import stream from 'stream';
2
2
  import zlib from 'zlib';
3
3
  import CryptoJS from 'crypto-js';
4
4
  import fs from 'fs';
5
- import fontkit from 'fontkit';
5
+ import * as fontkit from 'fontkit';
6
6
  import { EventEmitter } from 'events';
7
7
  import LineBreaker from 'linebreak';
8
8
  import exif from 'jpeg-exif';
9
9
  import PNG from 'png-js';
10
10
 
11
- /*
12
- PDFAbstractReference - abstract class for PDF reference
13
- */
14
-
15
11
  class PDFAbstractReference {
16
12
  toString() {
17
13
  throw new Error('Must be implemented by subclasses');
18
14
  }
19
15
  }
20
16
 
21
- /*
22
- PDFTree - abstract base class for name and number tree objects
23
- */
24
17
  class PDFTree {
25
18
  constructor() {
26
19
  let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
27
20
  this._items = {};
28
- // disable /Limits output for this tree
29
21
  this.limits = typeof options.limits === 'boolean' ? options.limits : true;
30
22
  }
31
23
  add(key, val) {
@@ -35,7 +27,6 @@ class PDFTree {
35
27
  return this._items[key];
36
28
  }
37
29
  toString() {
38
- // Needs to be sorted by key
39
30
  const sortedKeys = Object.keys(this._items).sort((a, b) => this._compareKeys(a, b));
40
31
  const out = ['<<'];
41
32
  if (this.limits && sortedKeys.length > 1) {
@@ -51,23 +42,37 @@ class PDFTree {
51
42
  out.push('>>');
52
43
  return out.join('\n');
53
44
  }
54
- _compareKeys( /*a, b*/
55
- ) {
45
+ _compareKeys() {
56
46
  throw new Error('Must be implemented by subclasses');
57
47
  }
58
48
  _keysName() {
59
49
  throw new Error('Must be implemented by subclasses');
60
50
  }
61
- _dataForKey( /*k*/
62
- ) {
51
+ _dataForKey() {
63
52
  throw new Error('Must be implemented by subclasses');
64
53
  }
65
54
  }
66
55
 
67
- /*
68
- PDFObject - converts JavaScript types into their corresponding PDF types.
69
- By Devon Govett
70
- */
56
+ class SpotColor {
57
+ constructor(doc, name, C, M, Y, K) {
58
+ this.id = 'CS' + Object.keys(doc.spotColors).length;
59
+ this.name = name;
60
+ this.values = [C, M, Y, K];
61
+ this.ref = doc.ref(['Separation', this.name, 'DeviceCMYK', {
62
+ Range: [0, 1, 0, 1, 0, 1, 0, 1],
63
+ C0: [0, 0, 0, 0],
64
+ C1: this.values.map(value => value / 100),
65
+ FunctionType: 2,
66
+ Domain: [0, 1],
67
+ N: 1
68
+ }]);
69
+ this.ref.end();
70
+ }
71
+ toString() {
72
+ return `${this.ref.id} 0 R`;
73
+ }
74
+ }
75
+
71
76
  const pad = (str, length) => (Array(length + 1).join('0') + str).slice(-length);
72
77
  const escapableRe = /[\n\r\t\b\f()\\]/g;
73
78
  const escapable = {
@@ -80,8 +85,6 @@ const escapable = {
80
85
  '(': '\\(',
81
86
  ')': '\\)'
82
87
  };
83
-
84
- // Convert little endian UTF-16 to big endian
85
88
  const swapBytes = function (buff) {
86
89
  const l = buff.length;
87
90
  if (l & 0x01) {
@@ -98,14 +101,10 @@ const swapBytes = function (buff) {
98
101
  class PDFObject {
99
102
  static convert(object) {
100
103
  let encryptFn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
101
- // String literals are converted to the PDF name type
102
104
  if (typeof object === 'string') {
103
105
  return `/${object}`;
104
-
105
- // String objects are converted to PDF strings (UTF-16)
106
106
  } else if (object instanceof String) {
107
107
  let string = object;
108
- // Detect if this is a unicode string
109
108
  let isUnicode = false;
110
109
  for (let i = 0, end = string.length; i < end; i++) {
111
110
  if (string.charCodeAt(i) > 0x7f) {
@@ -113,39 +112,27 @@ class PDFObject {
113
112
  break;
114
113
  }
115
114
  }
116
-
117
- // If so, encode it as big endian UTF-16
118
115
  let stringBuffer;
119
116
  if (isUnicode) {
120
117
  stringBuffer = swapBytes(Buffer.from(`\ufeff${string}`, 'utf16le'));
121
118
  } else {
122
119
  stringBuffer = Buffer.from(string.valueOf(), 'ascii');
123
120
  }
124
-
125
- // Encrypt the string when necessary
126
121
  if (encryptFn) {
127
122
  string = encryptFn(stringBuffer).toString('binary');
128
123
  } else {
129
124
  string = stringBuffer.toString('binary');
130
125
  }
131
-
132
- // Escape characters as required by the spec
133
126
  string = string.replace(escapableRe, c => escapable[c]);
134
127
  return `(${string})`;
135
-
136
- // Buffers are converted to PDF hex strings
137
128
  } else if (Buffer.isBuffer(object)) {
138
129
  return `<${object.toString('hex')}>`;
139
- } else if (object instanceof PDFAbstractReference || object instanceof PDFTree) {
130
+ } else if (object instanceof PDFAbstractReference || object instanceof PDFTree || object instanceof SpotColor) {
140
131
  return object.toString();
141
132
  } else if (object instanceof Date) {
142
133
  let string = `D:${pad(object.getUTCFullYear(), 4)}` + pad(object.getUTCMonth() + 1, 2) + pad(object.getUTCDate(), 2) + pad(object.getUTCHours(), 2) + pad(object.getUTCMinutes(), 2) + pad(object.getUTCSeconds(), 2) + 'Z';
143
-
144
- // Encrypt the string when necessary
145
134
  if (encryptFn) {
146
135
  string = encryptFn(Buffer.from(string, 'ascii')).toString('binary');
147
-
148
- // Escape characters as required by the spec
149
136
  string = string.replace(escapableRe, c => escapable[c]);
150
137
  }
151
138
  return `(${string})`;
@@ -174,10 +161,6 @@ class PDFObject {
174
161
  }
175
162
  }
176
163
 
177
- /*
178
- PDFReference - represents a reference to another object in the PDF object heirarchy
179
- By Devon Govett
180
- */
181
164
  class PDFReference extends PDFAbstractReference {
182
165
  constructor(document, id) {
183
166
  let data = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
@@ -191,7 +174,7 @@ class PDFReference extends PDFAbstractReference {
191
174
  this.buffer = [];
192
175
  }
193
176
  write(chunk) {
194
- if (!Buffer.isBuffer(chunk)) {
177
+ if (!(chunk instanceof Uint8Array)) {
195
178
  chunk = Buffer.from(chunk + '\n', 'binary');
196
179
  }
197
180
  this.uncompressedLength += chunk.length;
@@ -201,14 +184,14 @@ class PDFReference extends PDFAbstractReference {
201
184
  this.buffer.push(chunk);
202
185
  this.data.Length += chunk.length;
203
186
  if (this.compress) {
204
- return this.data.Filter = 'FlateDecode';
187
+ this.data.Filter = 'FlateDecode';
205
188
  }
206
189
  }
207
190
  end(chunk) {
208
191
  if (chunk) {
209
192
  this.write(chunk);
210
193
  }
211
- return this.finalize();
194
+ this.finalize();
212
195
  }
213
196
  finalize() {
214
197
  this.offset = this.document._offset;
@@ -228,7 +211,7 @@ class PDFReference extends PDFAbstractReference {
228
211
  if (this.buffer.length) {
229
212
  this.document._write('stream');
230
213
  this.document._write(this.buffer);
231
- this.buffer = []; // free up memory
214
+ this.buffer = [];
232
215
  this.document._write('\nendstream');
233
216
  }
234
217
  this.document._write('endobj');
@@ -239,10 +222,71 @@ class PDFReference extends PDFAbstractReference {
239
222
  }
240
223
  }
241
224
 
242
- /*
243
- PDFPage - represents a single page in the PDF document
244
- By Devon Govett
245
- */
225
+ function PDFNumber(n) {
226
+ return Math.fround(n);
227
+ }
228
+ function normalizeSides(sides) {
229
+ let defaultDefinition = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
230
+ let transformer = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : v => v;
231
+ if (sides == null || typeof sides === 'object' && Object.keys(sides).length === 0) {
232
+ sides = defaultDefinition;
233
+ }
234
+ if (sides == null || typeof sides !== 'object') {
235
+ sides = {
236
+ top: sides,
237
+ right: sides,
238
+ bottom: sides,
239
+ left: sides
240
+ };
241
+ } else if (Array.isArray(sides)) {
242
+ if (sides.length === 2) {
243
+ sides = {
244
+ vertical: sides[0],
245
+ horizontal: sides[1]
246
+ };
247
+ } else {
248
+ sides = {
249
+ top: sides[0],
250
+ right: sides[1],
251
+ bottom: sides[2],
252
+ left: sides[3]
253
+ };
254
+ }
255
+ }
256
+ if ('vertical' in sides || 'horizontal' in sides) {
257
+ sides = {
258
+ top: sides.vertical,
259
+ right: sides.horizontal,
260
+ bottom: sides.vertical,
261
+ left: sides.horizontal
262
+ };
263
+ }
264
+ return {
265
+ top: transformer(sides.top),
266
+ right: transformer(sides.right),
267
+ bottom: transformer(sides.bottom),
268
+ left: transformer(sides.left)
269
+ };
270
+ }
271
+ const MM_TO_CM = 1 / 10;
272
+ const CM_TO_IN = 1 / 2.54;
273
+ const PX_TO_IN = 1 / 96;
274
+ const IN_TO_PT = 72;
275
+ const PC_TO_PT = 12;
276
+ function cosine(a) {
277
+ if (a === 0) return 1;
278
+ if (a === 90) return 0;
279
+ if (a === 180) return -1;
280
+ if (a === 270) return 0;
281
+ return Math.cos(a * Math.PI / 180);
282
+ }
283
+ function sine(a) {
284
+ if (a === 0) return 0;
285
+ if (a === 90) return 1;
286
+ if (a === 180) return 0;
287
+ if (a === 270) return -1;
288
+ return Math.sin(a * Math.PI / 180);
289
+ }
246
290
 
247
291
  const DEFAULT_MARGINS = {
248
292
  top: 72,
@@ -306,35 +350,19 @@ class PDFPage {
306
350
  constructor(document) {
307
351
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
308
352
  this.document = document;
353
+ this._options = options;
309
354
  this.size = options.size || 'letter';
310
355
  this.layout = options.layout || 'portrait';
311
-
312
- // process margins
313
- if (typeof options.margin === 'number') {
314
- this.margins = {
315
- top: options.margin,
316
- left: options.margin,
317
- bottom: options.margin,
318
- right: options.margin
319
- };
320
-
321
- // default to 1 inch margins
322
- } else {
323
- this.margins = options.margins || DEFAULT_MARGINS;
324
- }
325
-
326
- // calculate page dimensions
327
356
  const dimensions = Array.isArray(this.size) ? this.size : SIZES[this.size.toUpperCase()];
328
357
  this.width = dimensions[this.layout === 'portrait' ? 0 : 1];
329
358
  this.height = dimensions[this.layout === 'portrait' ? 1 : 0];
330
359
  this.content = this.document.ref();
331
-
332
- // Initialize the Font, XObject, and ExtGState dictionaries
360
+ if (options.font) document.font(options.font, options.fontFamily);
361
+ if (options.fontSize) document.fontSize(options.fontSize);
362
+ this.margins = normalizeSides(options.margin ?? options.margins, DEFAULT_MARGINS, x => document.sizeToPoint(x, 0, this));
333
363
  this.resources = this.document.ref({
334
364
  ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI']
335
365
  });
336
-
337
- // The page dictionary
338
366
  this.dictionary = this.document.ref({
339
367
  Type: 'Page',
340
368
  Parent: this.document._root.data.Pages,
@@ -344,8 +372,6 @@ class PDFPage {
344
372
  });
345
373
  this.markings = [];
346
374
  }
347
-
348
- // Lazily create these objects
349
375
  get fonts() {
350
376
  const data = this.resources.data;
351
377
  return data.Font != null ? data.Font : data.Font = {};
@@ -374,204 +400,86 @@ class PDFPage {
374
400
  const data = this.dictionary.data;
375
401
  return data.StructParents != null ? data.StructParents : data.StructParents = this.document.createStructParentTreeNextKey();
376
402
  }
403
+ get contentWidth() {
404
+ return this.width - this.margins.left - this.margins.right;
405
+ }
406
+ get contentHeight() {
407
+ return this.height - this.margins.top - this.margins.bottom;
408
+ }
377
409
  maxY() {
378
410
  return this.height - this.margins.bottom;
379
411
  }
380
412
  write(chunk) {
381
413
  return this.content.write(chunk);
382
414
  }
415
+ _setTabOrder() {
416
+ if (!this.dictionary.Tabs && this.document.hasMarkInfoDictionary()) {
417
+ this.dictionary.data.Tabs = 'S';
418
+ }
419
+ }
383
420
  end() {
421
+ this._setTabOrder();
384
422
  this.dictionary.end();
423
+ this.resources.data.ColorSpace = this.resources.data.ColorSpace || {};
424
+ for (let color of Object.values(this.document.spotColors)) {
425
+ this.resources.data.ColorSpace[color.id] = color;
426
+ }
385
427
  this.resources.end();
386
428
  return this.content.end();
387
429
  }
388
430
  }
389
431
 
390
- /*
391
- PDFNameTree - represents a name tree object
392
- */
393
432
  class PDFNameTree extends PDFTree {
394
433
  _compareKeys(a, b) {
395
434
  return a.localeCompare(b);
396
435
  }
397
436
  _keysName() {
398
- return "Names";
437
+ return 'Names';
399
438
  }
400
439
  _dataForKey(k) {
401
440
  return new String(k);
402
441
  }
403
442
  }
404
443
 
405
- /**
406
- * Check if value is in a range group.
407
- * @param {number} value
408
- * @param {number[]} rangeGroup
409
- * @returns {boolean}
410
- */
411
444
  function inRange(value, rangeGroup) {
412
445
  if (value < rangeGroup[0]) return false;
413
446
  let startRange = 0;
414
447
  let endRange = rangeGroup.length / 2;
415
448
  while (startRange <= endRange) {
416
449
  const middleRange = Math.floor((startRange + endRange) / 2);
417
-
418
- // actual array index
419
450
  const arrayIndex = middleRange * 2;
420
-
421
- // Check if value is in range pointed by actual index
422
451
  if (value >= rangeGroup[arrayIndex] && value <= rangeGroup[arrayIndex + 1]) {
423
452
  return true;
424
453
  }
425
454
  if (value > rangeGroup[arrayIndex + 1]) {
426
- // Search Right Side Of Array
427
455
  startRange = middleRange + 1;
428
456
  } else {
429
- // Search Left Side Of Array
430
457
  endRange = middleRange - 1;
431
458
  }
432
459
  }
433
460
  return false;
434
461
  }
435
462
 
436
- // prettier-ignore-start
437
- /**
438
- * A.1 Unassigned code points in Unicode 3.2
439
- * @link https://tools.ietf.org/html/rfc3454#appendix-A.1
440
- */
441
463
  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];
442
- // prettier-ignore-end
443
-
444
464
  const isUnassignedCodePoint = character => inRange(character, unassigned_code_points);
445
-
446
- // prettier-ignore-start
447
- /**
448
- * B.1 Commonly mapped to nothing
449
- * @link https://tools.ietf.org/html/rfc3454#appendix-B.1
450
- */
451
465
  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];
452
- // prettier-ignore-end
453
-
454
466
  const isCommonlyMappedToNothing = character => inRange(character, commonly_mapped_to_nothing);
455
-
456
- // prettier-ignore-start
457
- /**
458
- * C.1.2 Non-ASCII space characters
459
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.1.2
460
- */
461
- 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 */];
462
- // prettier-ignore-end
463
-
467
+ 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];
464
468
  const isNonASCIISpaceCharacter = character => inRange(character, non_ASCII_space_characters);
465
-
466
- // prettier-ignore-start
467
- const non_ASCII_controls_characters = [
468
- /**
469
- * C.2.2 Non-ASCII control characters
470
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.2.2
471
- */
472
- 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] */];
473
- const non_character_codepoints = [
474
- /**
475
- * C.4 Non-character code points
476
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.4
477
- */
478
- 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] */];
479
-
480
- /**
481
- * 2.3. Prohibited Output
482
- */
483
- const prohibited_characters = [
484
- /**
485
- * C.2.1 ASCII control characters
486
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.2.1
487
- */
488
- 0, 0x001f /* [CONTROL CHARACTERS] */, 0x007f, 0x007f /* DELETE */,
489
- /**
490
- * C.8 Change display properties or are deprecated
491
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.8
492
- */
493
- 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 */,
494
- /**
495
- * C.7 Inappropriate for canonical representation
496
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.7
497
- */
498
- 0x2ff0, 0x2ffb /* [IDEOGRAPHIC DESCRIPTION CHARACTERS] */,
499
- /**
500
- * C.5 Surrogate codes
501
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.5
502
- */
503
- 0xd800, 0xdfff,
504
- /**
505
- * C.3 Private use
506
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.3
507
- */
508
- 0xe000, 0xf8ff /* [PRIVATE USE, PLANE 0] */,
509
- /**
510
- * C.6 Inappropriate for plain text
511
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.6
512
- */
513
- 0xfff9, 0xfff9 /* INTERLINEAR ANNOTATION ANCHOR */, 0xfffa, 0xfffa /* INTERLINEAR ANNOTATION SEPARATOR */, 0xfffb, 0xfffb /* INTERLINEAR ANNOTATION TERMINATOR */, 0xfffc, 0xfffc /* OBJECT REPLACEMENT CHARACTER */, 0xfffd, 0xfffd /* REPLACEMENT CHARACTER */,
514
- /**
515
- * C.9 Tagging characters
516
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.9
517
- */
518
- 0xe0001, 0xe0001 /* LANGUAGE TAG */, 0xe0020, 0xe007f /* [TAGGING CHARACTERS] */,
519
- /**
520
- * C.3 Private use
521
- * @link https://tools.ietf.org/html/rfc3454#appendix-C.3
522
- */
523
-
524
- 0xf0000, 0xffffd /* [PRIVATE USE, PLANE 15] */, 0x100000, 0x10fffd /* [PRIVATE USE, PLANE 16] */];
525
- // prettier-ignore-end
526
-
469
+ 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];
470
+ 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];
471
+ 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];
527
472
  const isProhibitedCharacter = character => inRange(character, non_ASCII_space_characters) || inRange(character, prohibited_characters) || inRange(character, non_ASCII_controls_characters) || inRange(character, non_character_codepoints);
528
-
529
- // prettier-ignore-start
530
- /**
531
- * D.1 Characters with bidirectional property "R" or "AL"
532
- * @link https://tools.ietf.org/html/rfc3454#appendix-D.1
533
- */
534
473
  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];
535
- // prettier-ignore-end
536
-
537
474
  const isBidirectionalRAL = character => inRange(character, bidirectional_r_al);
538
-
539
- // prettier-ignore-start
540
- /**
541
- * D.2 Characters with bidirectional property "L"
542
- * @link https://tools.ietf.org/html/rfc3454#appendix-D.2
543
- */
544
475
  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];
545
- // prettier-ignore-end
546
-
547
476
  const isBidirectionalL = character => inRange(character, bidirectional_l);
548
477
 
549
- // 2.1. Mapping
550
-
551
- /**
552
- * non-ASCII space characters [StringPrep, C.1.2] that can be
553
- * mapped to SPACE (U+0020)
554
- */
555
478
  const mapping2space = isNonASCIISpaceCharacter;
556
-
557
- /**
558
- * the "commonly mapped to nothing" characters [StringPrep, B.1]
559
- * that can be mapped to nothing.
560
- */
561
479
  const mapping2nothing = isCommonlyMappedToNothing;
562
-
563
- // utils
564
480
  const getCodePoint = character => character.codePointAt(0);
565
481
  const first = x => x[0];
566
482
  const last = x => x[x.length - 1];
567
-
568
- /**
569
- * Convert provided string into an array of Unicode Code Points.
570
- * Based on https://stackoverflow.com/a/21409165/1556249
571
- * and https://www.npmjs.com/package/code-point-at.
572
- * @param {string} input
573
- * @returns {number[]}
574
- */
575
483
  function toCodePoints(input) {
576
484
  const codepoints = [];
577
485
  const size = input.length;
@@ -589,14 +497,6 @@ function toCodePoints(input) {
589
497
  }
590
498
  return codepoints;
591
499
  }
592
-
593
- /**
594
- * SASLprep.
595
- * @param {string} input
596
- * @param {Object} opts
597
- * @param {boolean} opts.allowUnassigned
598
- * @returns {string}
599
- */
600
500
  function saslprep(input) {
601
501
  let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
602
502
  if (typeof input !== 'string') {
@@ -605,49 +505,24 @@ function saslprep(input) {
605
505
  if (input.length === 0) {
606
506
  return '';
607
507
  }
608
-
609
- // 1. Map
610
- const mapped_input = toCodePoints(input)
611
- // 1.1 mapping to space
612
- .map(character => mapping2space(character) ? 0x20 : character)
613
- // 1.2 mapping to nothing
614
- .filter(character => !mapping2nothing(character));
615
-
616
- // 2. Normalize
508
+ const mapped_input = toCodePoints(input).map(character => mapping2space(character) ? 0x20 : character).filter(character => !mapping2nothing(character));
617
509
  const normalized_input = String.fromCodePoint.apply(null, mapped_input).normalize('NFKC');
618
510
  const normalized_map = toCodePoints(normalized_input);
619
-
620
- // 3. Prohibit
621
511
  const hasProhibited = normalized_map.some(isProhibitedCharacter);
622
512
  if (hasProhibited) {
623
513
  throw new Error('Prohibited character, see https://tools.ietf.org/html/rfc4013#section-2.3');
624
514
  }
625
-
626
- // Unassigned Code Points
627
515
  if (opts.allowUnassigned !== true) {
628
516
  const hasUnassigned = normalized_map.some(isUnassignedCodePoint);
629
517
  if (hasUnassigned) {
630
518
  throw new Error('Unassigned code point, see https://tools.ietf.org/html/rfc4013#section-2.5');
631
519
  }
632
520
  }
633
-
634
- // 4. check bidi
635
-
636
521
  const hasBidiRAL = normalized_map.some(isBidirectionalRAL);
637
522
  const hasBidiL = normalized_map.some(isBidirectionalL);
638
-
639
- // 4.1 If a string contains any RandALCat character, the string MUST NOT
640
- // contain any LCat character.
641
523
  if (hasBidiRAL && hasBidiL) {
642
524
  throw new Error('String must not contain RandALCat and LCat at the same time,' + ' see https://tools.ietf.org/html/rfc3454#section-6');
643
525
  }
644
-
645
- /**
646
- * 4.2 If a string contains any RandALCat character, a RandALCat
647
- * character MUST be the first character of the string, and a
648
- * RandALCat character MUST be the last character of the string.
649
- */
650
-
651
526
  const isFirstBidiRAL = isBidirectionalRAL(getCodePoint(first(normalized_input)));
652
527
  const isLastBidiRAL = isBidirectionalRAL(getCodePoint(last(normalized_input)));
653
528
  if (hasBidiRAL && !(isFirstBidiRAL && isLastBidiRAL)) {
@@ -656,16 +531,11 @@ function saslprep(input) {
656
531
  return normalized_input;
657
532
  }
658
533
 
659
- /*
660
- PDFSecurity - represents PDF security settings
661
- By Yang Liu <hi@zesik.com>
662
- */
663
534
  class PDFSecurity {
664
535
  static generateFileID() {
665
536
  let info = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
666
537
  let infoStr = `${info.CreationDate.getTime()}\n`;
667
538
  for (let key in info) {
668
- // eslint-disable-next-line no-prototype-builtins
669
539
  if (!info.hasOwnProperty(key)) {
670
540
  continue;
671
541
  }
@@ -1002,9 +872,9 @@ function wordArrayToBuffer(wordArray) {
1002
872
  const PASSWORD_PADDING = [0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a];
1003
873
 
1004
874
  const {
1005
- number
875
+ number: number$2
1006
876
  } = PDFObject;
1007
- class PDFGradient {
877
+ class PDFGradient$1 {
1008
878
  constructor(doc) {
1009
879
  this.doc = doc;
1010
880
  this.stops = [];
@@ -1045,8 +915,6 @@ class PDFGradient {
1045
915
  }
1046
916
  this.embedded = true;
1047
917
  this.matrix = m;
1048
-
1049
- // if the last stop comes before 100%, add a copy at 100%
1050
918
  const last = this.stops[stopsLength - 1];
1051
919
  if (last[0] < 1) {
1052
920
  this.stops.push([1, last[1], last[2]]);
@@ -1069,14 +937,11 @@ class PDFGradient {
1069
937
  stops.push(fn);
1070
938
  fn.end();
1071
939
  }
1072
-
1073
- // if there are only two stops, we don't need a stitching function
1074
940
  if (stopsLength === 1) {
1075
941
  fn = stops[0];
1076
942
  } else {
1077
943
  fn = this.doc.ref({
1078
944
  FunctionType: 3,
1079
- // stitching function
1080
945
  Domain: [0, 1],
1081
946
  Functions: stops,
1082
947
  Bounds: bounds,
@@ -1091,7 +956,7 @@ class PDFGradient {
1091
956
  Type: 'Pattern',
1092
957
  PatternType: 2,
1093
958
  Shading: shader,
1094
- Matrix: this.matrix.map(number)
959
+ Matrix: this.matrix.map(number$2)
1095
960
  });
1096
961
  pattern.end();
1097
962
  if (this.stops.some(stop => stop[2] < 1)) {
@@ -1157,7 +1022,6 @@ class PDFGradient {
1157
1022
  return pattern;
1158
1023
  }
1159
1024
  apply(stroke) {
1160
- // apply gradient transform to existing document ctm
1161
1025
  const [m0, m1, m2, m3, m4, m5] = this.doc._ctm;
1162
1026
  const [m11, m12, m21, m22, dx, dy] = this.transform;
1163
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];
@@ -1169,7 +1033,7 @@ class PDFGradient {
1169
1033
  return this.doc.addContent(`/${this.id} ${op}`);
1170
1034
  }
1171
1035
  }
1172
- class PDFLinearGradient extends PDFGradient {
1036
+ class PDFLinearGradient$1 extends PDFGradient$1 {
1173
1037
  constructor(doc, x1, y1, x2, y2) {
1174
1038
  super(doc);
1175
1039
  this.x1 = x1;
@@ -1187,10 +1051,10 @@ class PDFLinearGradient extends PDFGradient {
1187
1051
  });
1188
1052
  }
1189
1053
  opacityGradient() {
1190
- return new PDFLinearGradient(this.doc, this.x1, this.y1, this.x2, this.y2);
1054
+ return new PDFLinearGradient$1(this.doc, this.x1, this.y1, this.x2, this.y2);
1191
1055
  }
1192
1056
  }
1193
- class PDFRadialGradient extends PDFGradient {
1057
+ class PDFRadialGradient$1 extends PDFGradient$1 {
1194
1058
  constructor(doc, x1, y1, r1, x2, y2, r2) {
1195
1059
  super(doc);
1196
1060
  this.doc = doc;
@@ -1211,21 +1075,17 @@ class PDFRadialGradient extends PDFGradient {
1211
1075
  });
1212
1076
  }
1213
1077
  opacityGradient() {
1214
- return new PDFRadialGradient(this.doc, this.x1, this.y1, this.r1, this.x2, this.y2, this.r2);
1078
+ return new PDFRadialGradient$1(this.doc, this.x1, this.y1, this.r1, this.x2, this.y2, this.r2);
1215
1079
  }
1216
1080
  }
1217
1081
  var Gradient = {
1218
- PDFGradient,
1219
- PDFLinearGradient,
1220
- PDFRadialGradient
1082
+ PDFGradient: PDFGradient$1,
1083
+ PDFLinearGradient: PDFLinearGradient$1,
1084
+ PDFRadialGradient: PDFRadialGradient$1
1221
1085
  };
1222
1086
 
1223
- /*
1224
- PDF tiling pattern support. Uncolored only.
1225
- */
1226
-
1227
1087
  const underlyingColorSpaces = ['DeviceCMYK', 'DeviceRGB'];
1228
- class PDFTilingPattern {
1088
+ class PDFTilingPattern$1 {
1229
1089
  constructor(doc, bBox, xStep, yStep, stream) {
1230
1090
  this.doc = doc;
1231
1091
  this.bBox = bBox;
@@ -1234,23 +1094,16 @@ class PDFTilingPattern {
1234
1094
  this.stream = stream;
1235
1095
  }
1236
1096
  createPattern() {
1237
- // no resources needed for our current usage
1238
- // required entry
1239
1097
  const resources = this.doc.ref();
1240
1098
  resources.end();
1241
- // apply default transform matrix (flipped in the default doc._ctm)
1242
- // see document.js & gradient.js
1243
1099
  const [m0, m1, m2, m3, m4, m5] = this.doc._ctm;
1244
1100
  const [m11, m12, m21, m22, dx, dy] = [1, 0, 0, 1, 0, 0];
1245
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];
1246
1102
  const pattern = this.doc.ref({
1247
1103
  Type: 'Pattern',
1248
1104
  PatternType: 1,
1249
- // tiling
1250
1105
  PaintType: 2,
1251
- // 1-colored, 2-uncolored
1252
1106
  TilingType: 2,
1253
- // 2-no distortion
1254
1107
  BBox: this.bBox,
1255
1108
  XStep: this.xStep,
1256
1109
  YStep: this.yStep,
@@ -1261,8 +1114,6 @@ class PDFTilingPattern {
1261
1114
  return pattern;
1262
1115
  }
1263
1116
  embedPatternColorSpaces() {
1264
- // map each pattern to an underlying color space
1265
- // and embed on each page
1266
1117
  underlyingColorSpaces.forEach(csName => {
1267
1118
  const csId = this.getPatternColorSpaceId(csName);
1268
1119
  if (this.doc.page.colorSpaces[csId]) return;
@@ -1280,47 +1131,40 @@ class PDFTilingPattern {
1280
1131
  this.id = 'P' + this.doc._patternCount;
1281
1132
  this.pattern = this.createPattern();
1282
1133
  }
1283
-
1284
- // patterns are embedded in each page
1285
1134
  if (!this.doc.page.patterns[this.id]) {
1286
1135
  this.doc.page.patterns[this.id] = this.pattern;
1287
1136
  }
1288
1137
  }
1289
1138
  apply(stroke, patternColor) {
1290
- // do any embedding/creating that might be needed
1291
1139
  this.embedPatternColorSpaces();
1292
1140
  this.embed();
1293
1141
  const normalizedColor = this.doc._normalizeColor(patternColor);
1294
1142
  if (!normalizedColor) throw Error(`invalid pattern color. (value: ${patternColor})`);
1295
-
1296
- // select one of the pattern color spaces
1297
1143
  const csId = this.getPatternColorSpaceId(this.doc._getColorSpace(normalizedColor));
1298
1144
  this.doc._setColorSpace(csId, stroke);
1299
-
1300
- // stroke/fill using the pattern and color (in the above underlying color space)
1301
1145
  const op = stroke ? 'SCN' : 'scn';
1302
1146
  return this.doc.addContent(`${normalizedColor.join(' ')} /${this.id} ${op}`);
1303
1147
  }
1304
1148
  }
1305
1149
  var pattern = {
1306
- PDFTilingPattern
1150
+ PDFTilingPattern: PDFTilingPattern$1
1307
1151
  };
1308
1152
 
1309
1153
  const {
1310
- PDFGradient: PDFGradient$1,
1311
- PDFLinearGradient: PDFLinearGradient$1,
1312
- PDFRadialGradient: PDFRadialGradient$1
1154
+ PDFGradient,
1155
+ PDFLinearGradient,
1156
+ PDFRadialGradient
1313
1157
  } = Gradient;
1314
1158
  const {
1315
- PDFTilingPattern: PDFTilingPattern$1
1159
+ PDFTilingPattern
1316
1160
  } = pattern;
1317
1161
  var ColorMixin = {
1318
1162
  initColor() {
1319
- // The opacity dictionaries
1163
+ this.spotColors = {};
1320
1164
  this._opacityRegistry = {};
1321
1165
  this._opacityCount = 0;
1322
1166
  this._patternCount = 0;
1323
- return this._gradCount = 0;
1167
+ this._gradCount = 0;
1324
1168
  },
1325
1169
  _normalizeColor(color) {
1326
1170
  if (typeof color === 'string') {
@@ -1332,13 +1176,13 @@ var ColorMixin = {
1332
1176
  color = [hex >> 16, hex >> 8 & 0xff, hex & 0xff];
1333
1177
  } else if (namedColors[color]) {
1334
1178
  color = namedColors[color];
1179
+ } else if (this.spotColors[color]) {
1180
+ return this.spotColors[color];
1335
1181
  }
1336
1182
  }
1337
1183
  if (Array.isArray(color)) {
1338
- // RGB
1339
1184
  if (color.length === 3) {
1340
1185
  color = color.map(part => part / 255);
1341
- // CMYK
1342
1186
  } else if (color.length === 4) {
1343
1187
  color = color.map(part => part / 100);
1344
1188
  }
@@ -1347,15 +1191,13 @@ var ColorMixin = {
1347
1191
  return null;
1348
1192
  },
1349
1193
  _setColor(color, stroke) {
1350
- if (color instanceof PDFGradient$1) {
1194
+ if (color instanceof PDFGradient) {
1351
1195
  color.apply(stroke);
1352
1196
  return true;
1353
- // see if tiling pattern, decode & apply it it
1354
- } else if (Array.isArray(color) && color[0] instanceof PDFTilingPattern$1) {
1197
+ } else if (Array.isArray(color) && color[0] instanceof PDFTilingPattern) {
1355
1198
  color[0].apply(stroke, color[1]);
1356
1199
  return true;
1357
1200
  }
1358
- // any other case should be a normal color and not a pattern
1359
1201
  return this._setColorCore(color, stroke);
1360
1202
  },
1361
1203
  _setColorCore(color, stroke) {
@@ -1366,8 +1208,12 @@ var ColorMixin = {
1366
1208
  const op = stroke ? 'SCN' : 'scn';
1367
1209
  const space = this._getColorSpace(color);
1368
1210
  this._setColorSpace(space, stroke);
1369
- color = color.join(' ');
1370
- this.addContent(`${color} ${op}`);
1211
+ if (color instanceof SpotColor) {
1212
+ this.page.colorSpaces[color.id] = color.ref;
1213
+ this.addContent(`1 ${op}`);
1214
+ } else {
1215
+ this.addContent(`${color.join(' ')} ${op}`);
1216
+ }
1371
1217
  return true;
1372
1218
  },
1373
1219
  _setColorSpace(space, stroke) {
@@ -1375,6 +1221,9 @@ var ColorMixin = {
1375
1221
  return this.addContent(`/${space} ${op}`);
1376
1222
  },
1377
1223
  _getColorSpace(color) {
1224
+ if (color instanceof SpotColor) {
1225
+ return color.id;
1226
+ }
1378
1227
  return color.length === 4 ? 'DeviceCMYK' : 'DeviceRGB';
1379
1228
  },
1380
1229
  fillColor(color, opacity) {
@@ -1382,9 +1231,6 @@ var ColorMixin = {
1382
1231
  if (set) {
1383
1232
  this.fillOpacity(opacity);
1384
1233
  }
1385
-
1386
- // save this for text wrapper, which needs to reset
1387
- // the fill color on new pages
1388
1234
  this._fillColor = [color, opacity];
1389
1235
  return this;
1390
1236
  },
@@ -1441,13 +1287,18 @@ var ColorMixin = {
1441
1287
  return this.addContent(`/${name} gs`);
1442
1288
  },
1443
1289
  linearGradient(x1, y1, x2, y2) {
1444
- return new PDFLinearGradient$1(this, x1, y1, x2, y2);
1290
+ return new PDFLinearGradient(this, x1, y1, x2, y2);
1445
1291
  },
1446
1292
  radialGradient(x1, y1, r1, x2, y2, r2) {
1447
- return new PDFRadialGradient$1(this, x1, y1, r1, x2, y2, r2);
1293
+ return new PDFRadialGradient(this, x1, y1, r1, x2, y2, r2);
1448
1294
  },
1449
1295
  pattern(bbox, xStep, yStep, stream) {
1450
- return new PDFTilingPattern$1(this, bbox, xStep, yStep, stream);
1296
+ return new PDFTilingPattern(this, bbox, xStep, yStep, stream);
1297
+ },
1298
+ addSpotColor(name, C, M, Y, K) {
1299
+ const color = new SpotColor(this, name, C, M, Y, K);
1300
+ this.spotColors[name] = color;
1301
+ return this;
1451
1302
  }
1452
1303
  };
1453
1304
  var namedColors = {
@@ -1635,7 +1486,6 @@ const parse = function (path) {
1635
1486
  if (parameters[c] != null) {
1636
1487
  params = parameters[c];
1637
1488
  if (cmd) {
1638
- // save existing command
1639
1489
  if (curArg.length > 0) {
1640
1490
  args[args.length] = +curArg;
1641
1491
  }
@@ -1653,14 +1503,11 @@ const parse = function (path) {
1653
1503
  continue;
1654
1504
  }
1655
1505
  if (args.length === params) {
1656
- // handle reused commands
1657
1506
  ret[ret.length] = {
1658
1507
  cmd,
1659
1508
  args
1660
1509
  };
1661
1510
  args = [+curArg];
1662
-
1663
- // handle assumed commands
1664
1511
  if (cmd === 'M') {
1665
1512
  cmd = 'L';
1666
1513
  }
@@ -1671,8 +1518,6 @@ const parse = function (path) {
1671
1518
  args[args.length] = +curArg;
1672
1519
  }
1673
1520
  foundDecimal = c === '.';
1674
-
1675
- // fix for negative numbers or repeated decimals with no delimeter between commands
1676
1521
  curArg = ['-', '.'].includes(c) ? c : '';
1677
1522
  } else {
1678
1523
  curArg += c;
@@ -1681,18 +1526,13 @@ const parse = function (path) {
1681
1526
  }
1682
1527
  }
1683
1528
  }
1684
-
1685
- // add the last command
1686
1529
  if (curArg.length > 0) {
1687
1530
  if (args.length === params) {
1688
- // handle reused commands
1689
1531
  ret[ret.length] = {
1690
1532
  cmd,
1691
1533
  args
1692
1534
  };
1693
1535
  args = [+curArg];
1694
-
1695
- // handle assumed commands
1696
1536
  if (cmd === 'M') {
1697
1537
  cmd = 'L';
1698
1538
  }
@@ -1710,10 +1550,7 @@ const parse = function (path) {
1710
1550
  return ret;
1711
1551
  };
1712
1552
  const apply = function (commands, doc) {
1713
- // current point, control point, and subpath starting point
1714
1553
  cx = cy = px = py = sx = sy = 0;
1715
-
1716
- // run the commands
1717
1554
  for (let i = 0; i < commands.length; i++) {
1718
1555
  const c = commands[i];
1719
1556
  if (typeof runners[c.cmd] === 'function') {
@@ -1877,8 +1714,6 @@ const solveArc = function (doc, x, y, coords) {
1877
1714
  doc.bezierCurveTo(...bez);
1878
1715
  }
1879
1716
  };
1880
-
1881
- // from Inkscape svgtopdf, thanks!
1882
1717
  const arcToSegments = function (x, y, rx, ry, large, sweep, rotateX, ox, oy) {
1883
1718
  const th = rotateX * (Math.PI / 180);
1884
1719
  const sin_th = Math.sin(th);
@@ -1954,18 +1789,14 @@ class SVGPath {
1954
1789
  const {
1955
1790
  number: number$1
1956
1791
  } = PDFObject;
1957
-
1958
- // This constant is used to approximate a symmetrical arc using a cubic
1959
- // Bezier curve.
1960
1792
  const KAPPA = 4.0 * ((Math.sqrt(2) - 1.0) / 3.0);
1961
1793
  var VectorMixin = {
1962
1794
  initVector() {
1963
- this._ctm = [1, 0, 0, 1, 0, 0]; // current transformation matrix
1964
- return this._ctmStack = [];
1795
+ this._ctm = [1, 0, 0, 1, 0, 0];
1796
+ this._ctmStack = [];
1965
1797
  },
1966
1798
  save() {
1967
1799
  this._ctmStack.push(this._ctm.slice());
1968
- // TODO: save/restore colorspace and styles so not setting it unnessesarily all the time?
1969
1800
  return this.addContent('q');
1970
1801
  },
1971
1802
  restore() {
@@ -2039,8 +1870,6 @@ var VectorMixin = {
2039
1870
  r = 0;
2040
1871
  }
2041
1872
  r = Math.min(r, 0.5 * w, 0.5 * h);
2042
-
2043
- // amount to inset control points from corners (see `ellipse`)
2044
1873
  const c = r * (1.0 - KAPPA);
2045
1874
  this.moveTo(x + r, y);
2046
1875
  this.lineTo(x + w - r, y);
@@ -2054,7 +1883,6 @@ var VectorMixin = {
2054
1883
  return this.closePath();
2055
1884
  },
2056
1885
  ellipse(x, y, r1, r2) {
2057
- // based on http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas/2173084#2173084
2058
1886
  if (r2 == null) {
2059
1887
  r2 = r1;
2060
1888
  }
@@ -2084,10 +1912,8 @@ var VectorMixin = {
2084
1912
  const HALF_PI = 0.5 * Math.PI;
2085
1913
  let deltaAng = endAngle - startAngle;
2086
1914
  if (Math.abs(deltaAng) > TWO_PI) {
2087
- // draw only full circle if more than that is specified
2088
1915
  deltaAng = TWO_PI;
2089
1916
  } else if (deltaAng !== 0 && anticlockwise !== deltaAng < 0) {
2090
- // necessary to flip direction of rendering
2091
1917
  const dir = anticlockwise ? -1 : 1;
2092
1918
  deltaAng = dir * TWO_PI + deltaAng;
2093
1919
  }
@@ -2095,38 +1921,21 @@ var VectorMixin = {
2095
1921
  const segAng = deltaAng / numSegs;
2096
1922
  const handleLen = segAng / HALF_PI * KAPPA * radius;
2097
1923
  let curAng = startAngle;
2098
-
2099
- // component distances between anchor point and control point
2100
1924
  let deltaCx = -Math.sin(curAng) * handleLen;
2101
1925
  let deltaCy = Math.cos(curAng) * handleLen;
2102
-
2103
- // anchor point
2104
1926
  let ax = x + Math.cos(curAng) * radius;
2105
1927
  let ay = y + Math.sin(curAng) * radius;
2106
-
2107
- // calculate and render segments
2108
1928
  this.moveTo(ax, ay);
2109
1929
  for (let segIdx = 0; segIdx < numSegs; segIdx++) {
2110
- // starting control point
2111
1930
  const cp1x = ax + deltaCx;
2112
1931
  const cp1y = ay + deltaCy;
2113
-
2114
- // step angle
2115
1932
  curAng += segAng;
2116
-
2117
- // next anchor point
2118
1933
  ax = x + Math.cos(curAng) * radius;
2119
1934
  ay = y + Math.sin(curAng) * radius;
2120
-
2121
- // next control point delta
2122
1935
  deltaCx = -Math.sin(curAng) * handleLen;
2123
1936
  deltaCy = Math.cos(curAng) * handleLen;
2124
-
2125
- // ending control point
2126
1937
  const cp2x = ax - deltaCx;
2127
1938
  const cp2y = ay - deltaCy;
2128
-
2129
- // render segment
2130
1939
  this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, ax, ay);
2131
1940
  }
2132
1941
  return this;
@@ -2190,9 +1999,7 @@ var VectorMixin = {
2190
1999
  return this.addContent(`W${this._windingRule(rule)} n`);
2191
2000
  },
2192
2001
  transform(m11, m12, m21, m22, dx, dy) {
2193
- // keep track of the current transformation matrix
2194
2002
  if (m11 === 1 && m12 === 0 && m21 === 0 && m22 === 1 && dx === 0 && dy === 0) {
2195
- // Ignore identity transforms
2196
2003
  return this;
2197
2004
  }
2198
2005
  const m = this._ctm;
@@ -2358,7 +2165,6 @@ class AFMFont {
2358
2165
  this.boundingBoxes = {};
2359
2166
  this.kernPairs = {};
2360
2167
  this.parse();
2361
- // todo: remove charWidths since appears to not be used
2362
2168
  this.charWidths = new Array(256);
2363
2169
  for (let char = 0; char <= 255; char++) {
2364
2170
  this.charWidths[char] = this.glyphWidths[characters[char]];
@@ -2465,21 +2271,18 @@ class PDFFont {
2465
2271
  return;
2466
2272
  }
2467
2273
  this.embed();
2468
- return this.embedded = true;
2274
+ this.embedded = true;
2469
2275
  }
2470
2276
  embed() {
2471
2277
  throw new Error('Must be implemented by subclasses');
2472
2278
  }
2473
- lineHeight(size, includeGap) {
2474
- if (includeGap == null) {
2475
- includeGap = false;
2476
- }
2279
+ lineHeight(size) {
2280
+ let includeGap = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2477
2281
  const gap = includeGap ? this.lineGap : 0;
2478
2282
  return (this.ascender + gap - this.descender) / 1000 * size;
2479
2283
  }
2480
2284
  }
2481
2285
 
2482
- // This insanity is so bundlers can inline the font files
2483
2286
  const STANDARD_FONTS = {
2484
2287
  Courier() {
2485
2288
  return fs.readFileSync(__dirname + '/data/Courier.afm', 'utf8');
@@ -2607,8 +2410,6 @@ class EmbeddedFont extends PDFFont {
2607
2410
  }
2608
2411
  layoutRun(text, features) {
2609
2412
  const run = this.font.layout(text, features);
2610
-
2611
- // Normalize position values
2612
2413
  for (let i = 0; i < run.positions.length; i++) {
2613
2414
  const position = run.positions[i];
2614
2415
  for (let key in position) {
@@ -2631,16 +2432,12 @@ class EmbeddedFont extends PDFFont {
2631
2432
  return run;
2632
2433
  }
2633
2434
  layout(text, features, onlyWidth) {
2634
- // Skip the cache if any user defined features are applied
2635
2435
  if (features) {
2636
2436
  return this.layoutRun(text, features);
2637
2437
  }
2638
2438
  let glyphs = onlyWidth ? null : [];
2639
2439
  let positions = onlyWidth ? null : [];
2640
2440
  let advanceWidth = 0;
2641
-
2642
- // Split the string by words to increase cache efficiency.
2643
- // For this purpose, spaces and tabs are a good enough delimeter.
2644
2441
  let last = 0;
2645
2442
  let index = 0;
2646
2443
  while (index <= text.length) {
@@ -2693,7 +2490,7 @@ class EmbeddedFont extends PDFFont {
2693
2490
  if (isCFF) {
2694
2491
  fontFile.data.Subtype = 'CIDFontType0C';
2695
2492
  }
2696
- this.subset.encodeStream().on('data', data => fontFile.write(data)).on('end', () => fontFile.end());
2493
+ fontFile.end(this.subset.encode());
2697
2494
  const familyClass = ((this.font['OS/2'] != null ? this.font['OS/2'].sFamilyClass : undefined) || 0) >> 8;
2698
2495
  let flags = 0;
2699
2496
  if (this.font.post.isFixedPitch) {
@@ -2702,17 +2499,15 @@ class EmbeddedFont extends PDFFont {
2702
2499
  if (1 <= familyClass && familyClass <= 7) {
2703
2500
  flags |= 1 << 1;
2704
2501
  }
2705
- flags |= 1 << 2; // assume the font uses non-latin characters
2502
+ flags |= 1 << 2;
2706
2503
  if (familyClass === 10) {
2707
2504
  flags |= 1 << 3;
2708
2505
  }
2709
2506
  if (this.font.head.macStyle.italic) {
2710
2507
  flags |= 1 << 6;
2711
2508
  }
2712
-
2713
- // generate a tag (6 uppercase letters. 17 is the char code offset from '0' to 'A'. 73 will map to 'Z')
2714
2509
  const tag = [1, 2, 3, 4, 5, 6].map(i => String.fromCharCode((this.id.charCodeAt(i) || 73) + 17)).join('');
2715
- const name = tag + '+' + this.font.postscriptName;
2510
+ const name = tag + '+' + this.font.postscriptName?.replaceAll(' ', '_');
2716
2511
  const {
2717
2512
  bbox
2718
2513
  } = this.font;
@@ -2727,8 +2522,7 @@ class EmbeddedFont extends PDFFont {
2727
2522
  CapHeight: (this.font.capHeight || this.font.ascent) * this.scale,
2728
2523
  XHeight: (this.font.xHeight || 0) * this.scale,
2729
2524
  StemV: 0
2730
- }); // not sure how to calculate this
2731
-
2525
+ });
2732
2526
  if (isCFF) {
2733
2527
  descriptor.data.FontFile3 = fontFile;
2734
2528
  } else {
@@ -2770,17 +2564,11 @@ class EmbeddedFont extends PDFFont {
2770
2564
  };
2771
2565
  return this.dictionary.end();
2772
2566
  }
2773
-
2774
- // Maps the glyph ids encoded in the PDF back to unicode strings
2775
- // Because of ligature substitutions and the like, there may be one or more
2776
- // unicode characters represented by each glyph.
2777
2567
  toUnicodeCmap() {
2778
2568
  const cmap = this.document.ref();
2779
2569
  const entries = [];
2780
2570
  for (let codePoints of this.unicode) {
2781
2571
  const encoded = [];
2782
-
2783
- // encode codePoints to utf16
2784
2572
  for (let value of codePoints) {
2785
2573
  if (value > 0xffff) {
2786
2574
  value -= 0x10000;
@@ -2834,12 +2622,10 @@ class PDFFontFactory {
2834
2622
  }
2835
2623
  src = fs.readFileSync(src);
2836
2624
  }
2837
- if (Buffer.isBuffer(src)) {
2625
+ if (src instanceof Uint8Array) {
2838
2626
  font = fontkit.create(src, family);
2839
- } else if (src instanceof Uint8Array) {
2840
- font = fontkit.create(Buffer.from(src), family);
2841
2627
  } else if (src instanceof ArrayBuffer) {
2842
- font = fontkit.create(Buffer.from(new Uint8Array(src)), family);
2628
+ font = fontkit.create(new Uint8Array(src), family);
2843
2629
  }
2844
2630
  if (font == null) {
2845
2631
  throw new Error('Not a supported font format or standard PDF font.');
@@ -2848,21 +2634,30 @@ class PDFFontFactory {
2848
2634
  }
2849
2635
  }
2850
2636
 
2637
+ const isEqualFont = (font1, font2) => {
2638
+ if (font1.font._tables?.head?.checkSumAdjustment !== font2.font._tables?.head?.checkSumAdjustment) {
2639
+ return false;
2640
+ }
2641
+ if (JSON.stringify(font1.font._tables?.name?.records) !== JSON.stringify(font2.font._tables?.name?.records)) {
2642
+ return false;
2643
+ }
2644
+ return true;
2645
+ };
2851
2646
  var FontsMixin = {
2852
2647
  initFonts() {
2853
2648
  let defaultFont = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'Helvetica';
2854
- // Lookup table for embedded fonts
2649
+ let defaultFontFamily = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
2650
+ let defaultFontSize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 12;
2855
2651
  this._fontFamilies = {};
2856
2652
  this._fontCount = 0;
2857
-
2858
- // Font state
2859
- this._fontSize = 12;
2653
+ this._fontSource = defaultFont;
2654
+ this._fontFamily = defaultFontFamily;
2655
+ this._fontSize = defaultFontSize;
2860
2656
  this._font = null;
2657
+ this._remSize = defaultFontSize;
2861
2658
  this._registeredFonts = {};
2862
-
2863
- // Set the default font
2864
2659
  if (defaultFont) {
2865
- this.font(defaultFont);
2660
+ this.font(defaultFont, defaultFontFamily);
2866
2661
  }
2867
2662
  },
2868
2663
  font(src, family, size) {
@@ -2871,8 +2666,6 @@ var FontsMixin = {
2871
2666
  size = family;
2872
2667
  family = null;
2873
2668
  }
2874
-
2875
- // check registered fonts if src is a string
2876
2669
  if (typeof src === 'string' && this._registeredFonts[src]) {
2877
2670
  cacheKey = src;
2878
2671
  ({
@@ -2885,28 +2678,21 @@ var FontsMixin = {
2885
2678
  cacheKey = null;
2886
2679
  }
2887
2680
  }
2681
+ this._fontSource = src;
2682
+ this._fontFamily = family;
2888
2683
  if (size != null) {
2889
2684
  this.fontSize(size);
2890
2685
  }
2891
-
2892
- // fast path: check if the font is already in the PDF
2893
2686
  if (font = this._fontFamilies[cacheKey]) {
2894
2687
  this._font = font;
2895
2688
  return this;
2896
2689
  }
2897
-
2898
- // load the font
2899
2690
  const id = `F${++this._fontCount}`;
2900
2691
  this._font = PDFFontFactory.open(this, src, family, id);
2901
-
2902
- // check for existing font familes with the same name already in the PDF
2903
- // useful if the font was passed as a buffer
2904
- if (font = this._fontFamilies[this._font.name]) {
2692
+ if ((font = this._fontFamilies[this._font.name]) && isEqualFont(this._font, font)) {
2905
2693
  this._font = font;
2906
2694
  return this;
2907
2695
  }
2908
-
2909
- // save the font for reuse later
2910
2696
  if (cacheKey) {
2911
2697
  this._fontFamilies[cacheKey] = this._font;
2912
2698
  }
@@ -2916,13 +2702,10 @@ var FontsMixin = {
2916
2702
  return this;
2917
2703
  },
2918
2704
  fontSize(_fontSize) {
2919
- this._fontSize = _fontSize;
2705
+ this._fontSize = this.sizeToPoint(_fontSize);
2920
2706
  return this;
2921
2707
  },
2922
2708
  currentLineHeight(includeGap) {
2923
- if (includeGap == null) {
2924
- includeGap = false;
2925
- }
2926
2709
  return this._font.lineHeight(this._fontSize, includeGap);
2927
2710
  },
2928
2711
  registerFont(name, src, family) {
@@ -2931,6 +2714,67 @@ var FontsMixin = {
2931
2714
  family
2932
2715
  };
2933
2716
  return this;
2717
+ },
2718
+ sizeToPoint(size) {
2719
+ let defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2720
+ let page = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.page;
2721
+ let percentageWidth = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : undefined;
2722
+ if (!percentageWidth) percentageWidth = this._fontSize;
2723
+ if (typeof defaultValue !== 'number') defaultValue = this.sizeToPoint(defaultValue);
2724
+ if (size === undefined) return defaultValue;
2725
+ if (typeof size === 'number') return size;
2726
+ if (typeof size === 'boolean') return Number(size);
2727
+ const match = String(size).match(/((\d+)?(\.\d+)?)(em|in|px|cm|mm|pc|ex|ch|rem|vw|vh|vmin|vmax|%|pt)?/);
2728
+ if (!match) throw new Error(`Unsupported size '${size}'`);
2729
+ let multiplier;
2730
+ switch (match[4]) {
2731
+ case 'em':
2732
+ multiplier = this._fontSize;
2733
+ break;
2734
+ case 'in':
2735
+ multiplier = IN_TO_PT;
2736
+ break;
2737
+ case 'px':
2738
+ multiplier = PX_TO_IN * IN_TO_PT;
2739
+ break;
2740
+ case 'cm':
2741
+ multiplier = CM_TO_IN * IN_TO_PT;
2742
+ break;
2743
+ case 'mm':
2744
+ multiplier = MM_TO_CM * CM_TO_IN * IN_TO_PT;
2745
+ break;
2746
+ case 'pc':
2747
+ multiplier = PC_TO_PT;
2748
+ break;
2749
+ case 'ex':
2750
+ multiplier = this.currentLineHeight();
2751
+ break;
2752
+ case 'ch':
2753
+ multiplier = this.widthOfString('0');
2754
+ break;
2755
+ case 'rem':
2756
+ multiplier = this._remSize;
2757
+ break;
2758
+ case 'vw':
2759
+ multiplier = page.width / 100;
2760
+ break;
2761
+ case 'vh':
2762
+ multiplier = page.height / 100;
2763
+ break;
2764
+ case 'vmin':
2765
+ multiplier = Math.min(page.width, page.height) / 100;
2766
+ break;
2767
+ case 'vmax':
2768
+ multiplier = Math.max(page.width, page.height) / 100;
2769
+ break;
2770
+ case '%':
2771
+ multiplier = percentageWidth / 100;
2772
+ break;
2773
+ case 'pt':
2774
+ default:
2775
+ multiplier = 1;
2776
+ }
2777
+ return multiplier * Number(match[1]);
2934
2778
  }
2935
2779
  };
2936
2780
 
@@ -2940,12 +2784,13 @@ class LineWrapper extends EventEmitter {
2940
2784
  constructor(document, options) {
2941
2785
  super();
2942
2786
  this.document = document;
2943
- this.indent = options.indent || 0;
2944
- this.characterSpacing = options.characterSpacing || 0;
2945
- this.wordSpacing = options.wordSpacing === 0;
2787
+ this.horizontalScaling = options.horizontalScaling || 100;
2788
+ this.indent = (options.indent || 0) * this.horizontalScaling / 100;
2789
+ this.characterSpacing = (options.characterSpacing || 0) * this.horizontalScaling / 100;
2790
+ this.wordSpacing = (options.wordSpacing === 0) * this.horizontalScaling / 100;
2946
2791
  this.columns = options.columns || 1;
2947
- this.columnGap = options.columnGap != null ? options.columnGap : 18; // 1/4 inch
2948
- this.lineWidth = (options.width - this.columnGap * (this.columns - 1)) / this.columns;
2792
+ this.columnGap = (options.columnGap != null ? options.columnGap : 18) * this.horizontalScaling / 100;
2793
+ this.lineWidth = (options.width * this.horizontalScaling / 100 - this.columnGap * (this.columns - 1)) / this.columns;
2949
2794
  this.spaceLeft = this.lineWidth;
2950
2795
  this.startX = this.document.x;
2951
2796
  this.startY = this.document.y;
@@ -2953,36 +2798,30 @@ class LineWrapper extends EventEmitter {
2953
2798
  this.ellipsis = options.ellipsis;
2954
2799
  this.continuedX = 0;
2955
2800
  this.features = options.features;
2956
-
2957
- // calculate the maximum Y position the text can appear at
2958
2801
  if (options.height != null) {
2959
2802
  this.height = options.height;
2960
- this.maxY = this.startY + options.height;
2803
+ this.maxY = PDFNumber(this.startY + options.height);
2961
2804
  } else {
2962
- this.maxY = this.document.page.maxY();
2805
+ this.maxY = PDFNumber(this.document.page.maxY());
2963
2806
  }
2964
-
2965
- // handle paragraph indents
2966
2807
  this.on('firstLine', options => {
2967
- // if this is the first line of the text segment, and
2968
- // we're continuing where we left off, indent that much
2969
- // otherwise use the user specified indent option
2970
2808
  const indent = this.continuedX || this.indent;
2971
2809
  this.document.x += indent;
2972
2810
  this.lineWidth -= indent;
2973
- return this.once('line', () => {
2811
+ if (options.indentAllLines) {
2812
+ return;
2813
+ }
2814
+ this.once('line', () => {
2974
2815
  this.document.x -= indent;
2975
2816
  this.lineWidth += indent;
2976
2817
  if (options.continued && !this.continuedX) {
2977
2818
  this.continuedX = this.indent;
2978
2819
  }
2979
2820
  if (!options.continued) {
2980
- return this.continuedX = 0;
2821
+ this.continuedX = 0;
2981
2822
  }
2982
2823
  });
2983
2824
  });
2984
-
2985
- // handle left aligning last lines of paragraphs
2986
2825
  this.on('lastLine', options => {
2987
2826
  const {
2988
2827
  align
@@ -2991,7 +2830,7 @@ class LineWrapper extends EventEmitter {
2991
2830
  options.align = 'left';
2992
2831
  }
2993
2832
  this.lastLine = true;
2994
- return this.once('line', () => {
2833
+ this.once('line', () => {
2995
2834
  this.document.y += options.paragraphGap || 0;
2996
2835
  options.align = align;
2997
2836
  return this.lastLine = false;
@@ -3008,7 +2847,6 @@ class LineWrapper extends EventEmitter {
3008
2847
  return w + this.wordWidth(HYPHEN) <= this.spaceLeft;
3009
2848
  }
3010
2849
  eachWord(text, fn) {
3011
- // setup a unicode line breaker
3012
2850
  let bk;
3013
2851
  const breaker = new LineBreaker(text);
3014
2852
  let last = null;
@@ -3017,19 +2855,12 @@ class LineWrapper extends EventEmitter {
3017
2855
  var shouldContinue;
3018
2856
  let word = text.slice((last != null ? last.position : undefined) || 0, bk.position);
3019
2857
  let w = wordWidths[word] != null ? wordWidths[word] : wordWidths[word] = this.wordWidth(word);
3020
-
3021
- // if the word is longer than the whole line, chop it up
3022
- // TODO: break by grapheme clusters, not JS string characters
3023
2858
  if (w > this.lineWidth + this.continuedX) {
3024
- // make some fake break objects
3025
2859
  let lbk = last;
3026
2860
  const fbk = {};
3027
2861
  while (word.length) {
3028
- // fit as much of the word as possible into the space we have
3029
2862
  var l, mightGrow;
3030
2863
  if (w > this.spaceLeft) {
3031
- // start our check at the end of our available space - this method is faster than a loop of each character and it resolves
3032
- // an issue with long loops when processing massive words, such as a huge number of spaces
3033
2864
  l = Math.ceil(this.spaceLeft / (w / word.length));
3034
2865
  w = this.wordWidth(word.slice(0, l));
3035
2866
  mightGrow = w <= this.spaceLeft && l < word.length;
@@ -3037,7 +2868,6 @@ class LineWrapper extends EventEmitter {
3037
2868
  l = word.length;
3038
2869
  }
3039
2870
  let mustShrink = w > this.spaceLeft && l > 0;
3040
- // shrink or grow word as necessary after our near-guess above
3041
2871
  while (mustShrink || mightGrow) {
3042
2872
  if (mustShrink) {
3043
2873
  w = this.wordWidth(word.slice(0, --l));
@@ -3048,20 +2878,14 @@ class LineWrapper extends EventEmitter {
3048
2878
  mightGrow = w <= this.spaceLeft && l < word.length;
3049
2879
  }
3050
2880
  }
3051
-
3052
- // check for the edge case where a single character cannot fit into a line.
3053
2881
  if (l === 0 && this.spaceLeft === this.lineWidth) {
3054
2882
  l = 1;
3055
2883
  }
3056
-
3057
- // send a required break unless this is the last piece and a linebreak is not specified
3058
2884
  fbk.required = bk.required || l < word.length;
3059
2885
  shouldContinue = fn(word.slice(0, l), w, fbk, lbk);
3060
2886
  lbk = {
3061
2887
  required: false
3062
2888
  };
3063
-
3064
- // get the remaining piece of the word
3065
2889
  word = word.slice(l);
3066
2890
  w = this.wordWidth(word);
3067
2891
  if (shouldContinue === false) {
@@ -3069,7 +2893,6 @@ class LineWrapper extends EventEmitter {
3069
2893
  }
3070
2894
  }
3071
2895
  } else {
3072
- // otherwise just emit the break as it was given to us
3073
2896
  shouldContinue = fn(word, w, bk, last);
3074
2897
  }
3075
2898
  if (shouldContinue === false) {
@@ -3079,23 +2902,19 @@ class LineWrapper extends EventEmitter {
3079
2902
  }
3080
2903
  }
3081
2904
  wrap(text, options) {
3082
- // override options from previous continued fragments
2905
+ this.horizontalScaling = options.horizontalScaling || 100;
3083
2906
  if (options.indent != null) {
3084
- this.indent = options.indent;
2907
+ this.indent = options.indent * this.horizontalScaling / 100;
3085
2908
  }
3086
2909
  if (options.characterSpacing != null) {
3087
- this.characterSpacing = options.characterSpacing;
2910
+ this.characterSpacing = options.characterSpacing * this.horizontalScaling / 100;
3088
2911
  }
3089
2912
  if (options.wordSpacing != null) {
3090
- this.wordSpacing = options.wordSpacing;
2913
+ this.wordSpacing = options.wordSpacing * this.horizontalScaling / 100;
3091
2914
  }
3092
2915
  if (options.ellipsis != null) {
3093
2916
  this.ellipsis = options.ellipsis;
3094
2917
  }
3095
-
3096
- // make sure we're actually on the page
3097
- // and that the first line of is never by
3098
- // itself at the bottom of a page (orphans)
3099
2918
  const nextY = this.document.y + this.document.currentLineHeight(true);
3100
2919
  if (this.document.y > this.maxY || nextY > this.maxY) {
3101
2920
  this.nextSection();
@@ -3106,7 +2925,7 @@ class LineWrapper extends EventEmitter {
3106
2925
  let lc = 0;
3107
2926
  let {
3108
2927
  y
3109
- } = this.document; // used to reset Y pos if options.continued (below)
2928
+ } = this.document;
3110
2929
  const emitLine = () => {
3111
2930
  options.textWidth = textWidth + this.wordSpacing * (wc - 1);
3112
2931
  options.wordCount = wc;
@@ -3129,23 +2948,17 @@ class LineWrapper extends EventEmitter {
3129
2948
  wc++;
3130
2949
  }
3131
2950
  if (bk.required || !this.canFit(word, w)) {
3132
- // if the user specified a max height and an ellipsis, and is about to pass the
3133
- // max height and max columns after the next line, append the ellipsis
3134
2951
  const lh = this.document.currentLineHeight(true);
3135
- if (this.height != null && this.ellipsis && this.document.y + lh * 2 > this.maxY && this.column >= this.columns) {
2952
+ if (this.height != null && this.ellipsis && PDFNumber(this.document.y + lh * 2) > this.maxY && this.column >= this.columns) {
3136
2953
  if (this.ellipsis === true) {
3137
2954
  this.ellipsis = '…';
3138
- } // map default ellipsis character
2955
+ }
3139
2956
  buffer = buffer.replace(/\s+$/, '');
3140
2957
  textWidth = this.wordWidth(buffer + this.ellipsis);
3141
-
3142
- // remove characters from the buffer until the ellipsis fits
3143
- // to avoid infinite loop need to stop while-loop if buffer is empty string
3144
2958
  while (buffer && textWidth > this.lineWidth) {
3145
2959
  buffer = buffer.slice(0, -1).replace(/\s+$/, '');
3146
2960
  textWidth = this.wordWidth(buffer + this.ellipsis);
3147
2961
  }
3148
- // need to add ellipsis only if there is enough space for it
3149
2962
  if (textWidth <= this.lineWidth) {
3150
2963
  buffer = buffer + this.ellipsis;
3151
2964
  }
@@ -3160,35 +2973,25 @@ class LineWrapper extends EventEmitter {
3160
2973
  }
3161
2974
  this.emit('lastLine', options, this);
3162
2975
  }
3163
-
3164
- // Previous entry is a soft hyphen - add visible hyphen.
3165
2976
  if (buffer[buffer.length - 1] == SOFT_HYPHEN) {
3166
2977
  buffer = buffer.slice(0, -1) + HYPHEN;
3167
2978
  this.spaceLeft -= this.wordWidth(HYPHEN);
3168
2979
  }
3169
2980
  emitLine();
3170
-
3171
- // if we've reached the edge of the page,
3172
- // continue on a new page or column
3173
- if (this.document.y + lh > this.maxY) {
2981
+ if (PDFNumber(this.document.y + lh) > this.maxY) {
3174
2982
  const shouldContinue = this.nextSection();
3175
-
3176
- // stop if we reached the maximum height
3177
2983
  if (!shouldContinue) {
3178
2984
  wc = 0;
3179
2985
  buffer = '';
3180
2986
  return false;
3181
2987
  }
3182
2988
  }
3183
-
3184
- // reset the space left and buffer
3185
2989
  if (bk.required) {
3186
2990
  this.spaceLeft = this.lineWidth;
3187
2991
  buffer = '';
3188
2992
  textWidth = 0;
3189
2993
  return wc = 0;
3190
2994
  } else {
3191
- // reset the space left and buffer
3192
2995
  this.spaceLeft = this.lineWidth - w;
3193
2996
  buffer = word;
3194
2997
  textWidth = w;
@@ -3203,25 +3006,19 @@ class LineWrapper extends EventEmitter {
3203
3006
  emitLine();
3204
3007
  }
3205
3008
  this.emit('sectionEnd', options, this);
3206
-
3207
- // if the wrap is set to be continued, save the X position
3208
- // to start the first line of the next segment at, and reset
3209
- // the y position
3210
3009
  if (options.continued === true) {
3211
3010
  if (lc > 1) {
3212
3011
  this.continuedX = 0;
3213
3012
  }
3214
3013
  this.continuedX += options.textWidth || 0;
3215
- return this.document.y = y;
3014
+ this.document.y = y;
3216
3015
  } else {
3217
- return this.document.x = this.startX;
3016
+ this.document.x = this.startX;
3218
3017
  }
3219
3018
  }
3220
3019
  nextSection(options) {
3221
3020
  this.emit('sectionEnd', options, this);
3222
3021
  if (++this.column > this.columns) {
3223
- // if a max height was specified by the user, we're done.
3224
- // otherwise, the default is to make a new page at the bottom.
3225
3022
  if (this.height != null) {
3226
3023
  return false;
3227
3024
  }
@@ -3245,15 +3042,14 @@ class LineWrapper extends EventEmitter {
3245
3042
  }
3246
3043
 
3247
3044
  const {
3248
- number: number$2
3045
+ number
3249
3046
  } = PDFObject;
3250
3047
  var TextMixin = {
3251
3048
  initText() {
3252
3049
  this._line = this._line.bind(this);
3253
- // Current coordinates
3254
3050
  this.x = 0;
3255
3051
  this.y = 0;
3256
- return this._lineGap = 0;
3052
+ this._lineGap = 0;
3257
3053
  },
3258
3054
  lineGap(_lineGap) {
3259
3055
  this._lineGap = _lineGap;
@@ -3275,11 +3071,7 @@ var TextMixin = {
3275
3071
  },
3276
3072
  _text(text, x, y, options, lineCallback) {
3277
3073
  options = this._initOptions(x, y, options);
3278
-
3279
- // Convert text to a string
3280
3074
  text = text == null ? '' : `${text}`;
3281
-
3282
- // if the wordSpacing option is specified, remove multiple consecutive spaces
3283
3075
  if (options.wordSpacing) {
3284
3076
  text = text.replace(/\s{2,}/g, ' ');
3285
3077
  }
@@ -3288,8 +3080,12 @@ var TextMixin = {
3288
3080
  options.structParent.add(this.struct(options.structType || 'P', [this.markStructureContent(options.structType || 'P')]));
3289
3081
  }
3290
3082
  };
3291
-
3292
- // word wrapping
3083
+ if (options.rotation !== 0) {
3084
+ this.save();
3085
+ this.rotate(-options.rotation, {
3086
+ origin: [this.x, this.y]
3087
+ });
3088
+ }
3293
3089
  if (options.width) {
3294
3090
  let wrapper = this._wrapper;
3295
3091
  if (!wrapper) {
@@ -3300,14 +3096,13 @@ var TextMixin = {
3300
3096
  this._wrapper = options.continued ? wrapper : null;
3301
3097
  this._textOptions = options.continued ? options : null;
3302
3098
  wrapper.wrap(text, options);
3303
-
3304
- // render paragraphs as single lines
3305
3099
  } else {
3306
3100
  for (let line of text.split('\n')) {
3307
3101
  addStructure();
3308
3102
  lineCallback(line, options);
3309
3103
  }
3310
3104
  }
3105
+ if (options.rotation !== 0) this.restore();
3311
3106
  return this;
3312
3107
  },
3313
3108
  text(text, x, y, options) {
@@ -3315,7 +3110,100 @@ var TextMixin = {
3315
3110
  },
3316
3111
  widthOfString(string) {
3317
3112
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
3318
- return this._font.widthOfString(string, this._fontSize, options.features) + (options.characterSpacing || 0) * (string.length - 1);
3113
+ const horizontalScaling = options.horizontalScaling || 100;
3114
+ return (this._font.widthOfString(string, this._fontSize, options.features) + (options.characterSpacing || 0) * (string.length - 1)) * horizontalScaling / 100;
3115
+ },
3116
+ boundsOfString(string, x, y, options) {
3117
+ options = this._initOptions(x, y, options);
3118
+ ({
3119
+ x,
3120
+ y
3121
+ } = this);
3122
+ const lineGap = options.lineGap ?? this._lineGap ?? 0;
3123
+ const lineHeight = this.currentLineHeight(true) + lineGap;
3124
+ let contentWidth = 0;
3125
+ string = String(string ?? '');
3126
+ if (options.wordSpacing) {
3127
+ string = string.replace(/\s{2,}/g, ' ');
3128
+ }
3129
+ if (options.width) {
3130
+ let wrapper = new LineWrapper(this, options);
3131
+ wrapper.on('line', (text, options) => {
3132
+ this.y += lineHeight;
3133
+ text = text.replace(/\n/g, '');
3134
+ if (text.length) {
3135
+ let wordSpacing = options.wordSpacing ?? 0;
3136
+ const characterSpacing = options.characterSpacing ?? 0;
3137
+ if (options.width && options.align === 'justify') {
3138
+ const words = text.trim().split(/\s+/);
3139
+ const textWidth = this.widthOfString(text.replace(/\s+/g, ''), options);
3140
+ const spaceWidth = this.widthOfString(' ') + characterSpacing;
3141
+ wordSpacing = Math.max(0, (options.lineWidth - textWidth) / Math.max(1, words.length - 1) - spaceWidth);
3142
+ }
3143
+ contentWidth = Math.max(contentWidth, options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1));
3144
+ }
3145
+ });
3146
+ wrapper.wrap(string, options);
3147
+ } else {
3148
+ for (let line of string.split('\n')) {
3149
+ const lineWidth = this.widthOfString(line, options);
3150
+ this.y += lineHeight;
3151
+ contentWidth = Math.max(contentWidth, lineWidth);
3152
+ }
3153
+ }
3154
+ let contentHeight = this.y - y;
3155
+ if (options.height) contentHeight = Math.min(contentHeight, options.height);
3156
+ this.x = x;
3157
+ this.y = y;
3158
+ if (options.rotation === 0) {
3159
+ return {
3160
+ x,
3161
+ y,
3162
+ width: contentWidth,
3163
+ height: contentHeight
3164
+ };
3165
+ } else if (options.rotation === 90) {
3166
+ return {
3167
+ x: x,
3168
+ y: y - contentWidth,
3169
+ width: contentHeight,
3170
+ height: contentWidth
3171
+ };
3172
+ } else if (options.rotation === 180) {
3173
+ return {
3174
+ x: x - contentWidth,
3175
+ y: y - contentHeight,
3176
+ width: contentWidth,
3177
+ height: contentHeight
3178
+ };
3179
+ } else if (options.rotation === 270) {
3180
+ return {
3181
+ x: x - contentHeight,
3182
+ y: y,
3183
+ width: contentHeight,
3184
+ height: contentWidth
3185
+ };
3186
+ }
3187
+ const cos = cosine(options.rotation);
3188
+ const sin = sine(options.rotation);
3189
+ const x1 = x;
3190
+ const y1 = y;
3191
+ const x2 = x + contentWidth * cos;
3192
+ const y2 = y - contentWidth * sin;
3193
+ const x3 = x + contentWidth * cos + contentHeight * sin;
3194
+ const y3 = y - contentWidth * sin + contentHeight * cos;
3195
+ const x4 = x + contentHeight * sin;
3196
+ const y4 = y + contentHeight * cos;
3197
+ const xMin = Math.min(x1, x2, x3, x4);
3198
+ const xMax = Math.max(x1, x2, x3, x4);
3199
+ const yMin = Math.min(y1, y2, y3, y4);
3200
+ const yMax = Math.max(y1, y2, y3, y4);
3201
+ return {
3202
+ x: xMin,
3203
+ y: yMin,
3204
+ width: xMax - xMin,
3205
+ height: yMax - yMin
3206
+ };
3319
3207
  },
3320
3208
  heightOfString(text, options) {
3321
3209
  const {
@@ -3323,11 +3211,10 @@ var TextMixin = {
3323
3211
  y
3324
3212
  } = this;
3325
3213
  options = this._initOptions(options);
3326
- options.height = Infinity; // don't break pages
3327
-
3214
+ options.height = Infinity;
3328
3215
  const lineGap = options.lineGap || this._lineGap || 0;
3329
3216
  this._text(text, this.x, this.y, options, () => {
3330
- return this.y += this.currentLineHeight(true) + lineGap;
3217
+ this.y += this.currentLineHeight(true) + lineGap;
3331
3218
  });
3332
3219
  const height = this.y - y;
3333
3220
  this.x = x;
@@ -3425,12 +3312,12 @@ var TextMixin = {
3425
3312
  wrapper.on('sectionStart', () => {
3426
3313
  const pos = indent + itemIndent * (level - 1);
3427
3314
  this.x += pos;
3428
- return wrapper.lineWidth -= pos;
3315
+ wrapper.lineWidth -= pos;
3429
3316
  });
3430
3317
  wrapper.on('sectionEnd', () => {
3431
3318
  const pos = indent + itemIndent * (level - 1);
3432
3319
  this.x -= pos;
3433
- return wrapper.lineWidth += pos;
3320
+ wrapper.lineWidth += pos;
3434
3321
  });
3435
3322
  wrapper.wrap(listItem, options);
3436
3323
  };
@@ -3447,11 +3334,7 @@ var TextMixin = {
3447
3334
  options = x;
3448
3335
  x = null;
3449
3336
  }
3450
-
3451
- // clone options object
3452
3337
  const result = Object.assign({}, options);
3453
-
3454
- // extend options with previous values for continued text
3455
3338
  if (this._textOptions) {
3456
3339
  for (let key in this._textOptions) {
3457
3340
  const val = this._textOptions[key];
@@ -3462,16 +3345,12 @@ var TextMixin = {
3462
3345
  }
3463
3346
  }
3464
3347
  }
3465
-
3466
- // Update the current position
3467
3348
  if (x != null) {
3468
3349
  this.x = x;
3469
3350
  }
3470
3351
  if (y != null) {
3471
3352
  this.y = y;
3472
3353
  }
3473
-
3474
- // wrap to margins if no x or y position passed
3475
3354
  if (result.lineBreak !== false) {
3476
3355
  if (result.width == null) {
3477
3356
  result.width = this.page.width - this.x - this.page.margins.right;
@@ -3483,8 +3362,9 @@ var TextMixin = {
3483
3362
  }
3484
3363
  if (result.columnGap == null) {
3485
3364
  result.columnGap = 18;
3486
- } // 1/4 inch
3487
-
3365
+ }
3366
+ result.rotation = Number(options.rotation ?? 0) % 360;
3367
+ if (result.rotation < 0) result.rotation += 360;
3488
3368
  return result;
3489
3369
  },
3490
3370
  _line(text) {
@@ -3493,9 +3373,9 @@ var TextMixin = {
3493
3373
  this._fragment(text, this.x, this.y, options);
3494
3374
  const lineGap = options.lineGap || this._lineGap || 0;
3495
3375
  if (!wrapper) {
3496
- return this.x += this.widthOfString(text);
3376
+ this.x += this.widthOfString(text, options);
3497
3377
  } else {
3498
- return this.y += this.currentLineHeight(true) + lineGap;
3378
+ this.y += this.currentLineHeight(true) + lineGap;
3499
3379
  }
3500
3380
  },
3501
3381
  _fragment(text, x, y, options) {
@@ -3504,13 +3384,10 @@ var TextMixin = {
3504
3384
  if (text.length === 0) {
3505
3385
  return;
3506
3386
  }
3507
-
3508
- // handle options
3509
3387
  const align = options.align || 'left';
3510
3388
  let wordSpacing = options.wordSpacing || 0;
3511
3389
  const characterSpacing = options.characterSpacing || 0;
3512
-
3513
- // text alignments
3390
+ const horizontalScaling = options.horizontalScaling || 100;
3514
3391
  if (options.width) {
3515
3392
  switch (align) {
3516
3393
  case 'right':
@@ -3521,7 +3398,6 @@ var TextMixin = {
3521
3398
  x += options.lineWidth / 2 - options.textWidth / 2;
3522
3399
  break;
3523
3400
  case 'justify':
3524
- // calculate the word spacing value
3525
3401
  words = text.trim().split(/\s+/);
3526
3402
  textWidth = this.widthOfString(text.replace(/\s+/g, ''), options);
3527
3403
  var spaceWidth = this.widthOfString(' ') + characterSpacing;
@@ -3529,8 +3405,6 @@ var TextMixin = {
3529
3405
  break;
3530
3406
  }
3531
3407
  }
3532
-
3533
- // text baseline alignments based on http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
3534
3408
  if (typeof options.baseline === 'number') {
3535
3409
  dy = -options.baseline;
3536
3410
  } else {
@@ -3563,11 +3437,7 @@ var TextMixin = {
3563
3437
  }
3564
3438
  dy = dy / 1000 * this._fontSize;
3565
3439
  }
3566
-
3567
- // calculate the actual rendered width of the string after word and character spacing
3568
3440
  const renderedWidth = options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1);
3569
-
3570
- // create link annotations if the link option is given
3571
3441
  if (options.link != null) {
3572
3442
  this.link(x, y, renderedWidth, this.currentLineHeight(), options.link);
3573
3443
  }
@@ -3577,8 +3447,6 @@ var TextMixin = {
3577
3447
  if (options.destination != null) {
3578
3448
  this.addNamedDestination(options.destination, 'XYZ', x, y, null);
3579
3449
  }
3580
-
3581
- // create underline
3582
3450
  if (options.underline) {
3583
3451
  this.save();
3584
3452
  if (!options.stroke) {
@@ -3592,8 +3460,6 @@ var TextMixin = {
3592
3460
  this.stroke();
3593
3461
  this.restore();
3594
3462
  }
3595
-
3596
- // create strikethrough line
3597
3463
  if (options.strike) {
3598
3464
  this.save();
3599
3465
  if (!options.stroke) {
@@ -3608,8 +3474,6 @@ var TextMixin = {
3608
3474
  this.restore();
3609
3475
  }
3610
3476
  this.save();
3611
-
3612
- // oblique (angle in degrees or boolean)
3613
3477
  if (options.oblique) {
3614
3478
  let skew;
3615
3479
  if (typeof options.oblique === 'number') {
@@ -3621,40 +3485,24 @@ var TextMixin = {
3621
3485
  this.transform(1, 0, skew, 1, -skew * dy, 0);
3622
3486
  this.transform(1, 0, 0, 1, -x, -y);
3623
3487
  }
3624
-
3625
- // flip coordinate system
3626
3488
  this.transform(1, 0, 0, -1, 0, this.page.height);
3627
3489
  y = this.page.height - y - dy;
3628
-
3629
- // add current font to page if necessary
3630
3490
  if (this.page.fonts[this._font.id] == null) {
3631
3491
  this.page.fonts[this._font.id] = this._font.ref();
3632
3492
  }
3633
-
3634
- // begin the text object
3635
3493
  this.addContent('BT');
3636
-
3637
- // text position
3638
- this.addContent(`1 0 0 1 ${number$2(x)} ${number$2(y)} Tm`);
3639
-
3640
- // font and font size
3641
- this.addContent(`/${this._font.id} ${number$2(this._fontSize)} Tf`);
3642
-
3643
- // rendering mode
3494
+ this.addContent(`1 0 0 1 ${number(x)} ${number(y)} Tm`);
3495
+ this.addContent(`/${this._font.id} ${number(this._fontSize)} Tf`);
3644
3496
  const mode = options.fill && options.stroke ? 2 : options.stroke ? 1 : 0;
3645
3497
  if (mode) {
3646
3498
  this.addContent(`${mode} Tr`);
3647
3499
  }
3648
-
3649
- // Character spacing
3650
3500
  if (characterSpacing) {
3651
- this.addContent(`${number$2(characterSpacing)} Tc`);
3501
+ this.addContent(`${number(characterSpacing)} Tc`);
3502
+ }
3503
+ if (horizontalScaling !== 100) {
3504
+ this.addContent(`${horizontalScaling} Tz`);
3652
3505
  }
3653
-
3654
- // Add the actual text
3655
- // If we have a word spacing value, we need to encode each word separately
3656
- // since the normal Tw operator only works on character code 32, which isn't
3657
- // used for embedded fonts.
3658
3506
  if (wordSpacing) {
3659
3507
  words = text.trim().split(/\s+/);
3660
3508
  wordSpacing += this.widthOfString(' ') + characterSpacing;
@@ -3665,9 +3513,6 @@ var TextMixin = {
3665
3513
  const [encodedWord, positionsWord] = this._font.encode(word, options.features);
3666
3514
  encoded = encoded.concat(encodedWord);
3667
3515
  positions = positions.concat(positionsWord);
3668
-
3669
- // add the word spacing to the end of the word
3670
- // clone object because of cache
3671
3516
  const space = {};
3672
3517
  const object = positions[positions.length - 1];
3673
3518
  for (let key in object) {
@@ -3684,60 +3529,42 @@ var TextMixin = {
3684
3529
  const commands = [];
3685
3530
  let last = 0;
3686
3531
  let hadOffset = false;
3687
-
3688
- // Adds a segment of text to the TJ command buffer
3689
3532
  const addSegment = cur => {
3690
3533
  if (last < cur) {
3691
3534
  const hex = encoded.slice(last, cur).join('');
3692
3535
  const advance = positions[cur - 1].xAdvance - positions[cur - 1].advanceWidth;
3693
- commands.push(`<${hex}> ${number$2(-advance)}`);
3536
+ commands.push(`<${hex}> ${number(-advance)}`);
3694
3537
  }
3695
- return last = cur;
3538
+ last = cur;
3696
3539
  };
3697
-
3698
- // Flushes the current TJ commands to the output stream
3699
3540
  const flush = i => {
3700
3541
  addSegment(i);
3701
3542
  if (commands.length > 0) {
3702
3543
  this.addContent(`[${commands.join(' ')}] TJ`);
3703
- return commands.length = 0;
3544
+ commands.length = 0;
3704
3545
  }
3705
3546
  };
3706
3547
  for (i = 0; i < positions.length; i++) {
3707
- // If we have an x or y offset, we have to break out of the current TJ command
3708
- // so we can move the text position.
3709
3548
  const pos = positions[i];
3710
3549
  if (pos.xOffset || pos.yOffset) {
3711
- // Flush the current buffer
3712
3550
  flush(i);
3713
-
3714
- // Move the text position and flush just the current character
3715
- this.addContent(`1 0 0 1 ${number$2(x + pos.xOffset * scale)} ${number$2(y + pos.yOffset * scale)} Tm`);
3551
+ this.addContent(`1 0 0 1 ${number(x + pos.xOffset * scale)} ${number(y + pos.yOffset * scale)} Tm`);
3716
3552
  flush(i + 1);
3717
3553
  hadOffset = true;
3718
3554
  } else {
3719
- // If the last character had an offset, reset the text position
3720
3555
  if (hadOffset) {
3721
- this.addContent(`1 0 0 1 ${number$2(x)} ${number$2(y)} Tm`);
3556
+ this.addContent(`1 0 0 1 ${number(x)} ${number(y)} Tm`);
3722
3557
  hadOffset = false;
3723
3558
  }
3724
-
3725
- // Group segments that don't have any advance adjustments
3726
3559
  if (pos.xAdvance - pos.advanceWidth !== 0) {
3727
3560
  addSegment(i + 1);
3728
3561
  }
3729
3562
  }
3730
3563
  x += pos.xAdvance * scale;
3731
3564
  }
3732
-
3733
- // Flush any remaining commands
3734
3565
  flush(i);
3735
-
3736
- // end the text object
3737
3566
  this.addContent('ET');
3738
-
3739
- // restore flipped coordinate system
3740
- return this.restore();
3567
+ this.restore();
3741
3568
  }
3742
3569
  };
3743
3570
 
@@ -3755,8 +3582,6 @@ class JPEG {
3755
3582
  if (this.data.readUInt16BE(0) !== 0xffd8) {
3756
3583
  throw 'SOI not found in JPEG';
3757
3584
  }
3758
-
3759
- // Parse the EXIF orientation
3760
3585
  this.orientation = exif.fromBuffer(this.data).Orientation || 1;
3761
3586
  let pos = 2;
3762
3587
  while (pos < this.data.length) {
@@ -3793,16 +3618,10 @@ class JPEG {
3793
3618
  ColorSpace: this.colorSpace,
3794
3619
  Filter: 'DCTDecode'
3795
3620
  });
3796
-
3797
- // add extra decode params for CMYK images. By swapping the
3798
- // min and max values from the default, we invert the colors. See
3799
- // section 4.8.4 of the spec.
3800
3621
  if (this.colorSpace === 'DeviceCMYK') {
3801
3622
  this.obj.data['Decode'] = [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0];
3802
3623
  }
3803
3624
  this.obj.end(this.data);
3804
-
3805
- // free memory
3806
3625
  return this.data = null;
3807
3626
  }
3808
3627
  }
@@ -3845,24 +3664,14 @@ class PNGImage {
3845
3664
  if (this.image.palette.length === 0) {
3846
3665
  this.obj.data['ColorSpace'] = this.image.colorSpace;
3847
3666
  } else {
3848
- // embed the color palette in the PDF as an object stream
3849
3667
  const palette = this.document.ref();
3850
3668
  palette.end(Buffer.from(this.image.palette));
3851
-
3852
- // build the color space array for the image
3853
3669
  this.obj.data['ColorSpace'] = ['Indexed', 'DeviceRGB', this.image.palette.length / 3 - 1, palette];
3854
3670
  }
3855
-
3856
- // For PNG color types 0, 2 and 3, the transparency data is stored in
3857
- // a dedicated PNG chunk.
3858
3671
  if (this.image.transparency.grayscale != null) {
3859
- // Use Color Key Masking (spec section 4.8.5)
3860
- // An array with N elements, where N is two times the number of color components.
3861
3672
  const val = this.image.transparency.grayscale;
3862
3673
  this.obj.data['Mask'] = [val, val];
3863
3674
  } else if (this.image.transparency.rgb) {
3864
- // Use Color Key Masking (spec section 4.8.5)
3865
- // An array with N elements, where N is two times the number of color components.
3866
3675
  const {
3867
3676
  rgb
3868
3677
  } = this.image.transparency;
@@ -3872,14 +3681,9 @@ class PNGImage {
3872
3681
  }
3873
3682
  this.obj.data['Mask'] = mask;
3874
3683
  } else if (this.image.transparency.indexed) {
3875
- // Create a transparency SMask for the image based on the data
3876
- // in the PLTE and tRNS sections. See below for details on SMasks.
3877
3684
  dataDecoded = true;
3878
3685
  return this.loadIndexedAlphaChannel();
3879
3686
  } else if (hasAlphaChannel) {
3880
- // For PNG color types 4 and 6, the transparency data is stored as a alpha
3881
- // channel mixed in with the main image data. Separate this data out into an
3882
- // SMask object and store it separately in the PDF.
3883
3687
  dataDecoded = true;
3884
3688
  return this.splitAlphaChannel();
3885
3689
  }
@@ -3903,11 +3707,7 @@ class PNGImage {
3903
3707
  sMask.end(this.alphaChannel);
3904
3708
  this.obj.data['SMask'] = sMask;
3905
3709
  }
3906
-
3907
- // add the actual image data
3908
3710
  this.obj.end(this.imgData);
3909
-
3910
- // free memory
3911
3711
  this.image = null;
3912
3712
  return this.imgData = null;
3913
3713
  }
@@ -3920,7 +3720,6 @@ class PNGImage {
3920
3720
  const alphaChannel = Buffer.alloc(pixelCount);
3921
3721
  let i = p = a = 0;
3922
3722
  const len = pixels.length;
3923
- // For 16bit images copy only most significant byte (MSB) - PNG data is always stored in network byte order (MSB first)
3924
3723
  const skipByteCount = this.image.bits === 16 ? 1 : 0;
3925
3724
  while (i < len) {
3926
3725
  for (let colorIndex = 0; colorIndex < colorCount; colorIndex++) {
@@ -3955,10 +3754,6 @@ class PNGImage {
3955
3754
  }
3956
3755
  }
3957
3756
 
3958
- /*
3959
- PDFImage - embeds images in PDF documents
3960
- By Devon Govett
3961
- */
3962
3757
  class PDFImage {
3963
3758
  static open(src, label) {
3964
3759
  let data;
@@ -3990,18 +3785,17 @@ class PDFImage {
3990
3785
  var ImagesMixin = {
3991
3786
  initImages() {
3992
3787
  this._imageRegistry = {};
3993
- return this._imageCount = 0;
3788
+ this._imageCount = 0;
3994
3789
  },
3995
3790
  image(src, x, y) {
3996
3791
  let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
3997
- let bh, bp, bw, image, ip, left, left1, rotateAngle, originX, originY;
3792
+ let bh, bp, bw, image, ip, left, left1, originX, originY;
3998
3793
  if (typeof x === 'object') {
3999
3794
  options = x;
4000
3795
  x = null;
4001
3796
  }
4002
-
4003
- // Ignore orientation based on document options or image options
4004
3797
  const ignoreOrientation = options.ignoreOrientation || options.ignoreOrientation !== false && this.options.ignoreOrientation;
3798
+ const inDocumentFlow = typeof y !== 'number';
4005
3799
  x = (left = x != null ? x : options.x) != null ? left : this.x;
4006
3800
  y = (left1 = y != null ? y : options.y) != null ? left1 : this.y;
4007
3801
  if (typeof src === 'string') {
@@ -4024,8 +3818,6 @@ var ImagesMixin = {
4024
3818
  width,
4025
3819
  height
4026
3820
  } = image;
4027
-
4028
- // If EXIF orientation calls for it, swap width and height
4029
3821
  if (!ignoreOrientation && image.orientation > 4) {
4030
3822
  [width, height] = [height, width];
4031
3823
  }
@@ -4077,80 +3869,70 @@ var ImagesMixin = {
4077
3869
  y = y + bh - h;
4078
3870
  }
4079
3871
  }
3872
+ let rotateAngle = 0;
3873
+ let xTransform = x;
3874
+ let yTransform = y;
3875
+ let hTransform = h;
3876
+ let wTransform = w;
4080
3877
  if (!ignoreOrientation) {
4081
3878
  switch (image.orientation) {
4082
- // No orientation (need to flip image, though, because of the default transform matrix on the document)
4083
3879
  default:
4084
3880
  case 1:
4085
- h = -h;
4086
- y -= h;
4087
- rotateAngle = 0;
3881
+ hTransform = -h;
3882
+ yTransform += h;
4088
3883
  break;
4089
- // Flip Horizontal
4090
3884
  case 2:
4091
- w = -w;
4092
- h = -h;
4093
- x -= w;
4094
- y -= h;
4095
- rotateAngle = 0;
3885
+ wTransform = -w;
3886
+ hTransform = -h;
3887
+ xTransform += w;
3888
+ yTransform += h;
4096
3889
  break;
4097
- // Rotate 180 degrees
4098
3890
  case 3:
4099
3891
  originX = x;
4100
3892
  originY = y;
4101
- h = -h;
4102
- x -= w;
3893
+ hTransform = -h;
3894
+ xTransform -= w;
4103
3895
  rotateAngle = 180;
4104
3896
  break;
4105
- // Flip vertical
4106
3897
  case 4:
4107
- // Do nothing, image will be flipped
4108
-
4109
3898
  break;
4110
- // Flip horizontally and rotate 270 degrees CW
4111
3899
  case 5:
4112
3900
  originX = x;
4113
3901
  originY = y;
4114
- [w, h] = [h, w];
4115
- y -= h;
3902
+ wTransform = h;
3903
+ hTransform = w;
3904
+ yTransform -= hTransform;
4116
3905
  rotateAngle = 90;
4117
3906
  break;
4118
- // Rotate 90 degrees CW
4119
3907
  case 6:
4120
3908
  originX = x;
4121
3909
  originY = y;
4122
- [w, h] = [h, w];
4123
- h = -h;
3910
+ wTransform = h;
3911
+ hTransform = -w;
4124
3912
  rotateAngle = 90;
4125
3913
  break;
4126
- // Flip horizontally and rotate 90 degrees CW
4127
3914
  case 7:
4128
3915
  originX = x;
4129
3916
  originY = y;
4130
- [w, h] = [h, w];
4131
- h = -h;
4132
- w = -w;
4133
- x -= w;
3917
+ hTransform = -w;
3918
+ wTransform = -h;
3919
+ xTransform += h;
4134
3920
  rotateAngle = 90;
4135
3921
  break;
4136
- // Rotate 270 degrees CW
4137
3922
  case 8:
4138
3923
  originX = x;
4139
3924
  originY = y;
4140
- [w, h] = [h, w];
4141
- h = -h;
4142
- x -= w;
4143
- y -= h;
3925
+ wTransform = h;
3926
+ hTransform = -w;
3927
+ xTransform -= h;
3928
+ yTransform += w;
4144
3929
  rotateAngle = -90;
4145
3930
  break;
4146
3931
  }
4147
3932
  } else {
4148
- h = -h;
4149
- y -= h;
4150
- rotateAngle = 0;
3933
+ hTransform = -h;
3934
+ yTransform += h;
4151
3935
  }
4152
-
4153
- // create link annotations if the link option is given
4154
3936
  if (options.link != null) {
4155
3937
  this.link(x, y, w, h, options.link);
4156
3938
  }
@@ -4160,9 +3942,7 @@ var ImagesMixin = {
4160
3942
  if (options.destination != null) {
4161
3943
  this.addNamedDestination(options.destination, 'XYZ', x, y, null);
4162
3944
  }
4163
-
4164
- // Set the current y position to below the image if it is in the document flow
4165
- if (this.y === y) {
3945
+ if (inDocumentFlow) {
4166
3946
  this.y += h;
4167
3947
  }
4168
3948
  this.save();
@@ -4171,7 +3951,7 @@ var ImagesMixin = {
4171
3951
  origin: [originX, originY]
4172
3952
  });
4173
3953
  }
4174
- this.transform(w, 0, 0, h, x, y);
3954
+ this.transform(wTransform, 0, 0, hTransform, xTransform, yTransform);
4175
3955
  this.addContent(`/${image.label} Do`);
4176
3956
  this.restore();
4177
3957
  return this;
@@ -4197,19 +3977,17 @@ var AnnotationsMixin = {
4197
3977
  options.Rect = this._convertRect(x, y, w, h);
4198
3978
  options.Border = [0, 0, 0];
4199
3979
  if (options.Subtype === 'Link' && typeof options.F === 'undefined') {
4200
- options.F = 1 << 2; // Print Annotation Flag
3980
+ options.F = 1 << 2;
4201
3981
  }
4202
3982
  if (options.Subtype !== 'Link') {
4203
3983
  if (options.C == null) {
4204
3984
  options.C = this._normalizeColor(options.color || [0, 0, 0]);
4205
3985
  }
4206
- } // convert colors
3986
+ }
4207
3987
  delete options.color;
4208
3988
  if (typeof options.Dest === 'string') {
4209
3989
  options.Dest = new String(options.Dest);
4210
3990
  }
4211
-
4212
- // Capitalize keys
4213
3991
  for (let key in options) {
4214
3992
  const val = options[key];
4215
3993
  options[key[0].toUpperCase() + key.slice(1)] = val;
@@ -4223,7 +4001,9 @@ var AnnotationsMixin = {
4223
4001
  let options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
4224
4002
  options.Subtype = 'Text';
4225
4003
  options.Contents = new String(contents);
4226
- options.Name = 'Comment';
4004
+ if (options.Name == null) {
4005
+ options.Name = 'Comment';
4006
+ }
4227
4007
  if (options.color == null) {
4228
4008
  options.color = [243, 223, 92];
4229
4009
  }
@@ -4243,7 +4023,6 @@ var AnnotationsMixin = {
4243
4023
  let options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
4244
4024
  options.Subtype = 'Link';
4245
4025
  if (typeof url === 'number') {
4246
- // Link to a page in the document (the page must already exist)
4247
4026
  const pages = this._root.data.Pages.data;
4248
4027
  if (url >= 0 && url < pages.Kids.length) {
4249
4028
  options.A = this.ref({
@@ -4255,7 +4034,6 @@ var AnnotationsMixin = {
4255
4034
  throw new Error(`The document has no page ${url}`);
4256
4035
  }
4257
4036
  } else {
4258
- // Link to an external url
4259
4037
  options.A = this.ref({
4260
4038
  S: 'URI',
4261
4039
  URI: new String(url)
@@ -4318,14 +4096,11 @@ var AnnotationsMixin = {
4318
4096
  fileAnnotation(x, y, w, h) {
4319
4097
  let file = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
4320
4098
  let options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
4321
- // create hidden file
4322
4099
  const filespec = this.file(file.src, Object.assign({
4323
4100
  hidden: true
4324
4101
  }, file));
4325
4102
  options.Subtype = 'FileAttachment';
4326
4103
  options.FS = filespec;
4327
-
4328
- // add description from filespec unless description (Contents) has already been set
4329
4104
  if (options.Contents) {
4330
4105
  options.Contents = new String(options.Contents);
4331
4106
  } else if (filespec.data.Desc) {
@@ -4334,14 +4109,9 @@ var AnnotationsMixin = {
4334
4109
  return this.annotate(x, y, w, h, options);
4335
4110
  },
4336
4111
  _convertRect(x1, y1, w, h) {
4337
- // flip y1 and y2
4338
4112
  let y2 = y1;
4339
4113
  y1 += h;
4340
-
4341
- // make x2
4342
4114
  let x2 = x1 + w;
4343
-
4344
- // apply current transformation matrix to points
4345
4115
  const [m0, m1, m2, m3, m4, m5] = this._ctm;
4346
4116
  x1 = m0 * x1 + m2 * y1 + m4;
4347
4117
  y1 = m1 * x1 + m3 * y1 + m5;
@@ -4405,7 +4175,7 @@ class PDFOutline {
4405
4175
 
4406
4176
  var OutlineMixin = {
4407
4177
  initOutline() {
4408
- return this.outline = new PDFOutline(this, null, null, null);
4178
+ this.outline = new PDFOutline(this, null, null, null);
4409
4179
  },
4410
4180
  endOutline() {
4411
4181
  this.outline.endOutline();
@@ -4416,11 +4186,6 @@ var OutlineMixin = {
4416
4186
  }
4417
4187
  };
4418
4188
 
4419
- /*
4420
- PDFStructureContent - a reference to a marked structure content
4421
- By Ben Schmidt
4422
- */
4423
-
4424
4189
  class PDFStructureContent {
4425
4190
  constructor(pageRef, mcid) {
4426
4191
  this.refs = [{
@@ -4433,10 +4198,6 @@ class PDFStructureContent {
4433
4198
  }
4434
4199
  }
4435
4200
 
4436
- /*
4437
- PDFStructureElement - represents an element in the PDF logical structure tree
4438
- By Ben Schmidt
4439
- */
4440
4201
  class PDFStructureElement {
4441
4202
  constructor(document, type) {
4442
4203
  let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
@@ -4446,7 +4207,6 @@ class PDFStructureElement {
4446
4207
  this._ended = false;
4447
4208
  this._flushed = false;
4448
4209
  this.dictionary = document.ref({
4449
- // Type: "StructElem",
4450
4210
  S: type
4451
4211
  });
4452
4212
  const data = this.dictionary.data;
@@ -4495,7 +4255,6 @@ class PDFStructureElement {
4495
4255
  this._addContentToParentTree(child);
4496
4256
  }
4497
4257
  if (typeof child === 'function' && this._attached) {
4498
- // _contentForClosure() adds the content to the parent tree
4499
4258
  child = this._contentForClosure(child);
4500
4259
  }
4501
4260
  this._children.push(child);
@@ -4572,10 +4331,6 @@ class PDFStructureElement {
4572
4331
  this.dictionary.data.K = [];
4573
4332
  this._children.forEach(child => this._flushChild(child));
4574
4333
  this.dictionary.end();
4575
-
4576
- // free memory used by children; the dictionary itself may still be
4577
- // referenced by a parent structure element or root, but we can
4578
- // at least trim the tree here
4579
4334
  this._children = [];
4580
4335
  this.dictionary.data.K = null;
4581
4336
  this._flushed = true;
@@ -4597,7 +4352,7 @@ class PDFStructureElement {
4597
4352
  this.dictionary.data.K.push(mcid);
4598
4353
  } else {
4599
4354
  this.dictionary.data.K.push({
4600
- Type: "MCR",
4355
+ Type: 'MCR',
4601
4356
  Pg: pageRef,
4602
4357
  MCID: mcid
4603
4358
  });
@@ -4607,25 +4362,18 @@ class PDFStructureElement {
4607
4362
  }
4608
4363
  }
4609
4364
 
4610
- /*
4611
- PDFNumberTree - represents a number tree object
4612
- */
4613
4365
  class PDFNumberTree extends PDFTree {
4614
4366
  _compareKeys(a, b) {
4615
4367
  return parseInt(a) - parseInt(b);
4616
4368
  }
4617
4369
  _keysName() {
4618
- return "Nums";
4370
+ return 'Nums';
4619
4371
  }
4620
4372
  _dataForKey(k) {
4621
4373
  return parseInt(k);
4622
4374
  }
4623
4375
  }
4624
4376
 
4625
- /*
4626
- Markings mixin - support marked content sequences in content streams
4627
- By Ben Schmidt
4628
- */
4629
4377
  var MarkingsMixin = {
4630
4378
  initMarkings(options) {
4631
4379
  this.structChildren = [];
@@ -4748,6 +4496,9 @@ var MarkingsMixin = {
4748
4496
  }
4749
4497
  return this._root.data.MarkInfo;
4750
4498
  },
4499
+ hasMarkInfoDictionary() {
4500
+ return !!this._root.data.MarkInfo;
4501
+ },
4751
4502
  getStructTreeRoot() {
4752
4503
  if (!this._root.data.StructTreeRoot) {
4753
4504
  this._root.data.StructTreeRoot = this.ref({
@@ -4762,7 +4513,6 @@ var MarkingsMixin = {
4762
4513
  return this.getStructTreeRoot().data.ParentTree;
4763
4514
  },
4764
4515
  createStructParentTreeNextKey() {
4765
- // initialise the MarkInfo dictionary
4766
4516
  this.getMarkInfoDictionary();
4767
4517
  const structTreeRoot = this.getStructTreeRoot();
4768
4518
  const key = structTreeRoot.data.ParentTreeNextKey++;
@@ -4826,10 +4576,6 @@ const FORMAT_DEFAULT = {
4826
4576
  }
4827
4577
  };
4828
4578
  var AcroFormMixin = {
4829
- /**
4830
- * Must call if adding AcroForms to a document. Must also call font() before
4831
- * this method to set the default font.
4832
- */
4833
4579
  initForm() {
4834
4580
  if (!this._font) {
4835
4581
  throw new Error('Must set a font before calling initForm method');
@@ -4852,9 +4598,6 @@ var AcroFormMixin = {
4852
4598
  this._root.data.AcroForm = AcroForm;
4853
4599
  return this;
4854
4600
  },
4855
- /**
4856
- * Called automatically by document.js
4857
- */
4858
4601
  endAcroForm() {
4859
4602
  if (this._root.data.AcroForm) {
4860
4603
  if (!Object.keys(this._acroform.fonts).length && !this._acroform.defaultFont) {
@@ -4880,13 +4623,6 @@ var AcroFormMixin = {
4880
4623
  }
4881
4624
  return this;
4882
4625
  },
4883
- /**
4884
- * Creates and adds a form field to the document. Form fields are intermediate
4885
- * nodes in a PDF form that are used to specify form name heirarchy and form
4886
- * value defaults.
4887
- * @param {string} name - field name (T attribute in field dictionary)
4888
- * @param {object} options - other attributes to include in field dictionary
4889
- */
4890
4626
  formField(name) {
4891
4627
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
4892
4628
  let fieldDict = this._fieldDict(name, null, options);
@@ -4894,26 +4630,13 @@ var AcroFormMixin = {
4894
4630
  this._addToParent(fieldRef);
4895
4631
  return fieldRef;
4896
4632
  },
4897
- /**
4898
- * Creates and adds a Form Annotation to the document. Form annotations are
4899
- * called Widget annotations internally within a PDF file.
4900
- * @param {string} name - form field name (T attribute of widget annotation
4901
- * dictionary)
4902
- * @param {number} x
4903
- * @param {number} y
4904
- * @param {number} w
4905
- * @param {number} h
4906
- * @param {object} options
4907
- */
4908
4633
  formAnnotation(name, type, x, y, w, h) {
4909
4634
  let options = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {};
4910
4635
  let fieldDict = this._fieldDict(name, type, options);
4911
4636
  fieldDict.Subtype = 'Widget';
4912
4637
  if (fieldDict.F === undefined) {
4913
- fieldDict.F = 4; // print the annotation
4638
+ fieldDict.F = 4;
4914
4639
  }
4915
-
4916
- // Add Field annot to page, and get it's ref
4917
4640
  this.annotate(x, y, w, h, fieldDict);
4918
4641
  let annotRef = this.page.annotations[this.page.annotations.length - 1];
4919
4642
  return this._addToParent(annotRef);
@@ -5081,23 +4804,18 @@ var AcroFormMixin = {
5081
4804
  delete options.align;
5082
4805
  }
5083
4806
  if (result !== 0) {
5084
- options.Q = result; // default
4807
+ options.Q = result;
5085
4808
  }
5086
4809
  return options;
5087
4810
  },
5088
4811
  _resolveFont(options) {
5089
- // add current font to document-level AcroForm dict if necessary
5090
4812
  if (this._acroform.fonts[this._font.id] == null) {
5091
4813
  this._acroform.fonts[this._font.id] = this._font.ref();
5092
4814
  }
5093
-
5094
- // add current font to field's resource dict (RD) if not the default acroform font
5095
4815
  if (this._acroform.defaultFont !== this._font.name) {
5096
4816
  options.DR = {
5097
4817
  Font: {}
5098
4818
  };
5099
-
5100
- // Get the fontSize option. If not set use auto sizing
5101
4819
  const fontSize = options.fontSize || 0;
5102
4820
  options.DR.Font[this._font.id] = this._font.ref();
5103
4821
  options.DA = new String(`/${this._font.id} ${fontSize} Tf 0 g`);
@@ -5149,19 +4867,6 @@ var AcroFormMixin = {
5149
4867
  };
5150
4868
 
5151
4869
  var AttachmentsMixin = {
5152
- /**
5153
- * Embed contents of `src` in PDF
5154
- * @param {Buffer | ArrayBuffer | string} src input Buffer, ArrayBuffer, base64 encoded string or path to file
5155
- * @param {object} options
5156
- * * options.name: filename to be shown in PDF, will use `src` if none set
5157
- * * options.type: filetype to be shown in PDF
5158
- * * options.description: description to be shown in PDF
5159
- * * options.hidden: if true, do not add attachment to EmbeddedFiles dictionary. Useful for file attachment annotations
5160
- * * options.creationDate: override creation date
5161
- * * options.modifiedDate: override modified date
5162
- * * options.relationship: Relationship between the PDF document and its attached file. Can be 'Alternative', 'Data', 'Source', 'Supplement' or 'Unspecified'.
5163
- * @returns filespec reference
5164
- */
5165
4870
  file(src) {
5166
4871
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
5167
4872
  options.name = options.name || src;
@@ -5190,8 +4895,6 @@ var AttachmentsMixin = {
5190
4895
  if (!data) {
5191
4896
  throw new Error(`Could not read contents of file at filepath ${src}`);
5192
4897
  }
5193
-
5194
- // update CreationDate and ModDate
5195
4898
  const {
5196
4899
  birthtime,
5197
4900
  ctime
@@ -5200,26 +4903,18 @@ var AttachmentsMixin = {
5200
4903
  refBody.Params.ModDate = ctime;
5201
4904
  }
5202
4905
  }
5203
-
5204
- // override creation date and modified date
5205
4906
  if (options.creationDate instanceof Date) {
5206
4907
  refBody.Params.CreationDate = options.creationDate;
5207
4908
  }
5208
4909
  if (options.modifiedDate instanceof Date) {
5209
4910
  refBody.Params.ModDate = options.modifiedDate;
5210
4911
  }
5211
- // add optional subtype
5212
4912
  if (options.type) {
5213
4913
  refBody.Subtype = options.type.replace('/', '#2F');
5214
4914
  }
5215
-
5216
- // add checksum and size information
5217
4915
  const checksum = CryptoJS.MD5(CryptoJS.lib.WordArray.create(new Uint8Array(data)));
5218
4916
  refBody.Params.CheckSum = new String(checksum);
5219
4917
  refBody.Params.Size = data.byteLength;
5220
-
5221
- // save some space when embedding the same file again
5222
- // if a file with the same name and metadata exists, reuse its reference
5223
4918
  let ref;
5224
4919
  if (!this._fileRegistry) this._fileRegistry = {};
5225
4920
  let file = this._fileRegistry[options.name];
@@ -5233,7 +4928,6 @@ var AttachmentsMixin = {
5233
4928
  ref
5234
4929
  };
5235
4930
  }
5236
- // add filespec for embedded file
5237
4931
  const fileSpecBody = {
5238
4932
  Type: 'Filespec',
5239
4933
  AFRelationship: options.relationship,
@@ -5251,8 +4945,6 @@ var AttachmentsMixin = {
5251
4945
  if (!options.hidden) {
5252
4946
  this.addNamedEmbeddedFile(options.name, filespec);
5253
4947
  }
5254
-
5255
- // Add file to the catalogue to be PDF/A3 compliant
5256
4948
  if (this._root.data.AF) {
5257
4949
  this._root.data.AF.push(filespec);
5258
4950
  } else {
@@ -5261,8 +4953,6 @@ var AttachmentsMixin = {
5261
4953
  return filespec;
5262
4954
  }
5263
4955
  };
5264
-
5265
- /** check two embedded file metadata objects for equality */
5266
4956
  function isEqual(a, b) {
5267
4957
  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());
5268
4958
  }
@@ -5273,7 +4963,6 @@ var PDFA = {
5273
4963
  this.subset_conformance = pSubset.charAt(pSubset.length - 1).toUpperCase();
5274
4964
  this.subset = parseInt(pSubset.charAt(pSubset.length - 2));
5275
4965
  } else {
5276
- // Default to Basic conformance when user doesn't specify
5277
4966
  this.subset_conformance = 'B';
5278
4967
  this.subset = parseInt(pSubset.charAt(pSubset.length - 1));
5279
4968
  }
@@ -5358,6 +5047,656 @@ var SubsetMixin = {
5358
5047
  }
5359
5048
  };
5360
5049
 
5050
+ const ROW_FIELDS = ['height', 'minHeight', 'maxHeight'];
5051
+ const COLUMN_FIELDS = ['width', 'minWidth', 'maxWidth'];
5052
+ function memoize(fn, maxSize) {
5053
+ const cache = new Map();
5054
+ return function () {
5055
+ const key = arguments.length <= 0 ? undefined : arguments[0];
5056
+ if (!cache.has(key)) {
5057
+ cache.set(key, fn(...arguments));
5058
+ if (cache.size > maxSize) cache.delete(cache.keys().next());
5059
+ }
5060
+ return cache.get(key);
5061
+ };
5062
+ }
5063
+ function isObject(item) {
5064
+ return item && typeof item === 'object' && !Array.isArray(item);
5065
+ }
5066
+ function deepMerge(target) {
5067
+ if (!isObject(target)) return target;
5068
+ target = deepClone(target);
5069
+ for (var _len = arguments.length, sources = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
5070
+ sources[_key - 1] = arguments[_key];
5071
+ }
5072
+ for (const source of sources) {
5073
+ if (isObject(source)) {
5074
+ for (const key in source) {
5075
+ if (isObject(source[key])) {
5076
+ if (!(key in target)) target[key] = {};
5077
+ target[key] = deepMerge(target[key], source[key]);
5078
+ } else if (source[key] !== undefined) {
5079
+ target[key] = deepClone(source[key]);
5080
+ }
5081
+ }
5082
+ }
5083
+ }
5084
+ return target;
5085
+ }
5086
+ function deepClone(obj) {
5087
+ let result = obj;
5088
+ if (typeof obj == 'object') {
5089
+ result = Array.isArray(obj) ? [] : {};
5090
+ for (const key in obj) result[key] = deepClone(obj[key]);
5091
+ }
5092
+ return result;
5093
+ }
5094
+
5095
+ function normalizedDefaultStyle(defaultStyleInternal) {
5096
+ let defaultStyle = defaultStyleInternal;
5097
+ if (typeof defaultStyle !== 'object') defaultStyle = {
5098
+ text: defaultStyle
5099
+ };
5100
+ const defaultRowStyle = Object.fromEntries(Object.entries(defaultStyle).filter(_ref => {
5101
+ let [k] = _ref;
5102
+ return ROW_FIELDS.includes(k);
5103
+ }));
5104
+ const defaultColStyle = Object.fromEntries(Object.entries(defaultStyle).filter(_ref2 => {
5105
+ let [k] = _ref2;
5106
+ return COLUMN_FIELDS.includes(k);
5107
+ }));
5108
+ defaultStyle.padding = normalizeSides(defaultStyle.padding);
5109
+ defaultStyle.border = normalizeSides(defaultStyle.border);
5110
+ defaultStyle.borderColor = normalizeSides(defaultStyle.borderColor);
5111
+ defaultStyle.align = normalizeAlignment(defaultStyle.align);
5112
+ return {
5113
+ defaultStyle,
5114
+ defaultRowStyle,
5115
+ defaultColStyle
5116
+ };
5117
+ }
5118
+ function normalizedRowStyle(defaultRowStyle, rowStyleInternal, i) {
5119
+ let rowStyle = rowStyleInternal(i);
5120
+ if (rowStyle == null || typeof rowStyle !== 'object') {
5121
+ rowStyle = {
5122
+ height: rowStyle
5123
+ };
5124
+ }
5125
+ rowStyle.padding = normalizeSides(rowStyle.padding);
5126
+ rowStyle.border = normalizeSides(rowStyle.border);
5127
+ rowStyle.borderColor = normalizeSides(rowStyle.borderColor);
5128
+ rowStyle.align = normalizeAlignment(rowStyle.align);
5129
+ rowStyle = deepMerge(defaultRowStyle, rowStyle);
5130
+ const document = this.document;
5131
+ const page = document.page;
5132
+ const contentHeight = page.contentHeight;
5133
+ if (rowStyle.height == null || rowStyle.height === 'auto') {
5134
+ rowStyle.height = 'auto';
5135
+ } else {
5136
+ rowStyle.height = document.sizeToPoint(rowStyle.height, 0, page, contentHeight);
5137
+ }
5138
+ rowStyle.minHeight = document.sizeToPoint(rowStyle.minHeight, 0, page, contentHeight);
5139
+ rowStyle.maxHeight = document.sizeToPoint(rowStyle.maxHeight, 0, page, contentHeight);
5140
+ return rowStyle;
5141
+ }
5142
+ function normalizedColumnStyle(defaultColStyle, colStyleInternal, i) {
5143
+ let colStyle = colStyleInternal(i);
5144
+ if (colStyle == null || typeof colStyle !== 'object') {
5145
+ colStyle = {
5146
+ width: colStyle
5147
+ };
5148
+ }
5149
+ colStyle.padding = normalizeSides(colStyle.padding);
5150
+ colStyle.border = normalizeSides(colStyle.border);
5151
+ colStyle.borderColor = normalizeSides(colStyle.borderColor);
5152
+ colStyle.align = normalizeAlignment(colStyle.align);
5153
+ colStyle = deepMerge(defaultColStyle, colStyle);
5154
+ if (colStyle.width == null || colStyle.width === '*') {
5155
+ colStyle.width = '*';
5156
+ } else {
5157
+ colStyle.width = this.document.sizeToPoint(colStyle.width, 0, this.document.page, this._maxWidth);
5158
+ }
5159
+ colStyle.minWidth = this.document.sizeToPoint(colStyle.minWidth, 0, this.document.page, this._maxWidth);
5160
+ colStyle.maxWidth = this.document.sizeToPoint(colStyle.maxWidth, 0, this.document.page, this._maxWidth);
5161
+ return colStyle;
5162
+ }
5163
+ function normalizeAlignment(align) {
5164
+ return align == null || typeof align === 'string' ? {
5165
+ x: align,
5166
+ y: align
5167
+ } : align;
5168
+ }
5169
+
5170
+ function normalizeTable() {
5171
+ const doc = this.document;
5172
+ const opts = this.opts;
5173
+ let index = doc._tableIndex++;
5174
+ this._id = new String(opts.id ?? `table-${index}`);
5175
+ this._position = {
5176
+ x: doc.sizeToPoint(opts.position?.x, doc.x),
5177
+ y: doc.sizeToPoint(opts.position?.y, doc.y)
5178
+ };
5179
+ this._maxWidth = doc.sizeToPoint(opts.maxWidth, doc.page.width - doc.page.margins.right - this._position.x);
5180
+ const {
5181
+ defaultStyle,
5182
+ defaultColStyle,
5183
+ defaultRowStyle
5184
+ } = normalizedDefaultStyle(opts.defaultStyle);
5185
+ this._defaultStyle = defaultStyle;
5186
+ let colStyle;
5187
+ if (opts.columnStyles) {
5188
+ if (Array.isArray(opts.columnStyles)) {
5189
+ colStyle = i => opts.columnStyles[i];
5190
+ } else if (typeof opts.columnStyles === 'function') {
5191
+ colStyle = memoize(i => opts.columnStyles(i), Infinity);
5192
+ } else if (typeof opts.columnStyles === 'object') {
5193
+ colStyle = () => opts.columnStyles;
5194
+ }
5195
+ }
5196
+ if (!colStyle) colStyle = () => ({});
5197
+ this._colStyle = normalizedColumnStyle.bind(this, defaultColStyle, colStyle);
5198
+ let rowStyle;
5199
+ if (opts.rowStyles) {
5200
+ if (Array.isArray(opts.rowStyles)) {
5201
+ rowStyle = i => opts.rowStyles[i];
5202
+ } else if (typeof opts.rowStyles === 'function') {
5203
+ rowStyle = memoize(i => opts.rowStyles(i), 10);
5204
+ } else if (typeof opts.rowStyles === 'object') {
5205
+ rowStyle = () => opts.rowStyles;
5206
+ }
5207
+ }
5208
+ if (!rowStyle) rowStyle = () => ({});
5209
+ this._rowStyle = normalizedRowStyle.bind(this, defaultRowStyle, rowStyle);
5210
+ }
5211
+ function normalizeText(text) {
5212
+ if (text != null) text = `${text}`;
5213
+ return text;
5214
+ }
5215
+ function normalizeCell(cell, rowIndex, colIndex) {
5216
+ const colStyle = this._colStyle(colIndex);
5217
+ let rowStyle = this._rowStyle(rowIndex);
5218
+ const font = deepMerge({}, colStyle.font, rowStyle.font, cell.font);
5219
+ const customFont = Object.values(font).filter(v => v != null).length > 0;
5220
+ const doc = this.document;
5221
+ const rollbackFont = doc._fontSource;
5222
+ const rollbackFontSize = doc._fontSize;
5223
+ const rollbackFontFamily = doc._fontFamily;
5224
+ if (customFont) {
5225
+ if (font.src) doc.font(font.src, font.family);
5226
+ if (font.size) doc.fontSize(font.size);
5227
+ rowStyle = this._rowStyle(rowIndex);
5228
+ }
5229
+ cell.padding = normalizeSides(cell.padding);
5230
+ cell.border = normalizeSides(cell.border);
5231
+ cell.borderColor = normalizeSides(cell.borderColor);
5232
+ const config = deepMerge(this._defaultStyle, colStyle, rowStyle, cell);
5233
+ config.rowIndex = rowIndex;
5234
+ config.colIndex = colIndex;
5235
+ config.font = font ?? {};
5236
+ config.customFont = customFont;
5237
+ config.text = normalizeText(config.text);
5238
+ config.rowSpan = config.rowSpan ?? 1;
5239
+ config.colSpan = config.colSpan ?? 1;
5240
+ config.padding = normalizeSides(config.padding, '0.25em', x => doc.sizeToPoint(x, '0.25em'));
5241
+ config.border = normalizeSides(config.border, 1, x => doc.sizeToPoint(x, 1));
5242
+ config.borderColor = normalizeSides(config.borderColor, 'black', x => x ?? 'black');
5243
+ config.align = normalizeAlignment(config.align);
5244
+ config.align.x = config.align.x ?? 'left';
5245
+ config.align.y = config.align.y ?? 'top';
5246
+ config.textStroke = doc.sizeToPoint(config.textStroke, 0);
5247
+ config.textStrokeColor = config.textStrokeColor ?? 'black';
5248
+ config.textColor = config.textColor ?? 'black';
5249
+ config.textOptions = config.textOptions ?? {};
5250
+ config.id = new String(config.id ?? `${this._id}-${rowIndex}-${colIndex}`);
5251
+ config.type = config.type?.toUpperCase() === 'TH' ? 'TH' : 'TD';
5252
+ if (config.scope) {
5253
+ config.scope = config.scope.toLowerCase();
5254
+ if (config.scope === 'row') config.scope = 'Row';else if (config.scope === 'both') config.scope = 'Both';else if (config.scope === 'column') config.scope = 'Column';
5255
+ }
5256
+ if (typeof this.opts.debug === 'boolean') config.debug = this.opts.debug;
5257
+ if (customFont) doc.font(rollbackFont, rollbackFontFamily, rollbackFontSize);
5258
+ return config;
5259
+ }
5260
+ function normalizeRow(row, rowIndex) {
5261
+ if (!this._cellClaim) this._cellClaim = new Set();
5262
+ let colIndex = 0;
5263
+ return row.map(cell => {
5264
+ if (cell == null || typeof cell !== 'object') cell = {
5265
+ text: cell
5266
+ };
5267
+ while (this._cellClaim.has(`${rowIndex},${colIndex}`)) {
5268
+ colIndex++;
5269
+ }
5270
+ cell = normalizeCell.call(this, cell, rowIndex, colIndex);
5271
+ for (let i = 0; i < cell.rowSpan; i++) {
5272
+ for (let j = 0; j < cell.colSpan; j++) {
5273
+ this._cellClaim.add(`${rowIndex + i},${colIndex + j}`);
5274
+ }
5275
+ }
5276
+ colIndex += cell.colSpan;
5277
+ return cell;
5278
+ });
5279
+ }
5280
+
5281
+ function ensure(row) {
5282
+ this._columnWidths = [];
5283
+ ensureColumnWidths.call(this, row.reduce((a, cell) => a + cell.colSpan, 0));
5284
+ this._rowHeights = [];
5285
+ this._rowYPos = [this._position.y];
5286
+ this._rowBuffer = new Set();
5287
+ }
5288
+ function ensureColumnWidths(numCols) {
5289
+ let starColumnIndexes = [];
5290
+ let starMinAcc = 0;
5291
+ let unclaimedWidth = this._maxWidth;
5292
+ for (let i = 0; i < numCols; i++) {
5293
+ let col = this._colStyle(i);
5294
+ if (col.width === '*') {
5295
+ starColumnIndexes[i] = col;
5296
+ starMinAcc += col.minWidth;
5297
+ } else {
5298
+ unclaimedWidth -= col.width;
5299
+ this._columnWidths[i] = col.width;
5300
+ }
5301
+ }
5302
+ let starColCount = starColumnIndexes.reduce(x => x + 1, 0);
5303
+ if (starMinAcc >= unclaimedWidth) {
5304
+ starColumnIndexes.forEach((cell, i) => {
5305
+ this._columnWidths[i] = cell.minWidth;
5306
+ });
5307
+ } else if (starColCount > 0) {
5308
+ starColumnIndexes.forEach((col, i) => {
5309
+ let starSize = unclaimedWidth / starColCount;
5310
+ this._columnWidths[i] = Math.max(starSize, col.minWidth);
5311
+ if (col.maxWidth > 0) {
5312
+ this._columnWidths[i] = Math.min(this._columnWidths[i], col.maxWidth);
5313
+ }
5314
+ unclaimedWidth -= this._columnWidths[i];
5315
+ starColCount--;
5316
+ });
5317
+ }
5318
+ let tempX = this._position.x;
5319
+ this._columnXPos = Array.from(this._columnWidths, v => {
5320
+ const t = tempX;
5321
+ tempX += v;
5322
+ return t;
5323
+ });
5324
+ }
5325
+ function measure(row, rowIndex) {
5326
+ row.forEach(cell => this._rowBuffer.add(cell));
5327
+ if (rowIndex > 0) {
5328
+ this._rowYPos[rowIndex] = this._rowYPos[rowIndex - 1] + this._rowHeights[rowIndex - 1];
5329
+ }
5330
+ const rowStyle = this._rowStyle(rowIndex);
5331
+ let toRender = [];
5332
+ this._rowBuffer.forEach(cell => {
5333
+ if (cell.rowIndex + cell.rowSpan - 1 === rowIndex) {
5334
+ toRender.push(measureCell.call(this, cell, rowStyle.height));
5335
+ this._rowBuffer.delete(cell);
5336
+ }
5337
+ });
5338
+ let rowHeight = rowStyle.height;
5339
+ if (rowHeight === 'auto') {
5340
+ rowHeight = toRender.reduce((acc, cell) => {
5341
+ let minHeight = cell.textBounds.height + cell.padding.top + cell.padding.bottom;
5342
+ for (let i = 0; i < cell.rowSpan - 1; i++) {
5343
+ minHeight -= this._rowHeights[cell.rowIndex + i];
5344
+ }
5345
+ return Math.max(acc, minHeight);
5346
+ }, 0);
5347
+ }
5348
+ rowHeight = Math.max(rowHeight, rowStyle.minHeight);
5349
+ if (rowStyle.maxHeight > 0) {
5350
+ rowHeight = Math.min(rowHeight, rowStyle.maxHeight);
5351
+ }
5352
+ this._rowHeights[rowIndex] = rowHeight;
5353
+ let newPage = false;
5354
+ if (rowHeight > this.document.page.contentHeight) {
5355
+ console.warn(new Error(`Row ${rowIndex} requested more than the safe page height, row has been clamped`).stack.slice(7));
5356
+ this._rowHeights[rowIndex] = this.document.page.maxY() - this._rowYPos[rowIndex];
5357
+ } else if (this._rowYPos[rowIndex] + rowHeight >= this.document.page.maxY()) {
5358
+ this._rowYPos[rowIndex] = this.document.page.margins.top;
5359
+ newPage = true;
5360
+ }
5361
+ return {
5362
+ newPage,
5363
+ toRender: toRender.map(cell => measureCell.call(this, cell, rowHeight))
5364
+ };
5365
+ }
5366
+ function measureCell(cell, rowHeight) {
5367
+ let cellWidth = 0;
5368
+ for (let i = 0; i < cell.colSpan; i++) {
5369
+ cellWidth += this._columnWidths[cell.colIndex + i];
5370
+ }
5371
+ let cellHeight = rowHeight;
5372
+ if (cellHeight === 'auto') {
5373
+ cellHeight = this.document.page.contentHeight;
5374
+ } else {
5375
+ for (let i = 0; i < cell.rowSpan - 1; i++) {
5376
+ cellHeight += this._rowHeights[cell.rowIndex + i];
5377
+ }
5378
+ }
5379
+ const textAllocatedWidth = cellWidth - cell.padding.left - cell.padding.right;
5380
+ const textAllocatedHeight = cellHeight - cell.padding.top - cell.padding.bottom;
5381
+ const rotation = cell.textOptions.rotation ?? 0;
5382
+ const {
5383
+ width: textMaxWidth,
5384
+ height: textMaxHeight
5385
+ } = computeBounds(rotation, textAllocatedWidth, textAllocatedHeight);
5386
+ const textOptions = {
5387
+ align: cell.align.x,
5388
+ ellipsis: true,
5389
+ stroke: cell.textStroke > 0,
5390
+ fill: true,
5391
+ width: textMaxWidth,
5392
+ height: textMaxHeight,
5393
+ rotation,
5394
+ ...cell.textOptions
5395
+ };
5396
+ let textBounds = {
5397
+ x: 0,
5398
+ y: 0,
5399
+ width: 0,
5400
+ height: 0
5401
+ };
5402
+ if (cell.text) {
5403
+ const rollbackFont = this.document._fontSource;
5404
+ const rollbackFontSize = this.document._fontSize;
5405
+ const rollbackFontFamily = this.document._fontFamily;
5406
+ if (cell.font?.src) this.document.font(cell.font.src, cell.font?.family);
5407
+ if (cell.font?.size) this.document.fontSize(cell.font.size);
5408
+ const unRotatedTextBounds = this.document.boundsOfString(cell.text, 0, 0, {
5409
+ ...textOptions,
5410
+ rotation: 0
5411
+ });
5412
+ textOptions.width = unRotatedTextBounds.width;
5413
+ textOptions.height = unRotatedTextBounds.height;
5414
+ textBounds = this.document.boundsOfString(cell.text, 0, 0, textOptions);
5415
+ this.document.font(rollbackFont, rollbackFontFamily, rollbackFontSize);
5416
+ }
5417
+ return {
5418
+ ...cell,
5419
+ textOptions,
5420
+ x: this._columnXPos[cell.colIndex],
5421
+ y: this._rowYPos[cell.rowIndex],
5422
+ textX: this._columnXPos[cell.colIndex] + cell.padding.left,
5423
+ textY: this._rowYPos[cell.rowIndex] + cell.padding.top,
5424
+ width: cellWidth,
5425
+ height: cellHeight,
5426
+ textAllocatedHeight,
5427
+ textAllocatedWidth,
5428
+ textBounds
5429
+ };
5430
+ }
5431
+ function computeBounds(rotation, allocWidth, allocHeight) {
5432
+ let textMaxWidth, textMaxHeight;
5433
+ const cos = cosine(rotation);
5434
+ const sin = sine(rotation);
5435
+ if (rotation === 0 || rotation === 180) {
5436
+ textMaxWidth = allocWidth;
5437
+ textMaxHeight = allocHeight;
5438
+ } else if (rotation === 90 || rotation === 270) {
5439
+ textMaxWidth = allocHeight;
5440
+ textMaxHeight = allocWidth;
5441
+ } else if (rotation < 90 || rotation > 180 && rotation < 270) {
5442
+ textMaxWidth = allocWidth / (2 * cos);
5443
+ textMaxHeight = allocWidth / (2 * sin);
5444
+ } else {
5445
+ textMaxHeight = allocWidth / (2 * cos);
5446
+ textMaxWidth = allocWidth / (2 * sin);
5447
+ }
5448
+ const EF = sin * textMaxWidth;
5449
+ const FG = cos * textMaxHeight;
5450
+ if (EF + FG > allocHeight) {
5451
+ const denominator = cos * cos - sin * sin;
5452
+ if (rotation === 0 || rotation === 180) {
5453
+ textMaxWidth = allocWidth;
5454
+ textMaxHeight = allocHeight;
5455
+ } else if (rotation === 90 || rotation === 270) {
5456
+ textMaxWidth = allocHeight;
5457
+ textMaxHeight = allocWidth;
5458
+ } else if (rotation < 90 || rotation > 180 && rotation < 270) {
5459
+ textMaxWidth = (allocWidth * cos - allocHeight * sin) / denominator;
5460
+ textMaxHeight = (allocHeight * cos - allocWidth * sin) / denominator;
5461
+ } else {
5462
+ textMaxHeight = (allocWidth * cos - allocHeight * sin) / denominator;
5463
+ textMaxWidth = (allocHeight * cos - allocWidth * sin) / denominator;
5464
+ }
5465
+ }
5466
+ return {
5467
+ width: Math.abs(textMaxWidth),
5468
+ height: Math.abs(textMaxHeight)
5469
+ };
5470
+ }
5471
+
5472
+ function accommodateTable() {
5473
+ const structParent = this.opts.structParent;
5474
+ if (structParent) {
5475
+ this._tableStruct = this.document.struct('Table');
5476
+ this._tableStruct.dictionary.data.ID = this._id;
5477
+ if (structParent instanceof PDFStructureElement) {
5478
+ structParent.add(this._tableStruct);
5479
+ } else if (structParent instanceof PDFDocument) {
5480
+ structParent.addStructure(this._tableStruct);
5481
+ }
5482
+ this._headerRowLookup = {};
5483
+ this._headerColumnLookup = {};
5484
+ }
5485
+ }
5486
+ function accommodateCleanup() {
5487
+ if (this._tableStruct) this._tableStruct.end();
5488
+ }
5489
+ function accessibleRow(row, rowIndex, renderCell) {
5490
+ const rowStruct = this.document.struct('TR');
5491
+ rowStruct.dictionary.data.ID = new String(`${this._id}-${rowIndex}`);
5492
+ this._tableStruct.add(rowStruct);
5493
+ row.forEach(cell => renderCell(cell, rowStruct));
5494
+ rowStruct.end();
5495
+ }
5496
+ function accessibleCell(cell, rowStruct, callback) {
5497
+ const doc = this.document;
5498
+ const cellStruct = doc.struct(cell.type, {
5499
+ title: cell.title
5500
+ });
5501
+ cellStruct.dictionary.data.ID = cell.id;
5502
+ rowStruct.add(cellStruct);
5503
+ const padding = cell.padding;
5504
+ const border = cell.border;
5505
+ const attributes = {
5506
+ O: 'Table',
5507
+ Width: cell.width,
5508
+ Height: cell.height,
5509
+ Padding: [padding.top, padding.bottom, padding.left, padding.right],
5510
+ RowSpan: cell.rowSpan > 1 ? cell.rowSpan : undefined,
5511
+ ColSpan: cell.colSpan > 1 ? cell.colSpan : undefined,
5512
+ BorderThickness: [border.top, border.bottom, border.left, border.right]
5513
+ };
5514
+ if (cell.type === 'TH') {
5515
+ if (cell.scope === 'Row' || cell.scope === 'Both') {
5516
+ for (let i = 0; i < cell.rowSpan; i++) {
5517
+ if (!this._headerRowLookup[cell.rowIndex + i]) {
5518
+ this._headerRowLookup[cell.rowIndex + i] = [];
5519
+ }
5520
+ this._headerRowLookup[cell.rowIndex + i].push(cell.id);
5521
+ }
5522
+ attributes.Scope = cell.scope;
5523
+ }
5524
+ if (cell.scope === 'Column' || cell.scope === 'Both') {
5525
+ for (let i = 0; i < cell.colSpan; i++) {
5526
+ if (!this._headerColumnLookup[cell.colIndex + i]) {
5527
+ this._headerColumnLookup[cell.colIndex + i] = [];
5528
+ }
5529
+ this._headerColumnLookup[cell.colIndex + i].push(cell.id);
5530
+ }
5531
+ attributes.Scope = cell.scope;
5532
+ }
5533
+ }
5534
+ const Headers = new Set([...Array.from({
5535
+ length: cell.colSpan
5536
+ }, (_, i) => this._headerColumnLookup[cell.colIndex + i]).flat(), ...Array.from({
5537
+ length: cell.rowSpan
5538
+ }, (_, i) => this._headerRowLookup[cell.rowIndex + i]).flat()].filter(Boolean));
5539
+ if (Headers.size) attributes.Headers = Array.from(Headers);
5540
+ const normalizeColor = doc._normalizeColor;
5541
+ if (cell.backgroundColor != null) {
5542
+ attributes.BackgroundColor = normalizeColor(cell.backgroundColor);
5543
+ }
5544
+ const hasBorder = [border.top, border.bottom, border.left, border.right];
5545
+ if (hasBorder.some(x => x)) {
5546
+ const borderColor = cell.borderColor;
5547
+ 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];
5548
+ }
5549
+ Object.keys(attributes).forEach(key => attributes[key] === undefined && delete attributes[key]);
5550
+ cellStruct.dictionary.data.A = doc.ref(attributes);
5551
+ cellStruct.add(callback);
5552
+ cellStruct.end();
5553
+ cellStruct.dictionary.data.A.end();
5554
+ }
5555
+
5556
+ function renderRow(row, rowIndex) {
5557
+ if (this._tableStruct) {
5558
+ accessibleRow.call(this, row, rowIndex, renderCell.bind(this));
5559
+ } else {
5560
+ row.forEach(cell => renderCell.call(this, cell));
5561
+ }
5562
+ return this._rowYPos[rowIndex] + this._rowHeights[rowIndex];
5563
+ }
5564
+ function renderCell(cell, rowStruct) {
5565
+ const cellRenderer = () => {
5566
+ if (cell.backgroundColor != null) {
5567
+ this.document.save().rect(cell.x, cell.y, cell.width, cell.height).fill(cell.backgroundColor).restore();
5568
+ }
5569
+ renderBorder.call(this, cell.border, cell.borderColor, cell.x, cell.y, cell.width, cell.height);
5570
+ if (cell.debug) {
5571
+ this.document.save();
5572
+ this.document.dash(1, {
5573
+ space: 1
5574
+ }).lineWidth(1).strokeOpacity(0.3);
5575
+ this.document.rect(cell.x, cell.y, cell.width, cell.height).stroke('green');
5576
+ this.document.restore();
5577
+ }
5578
+ if (cell.text) renderCellText.call(this, cell);
5579
+ };
5580
+ if (rowStruct) accessibleCell.call(this, cell, rowStruct, cellRenderer);else cellRenderer();
5581
+ }
5582
+ function renderCellText(cell) {
5583
+ const doc = this.document;
5584
+ const rollbackFont = doc._fontSource;
5585
+ const rollbackFontSize = doc._fontSize;
5586
+ const rollbackFontFamily = doc._fontFamily;
5587
+ if (cell.customFont) {
5588
+ if (cell.font.src) doc.font(cell.font.src, cell.font.family);
5589
+ if (cell.font.size) doc.fontSize(cell.font.size);
5590
+ }
5591
+ const x = cell.textX;
5592
+ const y = cell.textY;
5593
+ const Ah = cell.textAllocatedHeight;
5594
+ const Aw = cell.textAllocatedWidth;
5595
+ const Cw = cell.textBounds.width;
5596
+ const Ch = cell.textBounds.height;
5597
+ const Ox = -cell.textBounds.x;
5598
+ const Oy = -cell.textBounds.y;
5599
+ const PxScale = cell.align.x === 'right' ? 1 : cell.align.x === 'center' ? 0.5 : 0;
5600
+ const Px = (Aw - Cw) * PxScale;
5601
+ const PyScale = cell.align.y === 'bottom' ? 1 : cell.align.y === 'center' ? 0.5 : 0;
5602
+ const Py = (Ah - Ch) * PyScale;
5603
+ const dx = Px + Ox;
5604
+ const dy = Py + Oy;
5605
+ if (cell.debug) {
5606
+ doc.save();
5607
+ doc.dash(1, {
5608
+ space: 1
5609
+ }).lineWidth(1).strokeOpacity(0.3);
5610
+ if (cell.text) {
5611
+ 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');
5612
+ }
5613
+ doc.rect(x, y, Aw, Ah).stroke('orange');
5614
+ doc.restore();
5615
+ }
5616
+ doc.save().rect(x, y, Aw, Ah).clip();
5617
+ doc.fillColor(cell.textColor).strokeColor(cell.textStrokeColor);
5618
+ if (cell.textStroke > 0) doc.lineWidth(cell.textStroke);
5619
+ doc.text(cell.text, x + dx, y + dy, cell.textOptions);
5620
+ doc.restore();
5621
+ if (cell.font) doc.font(rollbackFont, rollbackFontFamily, rollbackFontSize);
5622
+ }
5623
+ function renderBorder(border, borderColor, x, y, width, height, mask) {
5624
+ border = Object.fromEntries(Object.entries(border).map(_ref => {
5625
+ let [k, v] = _ref;
5626
+ return [k, mask && !mask[k] ? 0 : v];
5627
+ }));
5628
+ const doc = this.document;
5629
+ if ([border.right, border.bottom, border.left].every(val => val === border.top)) {
5630
+ if (border.top > 0) {
5631
+ doc.save().lineWidth(border.top).rect(x, y, width, height).stroke(borderColor.top).restore();
5632
+ }
5633
+ } else {
5634
+ if (border.top > 0) {
5635
+ doc.save().lineWidth(border.top).moveTo(x, y).lineTo(x + width, y).stroke(borderColor.top).restore();
5636
+ }
5637
+ if (border.right > 0) {
5638
+ doc.save().lineWidth(border.right).moveTo(x + width, y).lineTo(x + width, y + height).stroke(borderColor.right).restore();
5639
+ }
5640
+ if (border.bottom > 0) {
5641
+ doc.save().lineWidth(border.bottom).moveTo(x + width, y + height).lineTo(x, y + height).stroke(borderColor.bottom).restore();
5642
+ }
5643
+ if (border.left > 0) {
5644
+ doc.save().lineWidth(border.left).moveTo(x, y + height).lineTo(x, y).stroke(borderColor.left).restore();
5645
+ }
5646
+ }
5647
+ }
5648
+
5649
+ class PDFTable {
5650
+ constructor(document) {
5651
+ let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
5652
+ this.document = document;
5653
+ this.opts = Object.freeze(opts);
5654
+ normalizeTable.call(this);
5655
+ accommodateTable.call(this);
5656
+ this._currRowIndex = 0;
5657
+ this._ended = false;
5658
+ if (opts.data) {
5659
+ for (const row of opts.data) this.row(row);
5660
+ return this.end();
5661
+ }
5662
+ }
5663
+ row(row) {
5664
+ let lastRow = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
5665
+ if (this._ended) {
5666
+ throw new Error(`Table was marked as ended on row ${this._currRowIndex}`);
5667
+ }
5668
+ row = Array.from(row);
5669
+ row = normalizeRow.call(this, row, this._currRowIndex);
5670
+ if (this._currRowIndex === 0) ensure.call(this, row);
5671
+ const {
5672
+ newPage,
5673
+ toRender
5674
+ } = measure.call(this, row, this._currRowIndex);
5675
+ if (newPage) this.document.continueOnNewPage();
5676
+ const yPos = renderRow.call(this, toRender, this._currRowIndex);
5677
+ this.document.x = this._position.x;
5678
+ this.document.y = yPos;
5679
+ if (lastRow) return this.end();
5680
+ this._currRowIndex++;
5681
+ return this;
5682
+ }
5683
+ end() {
5684
+ while (this._rowBuffer?.size) this.row([]);
5685
+ this._ended = true;
5686
+ accommodateCleanup.call(this);
5687
+ return this.document;
5688
+ }
5689
+ }
5690
+
5691
+ var TableMixin = {
5692
+ initTables() {
5693
+ this._tableIndex = 0;
5694
+ },
5695
+ table(opts) {
5696
+ return new PDFTable(this, opts);
5697
+ }
5698
+ };
5699
+
5361
5700
  class PDFMetadata {
5362
5701
  constructor() {
5363
5702
  this._metadata = `
@@ -5401,7 +5740,7 @@ var MetadataMixin = {
5401
5740
  _addInfo() {
5402
5741
  this.appendXML(`
5403
5742
  <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">
5404
- <xmp:CreateDate>${this.info.CreationDate.toISOString().split('.')[0] + "Z"}</xmp:CreateDate>
5743
+ <xmp:CreateDate>${this.info.CreationDate.toISOString().split('.')[0] + 'Z'}</xmp:CreateDate>
5405
5744
  <xmp:CreatorTool>${this.info.Creator}</xmp:CreatorTool>
5406
5745
  </rdf:Description>
5407
5746
  `);
@@ -5454,11 +5793,6 @@ var MetadataMixin = {
5454
5793
  endMetadata() {
5455
5794
  this._addInfo();
5456
5795
  this.metadata.end();
5457
-
5458
- /*
5459
- Metadata was introduced in PDF 1.4, so adding it to 1.3
5460
- will likely only take up more space.
5461
- */
5462
5796
  if (this.version != 1.3) {
5463
5797
  this.metadataRef = this.ref({
5464
5798
  length: this.metadata.getLength(),
@@ -5473,17 +5807,11 @@ var MetadataMixin = {
5473
5807
  }
5474
5808
  };
5475
5809
 
5476
- /*
5477
- PDFDocument - represents an entire PDF document
5478
- By Devon Govett
5479
- */
5480
5810
  class PDFDocument extends stream.Readable {
5481
5811
  constructor() {
5482
5812
  let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
5483
5813
  super(options);
5484
5814
  this.options = options;
5485
-
5486
- // PDF version
5487
5815
  switch (options.pdfVersion) {
5488
5816
  case '1.4':
5489
5817
  this.version = 1.4;
@@ -5502,13 +5830,9 @@ class PDFDocument extends stream.Readable {
5502
5830
  this.version = 1.3;
5503
5831
  break;
5504
5832
  }
5505
-
5506
- // Whether streams should be compressed
5507
5833
  this.compress = this.options.compress != null ? this.options.compress : true;
5508
5834
  this._pageBuffer = [];
5509
5835
  this._pageBufferStart = 0;
5510
-
5511
- // The PDF object store
5512
5836
  this._offsets = [];
5513
5837
  this._waiting = 0;
5514
5838
  this._ended = false;
@@ -5529,11 +5853,7 @@ class PDFDocument extends stream.Readable {
5529
5853
  if (this.options.lang) {
5530
5854
  this._root.data.Lang = new String(this.options.lang);
5531
5855
  }
5532
-
5533
- // The current page
5534
5856
  this.page = null;
5535
-
5536
- // Initialize mixins
5537
5857
  this.initMetadata();
5538
5858
  this.initColor();
5539
5859
  this.initVector();
@@ -5542,9 +5862,8 @@ class PDFDocument extends stream.Readable {
5542
5862
  this.initImages();
5543
5863
  this.initOutline();
5544
5864
  this.initMarkings(options);
5865
+ this.initTables();
5545
5866
  this.initSubset(options);
5546
-
5547
- // Initialize the metadata
5548
5867
  this.info = {
5549
5868
  Producer: 'PDFKit',
5550
5869
  Creator: 'PDFKit',
@@ -5561,21 +5880,10 @@ class PDFDocument extends stream.Readable {
5561
5880
  DisplayDocTitle: true
5562
5881
  });
5563
5882
  }
5564
-
5565
- // Generate file ID
5566
5883
  this._id = PDFSecurity.generateFileID(this.info);
5567
-
5568
- // Initialize security settings
5569
5884
  this._security = PDFSecurity.create(this, options);
5570
-
5571
- // Write the header
5572
- // PDF version
5573
5885
  this._write(`%PDF-${this.version}`);
5574
-
5575
- // 4 binary chars, as recommended by the spec
5576
5886
  this._write('%\xFF\xFF\xFF\xFF');
5577
-
5578
- // Add the first page
5579
5887
  if (this.options.autoFirstPage !== false) {
5580
5888
  this.addPage();
5581
5889
  }
@@ -5586,27 +5894,16 @@ class PDFDocument extends stream.Readable {
5586
5894
  options
5587
5895
  } = this);
5588
5896
  }
5589
-
5590
- // end the current page if needed
5591
5897
  if (!this.options.bufferPages) {
5592
5898
  this.flushPages();
5593
5899
  }
5594
-
5595
- // create a page object
5596
5900
  this.page = new PDFPage(this, options);
5597
5901
  this._pageBuffer.push(this.page);
5598
-
5599
- // add the page to the object store
5600
5902
  const pages = this._root.data.Pages.data;
5601
5903
  pages.Kids.push(this.page.dictionary);
5602
5904
  pages.Count++;
5603
-
5604
- // reset x and y coordinates
5605
5905
  this.x = this.page.margins.left;
5606
5906
  this.y = this.page.margins.top;
5607
-
5608
- // flip PDF coordinate system so that the origin is in
5609
- // the top left rather than the bottom left
5610
5907
  this._ctm = [1, 0, 0, 1, 0, 0];
5611
5908
  this.transform(1, 0, 0, -1, 0, this.page.height);
5612
5909
  this.emit('pageAdded');
@@ -5614,7 +5911,7 @@ class PDFDocument extends stream.Readable {
5614
5911
  }
5615
5912
  continueOnNewPage(options) {
5616
5913
  const pageMarkings = this.endPageMarkings(this.page);
5617
- this.addPage(options);
5914
+ this.addPage(options ?? this.page._options);
5618
5915
  this.initPageMarkings(pageMarkings);
5619
5916
  return this;
5620
5917
  }
@@ -5632,8 +5929,6 @@ class PDFDocument extends stream.Readable {
5632
5929
  return this.page = page;
5633
5930
  }
5634
5931
  flushPages() {
5635
- // this local variable exists so we're future-proof against
5636
- // reentrant calls to flushPages.
5637
5932
  const pages = this._pageBuffer;
5638
5933
  this._pageBuffer = [];
5639
5934
  this._pageBufferStart += pages.length;
@@ -5657,13 +5952,10 @@ class PDFDocument extends stream.Readable {
5657
5952
  }
5658
5953
  addNamedEmbeddedFile(name, ref) {
5659
5954
  if (!this._root.data.Names.data.EmbeddedFiles) {
5660
- // disabling /Limits for this tree fixes attachments not showing in Adobe Reader
5661
5955
  this._root.data.Names.data.EmbeddedFiles = new PDFNameTree({
5662
5956
  limits: false
5663
5957
  });
5664
5958
  }
5665
-
5666
- // add filespec to EmbeddedFiles
5667
5959
  this._root.data.Names.data.EmbeddedFiles.add(name, ref);
5668
5960
  }
5669
5961
  addNamedJavaScript(name, js) {
@@ -5678,19 +5970,17 @@ class PDFDocument extends stream.Readable {
5678
5970
  }
5679
5971
  ref(data) {
5680
5972
  const ref = new PDFReference(this, this._offsets.length + 1, data);
5681
- this._offsets.push(null); // placeholder for this object's offset once it is finalized
5973
+ this._offsets.push(null);
5682
5974
  this._waiting++;
5683
5975
  return ref;
5684
5976
  }
5685
5977
  _read() {}
5686
- // do nothing, but this method is required by node
5687
-
5688
5978
  _write(data) {
5689
5979
  if (!Buffer.isBuffer(data)) {
5690
5980
  data = Buffer.from(data + '\n', 'binary');
5691
5981
  }
5692
5982
  this.push(data);
5693
- return this._offset += data.length;
5983
+ this._offset += data.length;
5694
5984
  }
5695
5985
  addContent(data) {
5696
5986
  this.page.write(data);
@@ -5700,7 +5990,7 @@ class PDFDocument extends stream.Readable {
5700
5990
  this._offsets[ref.id - 1] = ref.offset;
5701
5991
  if (--this._waiting === 0 && this._ended) {
5702
5992
  this._finalize();
5703
- return this._ended = false;
5993
+ this._ended = false;
5704
5994
  }
5705
5995
  }
5706
5996
  end() {
@@ -5737,13 +6027,12 @@ class PDFDocument extends stream.Readable {
5737
6027
  this._security.end();
5738
6028
  }
5739
6029
  if (this._waiting === 0) {
5740
- return this._finalize();
6030
+ this._finalize();
5741
6031
  } else {
5742
- return this._ended = true;
6032
+ this._ended = true;
5743
6033
  }
5744
6034
  }
5745
6035
  _finalize() {
5746
- // generate xref
5747
6036
  const xRefOffset = this._offset;
5748
6037
  this._write('xref');
5749
6038
  this._write(`0 ${this._offsets.length + 1}`);
@@ -5752,8 +6041,6 @@ class PDFDocument extends stream.Readable {
5752
6041
  offset = `0000000000${offset}`.slice(-10);
5753
6042
  this._write(offset + ' 00000 n ');
5754
6043
  }
5755
-
5756
- // trailer
5757
6044
  const trailer = {
5758
6045
  Size: this._offsets.length + 1,
5759
6046
  Root: this._root,
@@ -5768,9 +6055,7 @@ class PDFDocument extends stream.Readable {
5768
6055
  this._write('startxref');
5769
6056
  this._write(`${xRefOffset}`);
5770
6057
  this._write('%%EOF');
5771
-
5772
- // end the stream
5773
- return this.push(null);
6058
+ this.push(null);
5774
6059
  }
5775
6060
  toString() {
5776
6061
  return '[object PDFDocument]';
@@ -5791,7 +6076,8 @@ mixin(MarkingsMixin);
5791
6076
  mixin(AcroFormMixin);
5792
6077
  mixin(AttachmentsMixin);
5793
6078
  mixin(SubsetMixin);
6079
+ mixin(TableMixin);
5794
6080
  PDFDocument.LineWrapper = LineWrapper;
5795
6081
 
5796
- export default PDFDocument;
6082
+ export { PDFDocument as default };
5797
6083
  //# sourceMappingURL=pdfkit.es.js.map