UNPKG

191 kBJavaScriptView Raw
1import stream from 'stream';
2import fs from 'fs';
3import zlib from 'zlib';
4import CryptoJS from 'crypto-js';
5import fontkit from 'fontkit';
6import { EventEmitter } from 'events';
7import LineBreaker from 'linebreak';
8import exif from 'jpeg-exif';
9import PNG from 'png-js';
10
11/*
12PDFAbstractReference - abstract class for PDF reference
13*/
14class PDFAbstractReference {
15 toString() {
16 throw new Error('Must be implemented by subclasses');
17 }
18
19}
20
21/*
22PDFTree - abstract base class for name and number tree objects
23*/
24
25class PDFTree {
26 constructor() {
27 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
28 this._items = {}; // disable /Limits output for this tree
29
30 this.limits = typeof options.limits === 'boolean' ? options.limits : true;
31 }
32
33 add(key, val) {
34 return this._items[key] = val;
35 }
36
37 get(key) {
38 return this._items[key];
39 }
40
41 toString() {
42 // Needs to be sorted by key
43 var sortedKeys = Object.keys(this._items).sort((a, b) => this._compareKeys(a, b));
44 var out = ['<<'];
45
46 if (this.limits && sortedKeys.length > 1) {
47 var first = sortedKeys[0],
48 last = sortedKeys[sortedKeys.length - 1];
49 out.push(" /Limits ".concat(PDFObject.convert([this._dataForKey(first), this._dataForKey(last)])));
50 }
51
52 out.push(" /".concat(this._keysName(), " ["));
53
54 for (var key of sortedKeys) {
55 out.push(" ".concat(PDFObject.convert(this._dataForKey(key)), " ").concat(PDFObject.convert(this._items[key])));
56 }
57
58 out.push(']');
59 out.push('>>');
60 return out.join('\n');
61 }
62
63 _compareKeys()
64 /*a, b*/
65 {
66 throw new Error('Must be implemented by subclasses');
67 }
68
69 _keysName() {
70 throw new Error('Must be implemented by subclasses');
71 }
72
73 _dataForKey()
74 /*k*/
75 {
76 throw new Error('Must be implemented by subclasses');
77 }
78
79}
80
81/*
82PDFObject - converts JavaScript types into their corresponding PDF types.
83By Devon Govett
84*/
85
86var pad = (str, length) => (Array(length + 1).join('0') + str).slice(-length);
87
88var escapableRe = /[\n\r\t\b\f()\\]/g;
89var escapable = {
90 '\n': '\\n',
91 '\r': '\\r',
92 '\t': '\\t',
93 '\b': '\\b',
94 '\f': '\\f',
95 '\\': '\\\\',
96 '(': '\\(',
97 ')': '\\)'
98}; // Convert little endian UTF-16 to big endian
99
100var swapBytes = function swapBytes(buff) {
101 var l = buff.length;
102
103 if (l & 0x01) {
104 throw new Error('Buffer length must be even');
105 } else {
106 for (var i = 0, end = l - 1; i < end; i += 2) {
107 var a = buff[i];
108 buff[i] = buff[i + 1];
109 buff[i + 1] = a;
110 }
111 }
112
113 return buff;
114};
115
116class PDFObject {
117 static convert(object) {
118 var encryptFn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
119
120 // String literals are converted to the PDF name type
121 if (typeof object === 'string') {
122 return "/".concat(object); // String objects are converted to PDF strings (UTF-16)
123 } else if (object instanceof String) {
124 var string = object; // Detect if this is a unicode string
125
126 var isUnicode = false;
127
128 for (var i = 0, end = string.length; i < end; i++) {
129 if (string.charCodeAt(i) > 0x7f) {
130 isUnicode = true;
131 break;
132 }
133 } // If so, encode it as big endian UTF-16
134
135
136 var stringBuffer;
137
138 if (isUnicode) {
139 stringBuffer = swapBytes(Buffer.from("\uFEFF".concat(string), 'utf16le'));
140 } else {
141 stringBuffer = Buffer.from(string.valueOf(), 'ascii');
142 } // Encrypt the string when necessary
143
144
145 if (encryptFn) {
146 string = encryptFn(stringBuffer).toString('binary');
147 } else {
148 string = stringBuffer.toString('binary');
149 } // Escape characters as required by the spec
150
151
152 string = string.replace(escapableRe, c => escapable[c]);
153 return "(".concat(string, ")"); // Buffers are converted to PDF hex strings
154 } else if (Buffer.isBuffer(object)) {
155 return "<".concat(object.toString('hex'), ">");
156 } else if (object instanceof PDFAbstractReference || object instanceof PDFTree) {
157 return object.toString();
158 } else if (object instanceof Date) {
159 var _string = "D:".concat(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'; // Encrypt the string when necessary
160
161
162 if (encryptFn) {
163 _string = encryptFn(Buffer.from(_string, 'ascii')).toString('binary'); // Escape characters as required by the spec
164
165 _string = _string.replace(escapableRe, c => escapable[c]);
166 }
167
168 return "(".concat(_string, ")");
169 } else if (Array.isArray(object)) {
170 var items = object.map(e => PDFObject.convert(e, encryptFn)).join(' ');
171 return "[".concat(items, "]");
172 } else if ({}.toString.call(object) === '[object Object]') {
173 var out = ['<<'];
174
175 for (var key in object) {
176 var val = object[key];
177 out.push("/".concat(key, " ").concat(PDFObject.convert(val, encryptFn)));
178 }
179
180 out.push('>>');
181 return out.join('\n');
182 } else if (typeof object === 'number') {
183 return PDFObject.number(object);
184 } else {
185 return "".concat(object);
186 }
187 }
188
189 static number(n) {
190 if (n > -1e21 && n < 1e21) {
191 return Math.round(n * 1e6) / 1e6;
192 }
193
194 throw new Error("unsupported number: ".concat(n));
195 }
196
197}
198
199/*
200PDFReference - represents a reference to another object in the PDF object heirarchy
201By Devon Govett
202*/
203
204class PDFReference extends PDFAbstractReference {
205 constructor(document, id) {
206 var data = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
207 super();
208 this.document = document;
209 this.id = id;
210 this.data = data;
211 this.gen = 0;
212 this.compress = this.document.compress && !this.data.Filter;
213 this.uncompressedLength = 0;
214 this.buffer = [];
215 }
216
217 write(chunk) {
218 if (!Buffer.isBuffer(chunk)) {
219 chunk = Buffer.from(chunk + '\n', 'binary');
220 }
221
222 this.uncompressedLength += chunk.length;
223
224 if (this.data.Length == null) {
225 this.data.Length = 0;
226 }
227
228 this.buffer.push(chunk);
229 this.data.Length += chunk.length;
230
231 if (this.compress) {
232 return this.data.Filter = 'FlateDecode';
233 }
234 }
235
236 end(chunk) {
237 if (chunk) {
238 this.write(chunk);
239 }
240
241 return this.finalize();
242 }
243
244 finalize() {
245 this.offset = this.document._offset;
246 var encryptFn = this.document._security ? this.document._security.getEncryptFn(this.id, this.gen) : null;
247
248 if (this.buffer.length) {
249 this.buffer = Buffer.concat(this.buffer);
250
251 if (this.compress) {
252 this.buffer = zlib.deflateSync(this.buffer);
253 }
254
255 if (encryptFn) {
256 this.buffer = encryptFn(this.buffer);
257 }
258
259 this.data.Length = this.buffer.length;
260 }
261
262 this.document._write("".concat(this.id, " ").concat(this.gen, " obj"));
263
264 this.document._write(PDFObject.convert(this.data, encryptFn));
265
266 if (this.buffer.length) {
267 this.document._write('stream');
268
269 this.document._write(this.buffer);
270
271 this.buffer = []; // free up memory
272
273 this.document._write('\nendstream');
274 }
275
276 this.document._write('endobj');
277
278 this.document._refEnd(this);
279 }
280
281 toString() {
282 return "".concat(this.id, " ").concat(this.gen, " R");
283 }
284
285}
286
287/*
288PDFPage - represents a single page in the PDF document
289By Devon Govett
290*/
291var DEFAULT_MARGINS = {
292 top: 72,
293 left: 72,
294 bottom: 72,
295 right: 72
296};
297var SIZES = {
298 '4A0': [4767.87, 6740.79],
299 '2A0': [3370.39, 4767.87],
300 A0: [2383.94, 3370.39],
301 A1: [1683.78, 2383.94],
302 A2: [1190.55, 1683.78],
303 A3: [841.89, 1190.55],
304 A4: [595.28, 841.89],
305 A5: [419.53, 595.28],
306 A6: [297.64, 419.53],
307 A7: [209.76, 297.64],
308 A8: [147.4, 209.76],
309 A9: [104.88, 147.4],
310 A10: [73.7, 104.88],
311 B0: [2834.65, 4008.19],
312 B1: [2004.09, 2834.65],
313 B2: [1417.32, 2004.09],
314 B3: [1000.63, 1417.32],
315 B4: [708.66, 1000.63],
316 B5: [498.9, 708.66],
317 B6: [354.33, 498.9],
318 B7: [249.45, 354.33],
319 B8: [175.75, 249.45],
320 B9: [124.72, 175.75],
321 B10: [87.87, 124.72],
322 C0: [2599.37, 3676.54],
323 C1: [1836.85, 2599.37],
324 C2: [1298.27, 1836.85],
325 C3: [918.43, 1298.27],
326 C4: [649.13, 918.43],
327 C5: [459.21, 649.13],
328 C6: [323.15, 459.21],
329 C7: [229.61, 323.15],
330 C8: [161.57, 229.61],
331 C9: [113.39, 161.57],
332 C10: [79.37, 113.39],
333 RA0: [2437.8, 3458.27],
334 RA1: [1729.13, 2437.8],
335 RA2: [1218.9, 1729.13],
336 RA3: [864.57, 1218.9],
337 RA4: [609.45, 864.57],
338 SRA0: [2551.18, 3628.35],
339 SRA1: [1814.17, 2551.18],
340 SRA2: [1275.59, 1814.17],
341 SRA3: [907.09, 1275.59],
342 SRA4: [637.8, 907.09],
343 EXECUTIVE: [521.86, 756.0],
344 FOLIO: [612.0, 936.0],
345 LEGAL: [612.0, 1008.0],
346 LETTER: [612.0, 792.0],
347 TABLOID: [792.0, 1224.0]
348};
349
350class PDFPage {
351 constructor(document) {
352 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
353 this.document = document;
354 this.size = options.size || 'letter';
355 this.layout = options.layout || 'portrait'; // process margins
356
357 if (typeof options.margin === 'number') {
358 this.margins = {
359 top: options.margin,
360 left: options.margin,
361 bottom: options.margin,
362 right: options.margin
363 }; // default to 1 inch margins
364 } else {
365 this.margins = options.margins || DEFAULT_MARGINS;
366 } // calculate page dimensions
367
368
369 var dimensions = Array.isArray(this.size) ? this.size : SIZES[this.size.toUpperCase()];
370 this.width = dimensions[this.layout === 'portrait' ? 0 : 1];
371 this.height = dimensions[this.layout === 'portrait' ? 1 : 0];
372 this.content = this.document.ref(); // Initialize the Font, XObject, and ExtGState dictionaries
373
374 this.resources = this.document.ref({
375 ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI']
376 }); // The page dictionary
377
378 this.dictionary = this.document.ref({
379 Type: 'Page',
380 Parent: this.document._root.data.Pages,
381 MediaBox: [0, 0, this.width, this.height],
382 Contents: this.content,
383 Resources: this.resources
384 });
385 this.markings = [];
386 } // Lazily create these objects
387
388
389 get fonts() {
390 var data = this.resources.data;
391 return data.Font != null ? data.Font : data.Font = {};
392 }
393
394 get xobjects() {
395 var data = this.resources.data;
396 return data.XObject != null ? data.XObject : data.XObject = {};
397 }
398
399 get ext_gstates() {
400 var data = this.resources.data;
401 return data.ExtGState != null ? data.ExtGState : data.ExtGState = {};
402 }
403
404 get patterns() {
405 var data = this.resources.data;
406 return data.Pattern != null ? data.Pattern : data.Pattern = {};
407 }
408
409 get colorSpaces() {
410 var data = this.resources.data;
411 return data.ColorSpace || (data.ColorSpace = {});
412 }
413
414 get annotations() {
415 var data = this.dictionary.data;
416 return data.Annots != null ? data.Annots : data.Annots = [];
417 }
418
419 get structParentTreeKey() {
420 var data = this.dictionary.data;
421 return data.StructParents != null ? data.StructParents : data.StructParents = this.document.createStructParentTreeNextKey();
422 }
423
424 maxY() {
425 return this.height - this.margins.bottom;
426 }
427
428 write(chunk) {
429 return this.content.write(chunk);
430 }
431
432 end() {
433 this.dictionary.end();
434 this.resources.end();
435 return this.content.end();
436 }
437
438}
439
440/*
441PDFNameTree - represents a name tree object
442*/
443
444class PDFNameTree extends PDFTree {
445 _compareKeys(a, b) {
446 return a.localeCompare(b);
447 }
448
449 _keysName() {
450 return "Names";
451 }
452
453 _dataForKey(k) {
454 return new String(k);
455 }
456
457}
458
459/**
460 * Check if value is in a range group.
461 * @param {number} value
462 * @param {number[]} rangeGroup
463 * @returns {boolean}
464 */
465function inRange(value, rangeGroup) {
466 if (value < rangeGroup[0]) return false;
467 var startRange = 0;
468 var endRange = rangeGroup.length / 2;
469
470 while (startRange <= endRange) {
471 var middleRange = Math.floor((startRange + endRange) / 2); // actual array index
472
473 var arrayIndex = middleRange * 2; // Check if value is in range pointed by actual index
474
475 if (value >= rangeGroup[arrayIndex] && value <= rangeGroup[arrayIndex + 1]) {
476 return true;
477 }
478
479 if (value > rangeGroup[arrayIndex + 1]) {
480 // Search Right Side Of Array
481 startRange = middleRange + 1;
482 } else {
483 // Search Left Side Of Array
484 endRange = middleRange - 1;
485 }
486 }
487
488 return false;
489}
490
491/**
492 * A.1 Unassigned code points in Unicode 3.2
493 * @link https://tools.ietf.org/html/rfc3454#appendix-A.1
494 */
495
496var 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]; // prettier-ignore-end
497
498var isUnassignedCodePoint = character => inRange(character, unassigned_code_points); // prettier-ignore-start
499
500/**
501 * B.1 Commonly mapped to nothing
502 * @link https://tools.ietf.org/html/rfc3454#appendix-B.1
503 */
504
505
506var 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]; // prettier-ignore-end
507
508var isCommonlyMappedToNothing = character => inRange(character, commonly_mapped_to_nothing); // prettier-ignore-start
509
510/**
511 * C.1.2 Non-ASCII space characters
512 * @link https://tools.ietf.org/html/rfc3454#appendix-C.1.2
513 */
514
515
516var non_ASCII_space_characters = [0x00a0, 0x00a0
517/* NO-BREAK SPACE */
518, 0x1680, 0x1680
519/* OGHAM SPACE MARK */
520, 0x2000, 0x2000
521/* EN QUAD */
522, 0x2001, 0x2001
523/* EM QUAD */
524, 0x2002, 0x2002
525/* EN SPACE */
526, 0x2003, 0x2003
527/* EM SPACE */
528, 0x2004, 0x2004
529/* THREE-PER-EM SPACE */
530, 0x2005, 0x2005
531/* FOUR-PER-EM SPACE */
532, 0x2006, 0x2006
533/* SIX-PER-EM SPACE */
534, 0x2007, 0x2007
535/* FIGURE SPACE */
536, 0x2008, 0x2008
537/* PUNCTUATION SPACE */
538, 0x2009, 0x2009
539/* THIN SPACE */
540, 0x200a, 0x200a
541/* HAIR SPACE */
542, 0x200b, 0x200b
543/* ZERO WIDTH SPACE */
544, 0x202f, 0x202f
545/* NARROW NO-BREAK SPACE */
546, 0x205f, 0x205f
547/* MEDIUM MATHEMATICAL SPACE */
548, 0x3000, 0x3000
549/* IDEOGRAPHIC SPACE */
550]; // prettier-ignore-end
551
552var isNonASCIISpaceCharacter = character => inRange(character, non_ASCII_space_characters); // prettier-ignore-start
553
554
555var non_ASCII_controls_characters = [
556/**
557 * C.2.2 Non-ASCII control characters
558 * @link https://tools.ietf.org/html/rfc3454#appendix-C.2.2
559 */
5600x0080, 0x009f
561/* [CONTROL CHARACTERS] */
562, 0x06dd, 0x06dd
563/* ARABIC END OF AYAH */
564, 0x070f, 0x070f
565/* SYRIAC ABBREVIATION MARK */
566, 0x180e, 0x180e
567/* MONGOLIAN VOWEL SEPARATOR */
568, 0x200c, 0x200c
569/* ZERO WIDTH NON-JOINER */
570, 0x200d, 0x200d
571/* ZERO WIDTH JOINER */
572, 0x2028, 0x2028
573/* LINE SEPARATOR */
574, 0x2029, 0x2029
575/* PARAGRAPH SEPARATOR */
576, 0x2060, 0x2060
577/* WORD JOINER */
578, 0x2061, 0x2061
579/* FUNCTION APPLICATION */
580, 0x2062, 0x2062
581/* INVISIBLE TIMES */
582, 0x2063, 0x2063
583/* INVISIBLE SEPARATOR */
584, 0x206a, 0x206f
585/* [CONTROL CHARACTERS] */
586, 0xfeff, 0xfeff
587/* ZERO WIDTH NO-BREAK SPACE */
588, 0xfff9, 0xfffc
589/* [CONTROL CHARACTERS] */
590, 0x1d173, 0x1d17a
591/* [MUSICAL CONTROL CHARACTERS] */
592];
593var non_character_codepoints = [
594/**
595 * C.4 Non-character code points
596 * @link https://tools.ietf.org/html/rfc3454#appendix-C.4
597 */
5980xfdd0, 0xfdef
599/* [NONCHARACTER CODE POINTS] */
600, 0xfffe, 0xffff
601/* [NONCHARACTER CODE POINTS] */
602, 0x1fffe, 0x1ffff
603/* [NONCHARACTER CODE POINTS] */
604, 0x2fffe, 0x2ffff
605/* [NONCHARACTER CODE POINTS] */
606, 0x3fffe, 0x3ffff
607/* [NONCHARACTER CODE POINTS] */
608, 0x4fffe, 0x4ffff
609/* [NONCHARACTER CODE POINTS] */
610, 0x5fffe, 0x5ffff
611/* [NONCHARACTER CODE POINTS] */
612, 0x6fffe, 0x6ffff
613/* [NONCHARACTER CODE POINTS] */
614, 0x7fffe, 0x7ffff
615/* [NONCHARACTER CODE POINTS] */
616, 0x8fffe, 0x8ffff
617/* [NONCHARACTER CODE POINTS] */
618, 0x9fffe, 0x9ffff
619/* [NONCHARACTER CODE POINTS] */
620, 0xafffe, 0xaffff
621/* [NONCHARACTER CODE POINTS] */
622, 0xbfffe, 0xbffff
623/* [NONCHARACTER CODE POINTS] */
624, 0xcfffe, 0xcffff
625/* [NONCHARACTER CODE POINTS] */
626, 0xdfffe, 0xdffff
627/* [NONCHARACTER CODE POINTS] */
628, 0xefffe, 0xeffff
629/* [NONCHARACTER CODE POINTS] */
630, 0x10fffe, 0x10ffff
631/* [NONCHARACTER CODE POINTS] */
632];
633/**
634 * 2.3. Prohibited Output
635 */
636
637var prohibited_characters = [
638/**
639 * C.2.1 ASCII control characters
640 * @link https://tools.ietf.org/html/rfc3454#appendix-C.2.1
641 */
6420, 0x001f
643/* [CONTROL CHARACTERS] */
644, 0x007f, 0x007f
645/* DELETE */
646,
647/**
648 * C.8 Change display properties or are deprecated
649 * @link https://tools.ietf.org/html/rfc3454#appendix-C.8
650 */
6510x0340, 0x0340
652/* COMBINING GRAVE TONE MARK */
653, 0x0341, 0x0341
654/* COMBINING ACUTE TONE MARK */
655, 0x200e, 0x200e
656/* LEFT-TO-RIGHT MARK */
657, 0x200f, 0x200f
658/* RIGHT-TO-LEFT MARK */
659, 0x202a, 0x202a
660/* LEFT-TO-RIGHT EMBEDDING */
661, 0x202b, 0x202b
662/* RIGHT-TO-LEFT EMBEDDING */
663, 0x202c, 0x202c
664/* POP DIRECTIONAL FORMATTING */
665, 0x202d, 0x202d
666/* LEFT-TO-RIGHT OVERRIDE */
667, 0x202e, 0x202e
668/* RIGHT-TO-LEFT OVERRIDE */
669, 0x206a, 0x206a
670/* INHIBIT SYMMETRIC SWAPPING */
671, 0x206b, 0x206b
672/* ACTIVATE SYMMETRIC SWAPPING */
673, 0x206c, 0x206c
674/* INHIBIT ARABIC FORM SHAPING */
675, 0x206d, 0x206d
676/* ACTIVATE ARABIC FORM SHAPING */
677, 0x206e, 0x206e
678/* NATIONAL DIGIT SHAPES */
679, 0x206f, 0x206f
680/* NOMINAL DIGIT SHAPES */
681,
682/**
683 * C.7 Inappropriate for canonical representation
684 * @link https://tools.ietf.org/html/rfc3454#appendix-C.7
685 */
6860x2ff0, 0x2ffb
687/* [IDEOGRAPHIC DESCRIPTION CHARACTERS] */
688,
689/**
690 * C.5 Surrogate codes
691 * @link https://tools.ietf.org/html/rfc3454#appendix-C.5
692 */
6930xd800, 0xdfff,
694/**
695 * C.3 Private use
696 * @link https://tools.ietf.org/html/rfc3454#appendix-C.3
697 */
6980xe000, 0xf8ff
699/* [PRIVATE USE, PLANE 0] */
700,
701/**
702 * C.6 Inappropriate for plain text
703 * @link https://tools.ietf.org/html/rfc3454#appendix-C.6
704 */
7050xfff9, 0xfff9
706/* INTERLINEAR ANNOTATION ANCHOR */
707, 0xfffa, 0xfffa
708/* INTERLINEAR ANNOTATION SEPARATOR */
709, 0xfffb, 0xfffb
710/* INTERLINEAR ANNOTATION TERMINATOR */
711, 0xfffc, 0xfffc
712/* OBJECT REPLACEMENT CHARACTER */
713, 0xfffd, 0xfffd
714/* REPLACEMENT CHARACTER */
715,
716/**
717 * C.9 Tagging characters
718 * @link https://tools.ietf.org/html/rfc3454#appendix-C.9
719 */
7200xe0001, 0xe0001
721/* LANGUAGE TAG */
722, 0xe0020, 0xe007f
723/* [TAGGING CHARACTERS] */
724,
725/**
726 * C.3 Private use
727 * @link https://tools.ietf.org/html/rfc3454#appendix-C.3
728 */
7290xf0000, 0xffffd
730/* [PRIVATE USE, PLANE 15] */
731, 0x100000, 0x10fffd
732/* [PRIVATE USE, PLANE 16] */
733]; // prettier-ignore-end
734
735var isProhibitedCharacter = character => inRange(character, non_ASCII_space_characters) || inRange(character, prohibited_characters) || inRange(character, non_ASCII_controls_characters) || inRange(character, non_character_codepoints); // prettier-ignore-start
736
737/**
738 * D.1 Characters with bidirectional property "R" or "AL"
739 * @link https://tools.ietf.org/html/rfc3454#appendix-D.1
740 */
741
742
743var 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]; // prettier-ignore-end
744
745var isBidirectionalRAL = character => inRange(character, bidirectional_r_al); // prettier-ignore-start
746
747/**
748 * D.2 Characters with bidirectional property "L"
749 * @link https://tools.ietf.org/html/rfc3454#appendix-D.2
750 */
751
752
753var 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]; // prettier-ignore-end
754
755var isBidirectionalL = character => inRange(character, bidirectional_l);
756
757/**
758 * non-ASCII space characters [StringPrep, C.1.2] that can be
759 * mapped to SPACE (U+0020)
760 */
761
762var mapping2space = isNonASCIISpaceCharacter;
763/**
764 * the "commonly mapped to nothing" characters [StringPrep, B.1]
765 * that can be mapped to nothing.
766 */
767
768var mapping2nothing = isCommonlyMappedToNothing; // utils
769
770var getCodePoint = character => character.codePointAt(0);
771
772var first = x => x[0];
773
774var last = x => x[x.length - 1];
775/**
776 * Convert provided string into an array of Unicode Code Points.
777 * Based on https://stackoverflow.com/a/21409165/1556249
778 * and https://www.npmjs.com/package/code-point-at.
779 * @param {string} input
780 * @returns {number[]}
781 */
782
783
784function toCodePoints(input) {
785 var codepoints = [];
786 var size = input.length;
787
788 for (var i = 0; i < size; i += 1) {
789 var before = input.charCodeAt(i);
790
791 if (before >= 0xd800 && before <= 0xdbff && size > i + 1) {
792 var next = input.charCodeAt(i + 1);
793
794 if (next >= 0xdc00 && next <= 0xdfff) {
795 codepoints.push((before - 0xd800) * 0x400 + next - 0xdc00 + 0x10000);
796 i += 1;
797 continue;
798 }
799 }
800
801 codepoints.push(before);
802 }
803
804 return codepoints;
805}
806/**
807 * SASLprep.
808 * @param {string} input
809 * @param {Object} opts
810 * @param {boolean} opts.allowUnassigned
811 * @returns {string}
812 */
813
814
815function saslprep(input) {
816 var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
817
818 if (typeof input !== 'string') {
819 throw new TypeError('Expected string.');
820 }
821
822 if (input.length === 0) {
823 return '';
824 } // 1. Map
825
826
827 var mapped_input = toCodePoints(input) // 1.1 mapping to space
828 .map(character => mapping2space(character) ? 0x20 : character) // 1.2 mapping to nothing
829 .filter(character => !mapping2nothing(character)); // 2. Normalize
830
831 var normalized_input = String.fromCodePoint.apply(null, mapped_input).normalize('NFKC');
832 var normalized_map = toCodePoints(normalized_input); // 3. Prohibit
833
834 var hasProhibited = normalized_map.some(isProhibitedCharacter);
835
836 if (hasProhibited) {
837 throw new Error('Prohibited character, see https://tools.ietf.org/html/rfc4013#section-2.3');
838 } // Unassigned Code Points
839
840
841 if (opts.allowUnassigned !== true) {
842 var hasUnassigned = normalized_map.some(isUnassignedCodePoint);
843
844 if (hasUnassigned) {
845 throw new Error('Unassigned code point, see https://tools.ietf.org/html/rfc4013#section-2.5');
846 }
847 } // 4. check bidi
848
849
850 var hasBidiRAL = normalized_map.some(isBidirectionalRAL);
851 var hasBidiL = normalized_map.some(isBidirectionalL); // 4.1 If a string contains any RandALCat character, the string MUST NOT
852 // contain any LCat character.
853
854 if (hasBidiRAL && hasBidiL) {
855 throw new Error('String must not contain RandALCat and LCat at the same time,' + ' see https://tools.ietf.org/html/rfc3454#section-6');
856 }
857 /**
858 * 4.2 If a string contains any RandALCat character, a RandALCat
859 * character MUST be the first character of the string, and a
860 * RandALCat character MUST be the last character of the string.
861 */
862
863
864 var isFirstBidiRAL = isBidirectionalRAL(getCodePoint(first(normalized_input)));
865 var isLastBidiRAL = isBidirectionalRAL(getCodePoint(last(normalized_input)));
866
867 if (hasBidiRAL && !(isFirstBidiRAL && isLastBidiRAL)) {
868 throw new Error('Bidirectional RandALCat character must be the first and the last' + ' character of the string, see https://tools.ietf.org/html/rfc3454#section-6');
869 }
870
871 return normalized_input;
872}
873
874/*
875 PDFSecurity - represents PDF security settings
876 By Yang Liu <hi@zesik.com>
877 */
878
879class PDFSecurity {
880 static generateFileID() {
881 var info = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
882 var infoStr = "".concat(info.CreationDate.getTime(), "\n");
883
884 for (var key in info) {
885 // eslint-disable-next-line no-prototype-builtins
886 if (!info.hasOwnProperty(key)) {
887 continue;
888 }
889
890 infoStr += "".concat(key, ": ").concat(info[key].valueOf(), "\n");
891 }
892
893 return wordArrayToBuffer(CryptoJS.MD5(infoStr));
894 }
895
896 static generateRandomWordArray(bytes) {
897 return CryptoJS.lib.WordArray.random(bytes);
898 }
899
900 static create(document) {
901 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
902
903 if (!options.ownerPassword && !options.userPassword) {
904 return null;
905 }
906
907 return new PDFSecurity(document, options);
908 }
909
910 constructor(document) {
911 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
912
913 if (!options.ownerPassword && !options.userPassword) {
914 throw new Error('None of owner password and user password is defined.');
915 }
916
917 this.document = document;
918
919 this._setupEncryption(options);
920 }
921
922 _setupEncryption(options) {
923 switch (options.pdfVersion) {
924 case '1.4':
925 case '1.5':
926 this.version = 2;
927 break;
928
929 case '1.6':
930 case '1.7':
931 this.version = 4;
932 break;
933
934 case '1.7ext3':
935 this.version = 5;
936 break;
937
938 default:
939 this.version = 1;
940 break;
941 }
942
943 var encDict = {
944 Filter: 'Standard'
945 };
946
947 switch (this.version) {
948 case 1:
949 case 2:
950 case 4:
951 this._setupEncryptionV1V2V4(this.version, encDict, options);
952
953 break;
954
955 case 5:
956 this._setupEncryptionV5(encDict, options);
957
958 break;
959 }
960
961 this.dictionary = this.document.ref(encDict);
962 }
963
964 _setupEncryptionV1V2V4(v, encDict, options) {
965 var r, permissions;
966
967 switch (v) {
968 case 1:
969 r = 2;
970 this.keyBits = 40;
971 permissions = getPermissionsR2(options.permissions);
972 break;
973
974 case 2:
975 r = 3;
976 this.keyBits = 128;
977 permissions = getPermissionsR3(options.permissions);
978 break;
979
980 case 4:
981 r = 4;
982 this.keyBits = 128;
983 permissions = getPermissionsR3(options.permissions);
984 break;
985 }
986
987 var paddedUserPassword = processPasswordR2R3R4(options.userPassword);
988 var paddedOwnerPassword = options.ownerPassword ? processPasswordR2R3R4(options.ownerPassword) : paddedUserPassword;
989 var ownerPasswordEntry = getOwnerPasswordR2R3R4(r, this.keyBits, paddedUserPassword, paddedOwnerPassword);
990 this.encryptionKey = getEncryptionKeyR2R3R4(r, this.keyBits, this.document._id, paddedUserPassword, ownerPasswordEntry, permissions);
991 var userPasswordEntry;
992
993 if (r === 2) {
994 userPasswordEntry = getUserPasswordR2(this.encryptionKey);
995 } else {
996 userPasswordEntry = getUserPasswordR3R4(this.document._id, this.encryptionKey);
997 }
998
999 encDict.V = v;
1000
1001 if (v >= 2) {
1002 encDict.Length = this.keyBits;
1003 }
1004
1005 if (v === 4) {
1006 encDict.CF = {
1007 StdCF: {
1008 AuthEvent: 'DocOpen',
1009 CFM: 'AESV2',
1010 Length: this.keyBits / 8
1011 }
1012 };
1013 encDict.StmF = 'StdCF';
1014 encDict.StrF = 'StdCF';
1015 }
1016
1017 encDict.R = r;
1018 encDict.O = wordArrayToBuffer(ownerPasswordEntry);
1019 encDict.U = wordArrayToBuffer(userPasswordEntry);
1020 encDict.P = permissions;
1021 }
1022
1023 _setupEncryptionV5(encDict, options) {
1024 this.keyBits = 256;
1025 var permissions = getPermissionsR3(options.permissions);
1026 var processedUserPassword = processPasswordR5(options.userPassword);
1027 var processedOwnerPassword = options.ownerPassword ? processPasswordR5(options.ownerPassword) : processedUserPassword;
1028 this.encryptionKey = getEncryptionKeyR5(PDFSecurity.generateRandomWordArray);
1029 var userPasswordEntry = getUserPasswordR5(processedUserPassword, PDFSecurity.generateRandomWordArray);
1030 var userKeySalt = CryptoJS.lib.WordArray.create(userPasswordEntry.words.slice(10, 12), 8);
1031 var userEncryptionKeyEntry = getUserEncryptionKeyR5(processedUserPassword, userKeySalt, this.encryptionKey);
1032 var ownerPasswordEntry = getOwnerPasswordR5(processedOwnerPassword, userPasswordEntry, PDFSecurity.generateRandomWordArray);
1033 var ownerKeySalt = CryptoJS.lib.WordArray.create(ownerPasswordEntry.words.slice(10, 12), 8);
1034 var ownerEncryptionKeyEntry = getOwnerEncryptionKeyR5(processedOwnerPassword, ownerKeySalt, userPasswordEntry, this.encryptionKey);
1035 var permsEntry = getEncryptedPermissionsR5(permissions, this.encryptionKey, PDFSecurity.generateRandomWordArray);
1036 encDict.V = 5;
1037 encDict.Length = this.keyBits;
1038 encDict.CF = {
1039 StdCF: {
1040 AuthEvent: 'DocOpen',
1041 CFM: 'AESV3',
1042 Length: this.keyBits / 8
1043 }
1044 };
1045 encDict.StmF = 'StdCF';
1046 encDict.StrF = 'StdCF';
1047 encDict.R = 5;
1048 encDict.O = wordArrayToBuffer(ownerPasswordEntry);
1049 encDict.OE = wordArrayToBuffer(ownerEncryptionKeyEntry);
1050 encDict.U = wordArrayToBuffer(userPasswordEntry);
1051 encDict.UE = wordArrayToBuffer(userEncryptionKeyEntry);
1052 encDict.P = permissions;
1053 encDict.Perms = wordArrayToBuffer(permsEntry);
1054 }
1055
1056 getEncryptFn(obj, gen) {
1057 var digest;
1058
1059 if (this.version < 5) {
1060 digest = this.encryptionKey.clone().concat(CryptoJS.lib.WordArray.create([(obj & 0xff) << 24 | (obj & 0xff00) << 8 | obj >> 8 & 0xff00 | gen & 0xff, (gen & 0xff00) << 16], 5));
1061 }
1062
1063 if (this.version === 1 || this.version === 2) {
1064 var _key = CryptoJS.MD5(digest);
1065
1066 _key.sigBytes = Math.min(16, this.keyBits / 8 + 5);
1067 return buffer => wordArrayToBuffer(CryptoJS.RC4.encrypt(CryptoJS.lib.WordArray.create(buffer), _key).ciphertext);
1068 }
1069
1070 var key;
1071
1072 if (this.version === 4) {
1073 key = CryptoJS.MD5(digest.concat(CryptoJS.lib.WordArray.create([0x73416c54], 4)));
1074 } else {
1075 key = this.encryptionKey;
1076 }
1077
1078 var iv = PDFSecurity.generateRandomWordArray(16);
1079 var options = {
1080 mode: CryptoJS.mode.CBC,
1081 padding: CryptoJS.pad.Pkcs7,
1082 iv
1083 };
1084 return buffer => wordArrayToBuffer(iv.clone().concat(CryptoJS.AES.encrypt(CryptoJS.lib.WordArray.create(buffer), key, options).ciphertext));
1085 }
1086
1087 end() {
1088 this.dictionary.end();
1089 }
1090
1091}
1092
1093function getPermissionsR2() {
1094 var permissionObject = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1095 var permissions = 0xffffffc0 >> 0;
1096
1097 if (permissionObject.printing) {
1098 permissions |= 0b000000000100;
1099 }
1100
1101 if (permissionObject.modifying) {
1102 permissions |= 0b000000001000;
1103 }
1104
1105 if (permissionObject.copying) {
1106 permissions |= 0b000000010000;
1107 }
1108
1109 if (permissionObject.annotating) {
1110 permissions |= 0b000000100000;
1111 }
1112
1113 return permissions;
1114}
1115
1116function getPermissionsR3() {
1117 var permissionObject = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1118 var permissions = 0xfffff0c0 >> 0;
1119
1120 if (permissionObject.printing === 'lowResolution') {
1121 permissions |= 0b000000000100;
1122 }
1123
1124 if (permissionObject.printing === 'highResolution') {
1125 permissions |= 0b100000000100;
1126 }
1127
1128 if (permissionObject.modifying) {
1129 permissions |= 0b000000001000;
1130 }
1131
1132 if (permissionObject.copying) {
1133 permissions |= 0b000000010000;
1134 }
1135
1136 if (permissionObject.annotating) {
1137 permissions |= 0b000000100000;
1138 }
1139
1140 if (permissionObject.fillingForms) {
1141 permissions |= 0b000100000000;
1142 }
1143
1144 if (permissionObject.contentAccessibility) {
1145 permissions |= 0b001000000000;
1146 }
1147
1148 if (permissionObject.documentAssembly) {
1149 permissions |= 0b010000000000;
1150 }
1151
1152 return permissions;
1153}
1154
1155function getUserPasswordR2(encryptionKey) {
1156 return CryptoJS.RC4.encrypt(processPasswordR2R3R4(), encryptionKey).ciphertext;
1157}
1158
1159function getUserPasswordR3R4(documentId, encryptionKey) {
1160 var key = encryptionKey.clone();
1161 var cipher = CryptoJS.MD5(processPasswordR2R3R4().concat(CryptoJS.lib.WordArray.create(documentId)));
1162
1163 for (var i = 0; i < 20; i++) {
1164 var xorRound = Math.ceil(key.sigBytes / 4);
1165
1166 for (var j = 0; j < xorRound; j++) {
1167 key.words[j] = encryptionKey.words[j] ^ (i | i << 8 | i << 16 | i << 24);
1168 }
1169
1170 cipher = CryptoJS.RC4.encrypt(cipher, key).ciphertext;
1171 }
1172
1173 return cipher.concat(CryptoJS.lib.WordArray.create(null, 16));
1174}
1175
1176function getOwnerPasswordR2R3R4(r, keyBits, paddedUserPassword, paddedOwnerPassword) {
1177 var digest = paddedOwnerPassword;
1178 var round = r >= 3 ? 51 : 1;
1179
1180 for (var i = 0; i < round; i++) {
1181 digest = CryptoJS.MD5(digest);
1182 }
1183
1184 var key = digest.clone();
1185 key.sigBytes = keyBits / 8;
1186 var cipher = paddedUserPassword;
1187 round = r >= 3 ? 20 : 1;
1188
1189 for (var _i = 0; _i < round; _i++) {
1190 var xorRound = Math.ceil(key.sigBytes / 4);
1191
1192 for (var j = 0; j < xorRound; j++) {
1193 key.words[j] = digest.words[j] ^ (_i | _i << 8 | _i << 16 | _i << 24);
1194 }
1195
1196 cipher = CryptoJS.RC4.encrypt(cipher, key).ciphertext;
1197 }
1198
1199 return cipher;
1200}
1201
1202function getEncryptionKeyR2R3R4(r, keyBits, documentId, paddedUserPassword, ownerPasswordEntry, permissions) {
1203 var key = paddedUserPassword.clone().concat(ownerPasswordEntry).concat(CryptoJS.lib.WordArray.create([lsbFirstWord(permissions)], 4)).concat(CryptoJS.lib.WordArray.create(documentId));
1204 var round = r >= 3 ? 51 : 1;
1205
1206 for (var i = 0; i < round; i++) {
1207 key = CryptoJS.MD5(key);
1208 key.sigBytes = keyBits / 8;
1209 }
1210
1211 return key;
1212}
1213
1214function getUserPasswordR5(processedUserPassword, generateRandomWordArray) {
1215 var validationSalt = generateRandomWordArray(8);
1216 var keySalt = generateRandomWordArray(8);
1217 return CryptoJS.SHA256(processedUserPassword.clone().concat(validationSalt)).concat(validationSalt).concat(keySalt);
1218}
1219
1220function getUserEncryptionKeyR5(processedUserPassword, userKeySalt, encryptionKey) {
1221 var key = CryptoJS.SHA256(processedUserPassword.clone().concat(userKeySalt));
1222 var options = {
1223 mode: CryptoJS.mode.CBC,
1224 padding: CryptoJS.pad.NoPadding,
1225 iv: CryptoJS.lib.WordArray.create(null, 16)
1226 };
1227 return CryptoJS.AES.encrypt(encryptionKey, key, options).ciphertext;
1228}
1229
1230function getOwnerPasswordR5(processedOwnerPassword, userPasswordEntry, generateRandomWordArray) {
1231 var validationSalt = generateRandomWordArray(8);
1232 var keySalt = generateRandomWordArray(8);
1233 return CryptoJS.SHA256(processedOwnerPassword.clone().concat(validationSalt).concat(userPasswordEntry)).concat(validationSalt).concat(keySalt);
1234}
1235
1236function getOwnerEncryptionKeyR5(processedOwnerPassword, ownerKeySalt, userPasswordEntry, encryptionKey) {
1237 var key = CryptoJS.SHA256(processedOwnerPassword.clone().concat(ownerKeySalt).concat(userPasswordEntry));
1238 var options = {
1239 mode: CryptoJS.mode.CBC,
1240 padding: CryptoJS.pad.NoPadding,
1241 iv: CryptoJS.lib.WordArray.create(null, 16)
1242 };
1243 return CryptoJS.AES.encrypt(encryptionKey, key, options).ciphertext;
1244}
1245
1246function getEncryptionKeyR5(generateRandomWordArray) {
1247 return generateRandomWordArray(32);
1248}
1249
1250function getEncryptedPermissionsR5(permissions, encryptionKey, generateRandomWordArray) {
1251 var cipher = CryptoJS.lib.WordArray.create([lsbFirstWord(permissions), 0xffffffff, 0x54616462], 12).concat(generateRandomWordArray(4));
1252 var options = {
1253 mode: CryptoJS.mode.ECB,
1254 padding: CryptoJS.pad.NoPadding
1255 };
1256 return CryptoJS.AES.encrypt(cipher, encryptionKey, options).ciphertext;
1257}
1258
1259function processPasswordR2R3R4() {
1260 var password = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
1261 var out = Buffer.alloc(32);
1262 var length = password.length;
1263 var index = 0;
1264
1265 while (index < length && index < 32) {
1266 var code = password.charCodeAt(index);
1267
1268 if (code > 0xff) {
1269 throw new Error('Password contains one or more invalid characters.');
1270 }
1271
1272 out[index] = code;
1273 index++;
1274 }
1275
1276 while (index < 32) {
1277 out[index] = PASSWORD_PADDING[index - length];
1278 index++;
1279 }
1280
1281 return CryptoJS.lib.WordArray.create(out);
1282}
1283
1284function processPasswordR5() {
1285 var password = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
1286 password = unescape(encodeURIComponent(saslprep(password)));
1287 var length = Math.min(127, password.length);
1288 var out = Buffer.alloc(length);
1289
1290 for (var i = 0; i < length; i++) {
1291 out[i] = password.charCodeAt(i);
1292 }
1293
1294 return CryptoJS.lib.WordArray.create(out);
1295}
1296
1297function lsbFirstWord(data) {
1298 return (data & 0xff) << 24 | (data & 0xff00) << 8 | data >> 8 & 0xff00 | data >> 24 & 0xff;
1299}
1300
1301function wordArrayToBuffer(wordArray) {
1302 var byteArray = [];
1303
1304 for (var i = 0; i < wordArray.sigBytes; i++) {
1305 byteArray.push(wordArray.words[Math.floor(i / 4)] >> 8 * (3 - i % 4) & 0xff);
1306 }
1307
1308 return Buffer.from(byteArray);
1309}
1310
1311var 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];
1312
1313var {
1314 number
1315} = PDFObject;
1316
1317class PDFGradient {
1318 constructor(doc) {
1319 this.doc = doc;
1320 this.stops = [];
1321 this.embedded = false;
1322 this.transform = [1, 0, 0, 1, 0, 0];
1323 }
1324
1325 stop(pos, color, opacity) {
1326 if (opacity == null) {
1327 opacity = 1;
1328 }
1329
1330 color = this.doc._normalizeColor(color);
1331
1332 if (this.stops.length === 0) {
1333 if (color.length === 3) {
1334 this._colorSpace = 'DeviceRGB';
1335 } else if (color.length === 4) {
1336 this._colorSpace = 'DeviceCMYK';
1337 } else if (color.length === 1) {
1338 this._colorSpace = 'DeviceGray';
1339 } else {
1340 throw new Error('Unknown color space');
1341 }
1342 } else if (this._colorSpace === 'DeviceRGB' && color.length !== 3 || this._colorSpace === 'DeviceCMYK' && color.length !== 4 || this._colorSpace === 'DeviceGray' && color.length !== 1) {
1343 throw new Error('All gradient stops must use the same color space');
1344 }
1345
1346 opacity = Math.max(0, Math.min(1, opacity));
1347 this.stops.push([pos, color, opacity]);
1348 return this;
1349 }
1350
1351 setTransform(m11, m12, m21, m22, dx, dy) {
1352 this.transform = [m11, m12, m21, m22, dx, dy];
1353 return this;
1354 }
1355
1356 embed(m) {
1357 var fn;
1358 var stopsLength = this.stops.length;
1359
1360 if (stopsLength === 0) {
1361 return;
1362 }
1363
1364 this.embedded = true;
1365 this.matrix = m; // if the last stop comes before 100%, add a copy at 100%
1366
1367 var last = this.stops[stopsLength - 1];
1368
1369 if (last[0] < 1) {
1370 this.stops.push([1, last[1], last[2]]);
1371 }
1372
1373 var bounds = [];
1374 var encode = [];
1375 var stops = [];
1376
1377 for (var i = 0; i < stopsLength - 1; i++) {
1378 encode.push(0, 1);
1379
1380 if (i + 2 !== stopsLength) {
1381 bounds.push(this.stops[i + 1][0]);
1382 }
1383
1384 fn = this.doc.ref({
1385 FunctionType: 2,
1386 Domain: [0, 1],
1387 C0: this.stops[i + 0][1],
1388 C1: this.stops[i + 1][1],
1389 N: 1
1390 });
1391 stops.push(fn);
1392 fn.end();
1393 } // if there are only two stops, we don't need a stitching function
1394
1395
1396 if (stopsLength === 1) {
1397 fn = stops[0];
1398 } else {
1399 fn = this.doc.ref({
1400 FunctionType: 3,
1401 // stitching function
1402 Domain: [0, 1],
1403 Functions: stops,
1404 Bounds: bounds,
1405 Encode: encode
1406 });
1407 fn.end();
1408 }
1409
1410 this.id = "Sh".concat(++this.doc._gradCount);
1411 var shader = this.shader(fn);
1412 shader.end();
1413 var pattern = this.doc.ref({
1414 Type: 'Pattern',
1415 PatternType: 2,
1416 Shading: shader,
1417 Matrix: this.matrix.map(number)
1418 });
1419 pattern.end();
1420
1421 if (this.stops.some(stop => stop[2] < 1)) {
1422 var grad = this.opacityGradient();
1423 grad._colorSpace = 'DeviceGray';
1424
1425 for (var stop of this.stops) {
1426 grad.stop(stop[0], [stop[2]]);
1427 }
1428
1429 grad = grad.embed(this.matrix);
1430 var pageBBox = [0, 0, this.doc.page.width, this.doc.page.height];
1431 var form = this.doc.ref({
1432 Type: 'XObject',
1433 Subtype: 'Form',
1434 FormType: 1,
1435 BBox: pageBBox,
1436 Group: {
1437 Type: 'Group',
1438 S: 'Transparency',
1439 CS: 'DeviceGray'
1440 },
1441 Resources: {
1442 ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'],
1443 Pattern: {
1444 Sh1: grad
1445 }
1446 }
1447 });
1448 form.write('/Pattern cs /Sh1 scn');
1449 form.end("".concat(pageBBox.join(' '), " re f"));
1450 var gstate = this.doc.ref({
1451 Type: 'ExtGState',
1452 SMask: {
1453 Type: 'Mask',
1454 S: 'Luminosity',
1455 G: form
1456 }
1457 });
1458 gstate.end();
1459 var opacityPattern = this.doc.ref({
1460 Type: 'Pattern',
1461 PatternType: 1,
1462 PaintType: 1,
1463 TilingType: 2,
1464 BBox: pageBBox,
1465 XStep: pageBBox[2],
1466 YStep: pageBBox[3],
1467 Resources: {
1468 ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'],
1469 Pattern: {
1470 Sh1: pattern
1471 },
1472 ExtGState: {
1473 Gs1: gstate
1474 }
1475 }
1476 });
1477 opacityPattern.write('/Gs1 gs /Pattern cs /Sh1 scn');
1478 opacityPattern.end("".concat(pageBBox.join(' '), " re f"));
1479 this.doc.page.patterns[this.id] = opacityPattern;
1480 } else {
1481 this.doc.page.patterns[this.id] = pattern;
1482 }
1483
1484 return pattern;
1485 }
1486
1487 apply(stroke) {
1488 // apply gradient transform to existing document ctm
1489 var [m0, m1, m2, m3, m4, m5] = this.doc._ctm;
1490 var [m11, m12, m21, m22, dx, dy] = this.transform;
1491 var 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];
1492
1493 if (!this.embedded || m.join(' ') !== this.matrix.join(' ')) {
1494 this.embed(m);
1495 }
1496
1497 this.doc._setColorSpace('Pattern', stroke);
1498
1499 var op = stroke ? 'SCN' : 'scn';
1500 return this.doc.addContent("/".concat(this.id, " ").concat(op));
1501 }
1502
1503}
1504
1505class PDFLinearGradient extends PDFGradient {
1506 constructor(doc, x1, y1, x2, y2) {
1507 super(doc);
1508 this.x1 = x1;
1509 this.y1 = y1;
1510 this.x2 = x2;
1511 this.y2 = y2;
1512 }
1513
1514 shader(fn) {
1515 return this.doc.ref({
1516 ShadingType: 2,
1517 ColorSpace: this._colorSpace,
1518 Coords: [this.x1, this.y1, this.x2, this.y2],
1519 Function: fn,
1520 Extend: [true, true]
1521 });
1522 }
1523
1524 opacityGradient() {
1525 return new PDFLinearGradient(this.doc, this.x1, this.y1, this.x2, this.y2);
1526 }
1527
1528}
1529
1530class PDFRadialGradient extends PDFGradient {
1531 constructor(doc, x1, y1, r1, x2, y2, r2) {
1532 super(doc);
1533 this.doc = doc;
1534 this.x1 = x1;
1535 this.y1 = y1;
1536 this.r1 = r1;
1537 this.x2 = x2;
1538 this.y2 = y2;
1539 this.r2 = r2;
1540 }
1541
1542 shader(fn) {
1543 return this.doc.ref({
1544 ShadingType: 3,
1545 ColorSpace: this._colorSpace,
1546 Coords: [this.x1, this.y1, this.r1, this.x2, this.y2, this.r2],
1547 Function: fn,
1548 Extend: [true, true]
1549 });
1550 }
1551
1552 opacityGradient() {
1553 return new PDFRadialGradient(this.doc, this.x1, this.y1, this.r1, this.x2, this.y2, this.r2);
1554 }
1555
1556}
1557
1558var Gradient = {
1559 PDFGradient,
1560 PDFLinearGradient,
1561 PDFRadialGradient
1562};
1563
1564/*
1565PDF tiling pattern support. Uncolored only.
1566 */
1567var underlyingColorSpaces = ['DeviceCMYK', 'DeviceRGB'];
1568
1569class PDFTilingPattern {
1570 constructor(doc, bBox, xStep, yStep, stream) {
1571 this.doc = doc;
1572 this.bBox = bBox;
1573 this.xStep = xStep;
1574 this.yStep = yStep;
1575 this.stream = stream;
1576 }
1577
1578 createPattern() {
1579 // no resources needed for our current usage
1580 // required entry
1581 var resources = this.doc.ref();
1582 resources.end(); // apply default transform matrix (flipped in the default doc._ctm)
1583 // see document.js & gradient.js
1584
1585 var [m0, m1, m2, m3, m4, m5] = this.doc._ctm;
1586 var [m11, m12, m21, m22, dx, dy] = [1, 0, 0, 1, 0, 0];
1587 var 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];
1588 var pattern = this.doc.ref({
1589 Type: 'Pattern',
1590 PatternType: 1,
1591 // tiling
1592 PaintType: 2,
1593 // 1-colored, 2-uncolored
1594 TilingType: 2,
1595 // 2-no distortion
1596 BBox: this.bBox,
1597 XStep: this.xStep,
1598 YStep: this.yStep,
1599 Matrix: m.map(v => +v.toFixed(5)),
1600 Resources: resources
1601 });
1602 pattern.end(this.stream);
1603 return pattern;
1604 }
1605
1606 embedPatternColorSpaces() {
1607 // map each pattern to an underlying color space
1608 // and embed on each page
1609 underlyingColorSpaces.forEach(csName => {
1610 var csId = this.getPatternColorSpaceId(csName);
1611 if (this.doc.page.colorSpaces[csId]) return;
1612 var cs = this.doc.ref(['Pattern', csName]);
1613 cs.end();
1614 this.doc.page.colorSpaces[csId] = cs;
1615 });
1616 }
1617
1618 getPatternColorSpaceId(underlyingColorspace) {
1619 return "CsP".concat(underlyingColorspace);
1620 }
1621
1622 embed() {
1623 if (!this.id) {
1624 this.doc._patternCount = this.doc._patternCount + 1;
1625 this.id = 'P' + this.doc._patternCount;
1626 this.pattern = this.createPattern();
1627 } // patterns are embedded in each page
1628
1629
1630 if (!this.doc.page.patterns[this.id]) {
1631 this.doc.page.patterns[this.id] = this.pattern;
1632 }
1633 }
1634
1635 apply(stroke, patternColor) {
1636 // do any embedding/creating that might be needed
1637 this.embedPatternColorSpaces();
1638 this.embed();
1639
1640 var normalizedColor = this.doc._normalizeColor(patternColor);
1641
1642 if (!normalizedColor) throw Error("invalid pattern color. (value: ".concat(patternColor, ")")); // select one of the pattern color spaces
1643
1644 var csId = this.getPatternColorSpaceId(this.doc._getColorSpace(normalizedColor));
1645
1646 this.doc._setColorSpace(csId, stroke); // stroke/fill using the pattern and color (in the above underlying color space)
1647
1648
1649 var op = stroke ? 'SCN' : 'scn';
1650 return this.doc.addContent("".concat(normalizedColor.join(' '), " /").concat(this.id, " ").concat(op));
1651 }
1652
1653}
1654
1655var pattern = {
1656 PDFTilingPattern
1657};
1658
1659var {
1660 PDFGradient: PDFGradient$1,
1661 PDFLinearGradient: PDFLinearGradient$1,
1662 PDFRadialGradient: PDFRadialGradient$1
1663} = Gradient;
1664var {
1665 PDFTilingPattern: PDFTilingPattern$1
1666} = pattern;
1667var ColorMixin = {
1668 initColor() {
1669 // The opacity dictionaries
1670 this._opacityRegistry = {};
1671 this._opacityCount = 0;
1672 this._patternCount = 0;
1673 return this._gradCount = 0;
1674 },
1675
1676 _normalizeColor(color) {
1677 if (typeof color === 'string') {
1678 if (color.charAt(0) === '#') {
1679 if (color.length === 4) {
1680 color = color.replace(/#([0-9A-F])([0-9A-F])([0-9A-F])/i, '#$1$1$2$2$3$3');
1681 }
1682
1683 var hex = parseInt(color.slice(1), 16);
1684 color = [hex >> 16, hex >> 8 & 0xff, hex & 0xff];
1685 } else if (namedColors[color]) {
1686 color = namedColors[color];
1687 }
1688 }
1689
1690 if (Array.isArray(color)) {
1691 // RGB
1692 if (color.length === 3) {
1693 color = color.map(part => part / 255); // CMYK
1694 } else if (color.length === 4) {
1695 color = color.map(part => part / 100);
1696 }
1697
1698 return color;
1699 }
1700
1701 return null;
1702 },
1703
1704 _setColor(color, stroke) {
1705 if (color instanceof PDFGradient$1) {
1706 color.apply(stroke);
1707 return true; // see if tiling pattern, decode & apply it it
1708 } else if (Array.isArray(color) && color[0] instanceof PDFTilingPattern$1) {
1709 color[0].apply(stroke, color[1]);
1710 return true;
1711 } // any other case should be a normal color and not a pattern
1712
1713
1714 return this._setColorCore(color, stroke);
1715 },
1716
1717 _setColorCore(color, stroke) {
1718 color = this._normalizeColor(color);
1719
1720 if (!color) {
1721 return false;
1722 }
1723
1724 var op = stroke ? 'SCN' : 'scn';
1725
1726 var space = this._getColorSpace(color);
1727
1728 this._setColorSpace(space, stroke);
1729
1730 color = color.join(' ');
1731 this.addContent("".concat(color, " ").concat(op));
1732 return true;
1733 },
1734
1735 _setColorSpace(space, stroke) {
1736 var op = stroke ? 'CS' : 'cs';
1737 return this.addContent("/".concat(space, " ").concat(op));
1738 },
1739
1740 _getColorSpace(color) {
1741 return color.length === 4 ? 'DeviceCMYK' : 'DeviceRGB';
1742 },
1743
1744 fillColor(color, opacity) {
1745 var set = this._setColor(color, false);
1746
1747 if (set) {
1748 this.fillOpacity(opacity);
1749 } // save this for text wrapper, which needs to reset
1750 // the fill color on new pages
1751
1752
1753 this._fillColor = [color, opacity];
1754 return this;
1755 },
1756
1757 strokeColor(color, opacity) {
1758 var set = this._setColor(color, true);
1759
1760 if (set) {
1761 this.strokeOpacity(opacity);
1762 }
1763
1764 return this;
1765 },
1766
1767 opacity(opacity) {
1768 this._doOpacity(opacity, opacity);
1769
1770 return this;
1771 },
1772
1773 fillOpacity(opacity) {
1774 this._doOpacity(opacity, null);
1775
1776 return this;
1777 },
1778
1779 strokeOpacity(opacity) {
1780 this._doOpacity(null, opacity);
1781
1782 return this;
1783 },
1784
1785 _doOpacity(fillOpacity, strokeOpacity) {
1786 var dictionary, name;
1787
1788 if (fillOpacity == null && strokeOpacity == null) {
1789 return;
1790 }
1791
1792 if (fillOpacity != null) {
1793 fillOpacity = Math.max(0, Math.min(1, fillOpacity));
1794 }
1795
1796 if (strokeOpacity != null) {
1797 strokeOpacity = Math.max(0, Math.min(1, strokeOpacity));
1798 }
1799
1800 var key = "".concat(fillOpacity, "_").concat(strokeOpacity);
1801
1802 if (this._opacityRegistry[key]) {
1803 [dictionary, name] = this._opacityRegistry[key];
1804 } else {
1805 dictionary = {
1806 Type: 'ExtGState'
1807 };
1808
1809 if (fillOpacity != null) {
1810 dictionary.ca = fillOpacity;
1811 }
1812
1813 if (strokeOpacity != null) {
1814 dictionary.CA = strokeOpacity;
1815 }
1816
1817 dictionary = this.ref(dictionary);
1818 dictionary.end();
1819 var id = ++this._opacityCount;
1820 name = "Gs".concat(id);
1821 this._opacityRegistry[key] = [dictionary, name];
1822 }
1823
1824 this.page.ext_gstates[name] = dictionary;
1825 return this.addContent("/".concat(name, " gs"));
1826 },
1827
1828 linearGradient(x1, y1, x2, y2) {
1829 return new PDFLinearGradient$1(this, x1, y1, x2, y2);
1830 },
1831
1832 radialGradient(x1, y1, r1, x2, y2, r2) {
1833 return new PDFRadialGradient$1(this, x1, y1, r1, x2, y2, r2);
1834 },
1835
1836 pattern(bbox, xStep, yStep, stream) {
1837 return new PDFTilingPattern$1(this, bbox, xStep, yStep, stream);
1838 }
1839
1840};
1841var namedColors = {
1842 aliceblue: [240, 248, 255],
1843 antiquewhite: [250, 235, 215],
1844 aqua: [0, 255, 255],
1845 aquamarine: [127, 255, 212],
1846 azure: [240, 255, 255],
1847 beige: [245, 245, 220],
1848 bisque: [255, 228, 196],
1849 black: [0, 0, 0],
1850 blanchedalmond: [255, 235, 205],
1851 blue: [0, 0, 255],
1852 blueviolet: [138, 43, 226],
1853 brown: [165, 42, 42],
1854 burlywood: [222, 184, 135],
1855 cadetblue: [95, 158, 160],
1856 chartreuse: [127, 255, 0],
1857 chocolate: [210, 105, 30],
1858 coral: [255, 127, 80],
1859 cornflowerblue: [100, 149, 237],
1860 cornsilk: [255, 248, 220],
1861 crimson: [220, 20, 60],
1862 cyan: [0, 255, 255],
1863 darkblue: [0, 0, 139],
1864 darkcyan: [0, 139, 139],
1865 darkgoldenrod: [184, 134, 11],
1866 darkgray: [169, 169, 169],
1867 darkgreen: [0, 100, 0],
1868 darkgrey: [169, 169, 169],
1869 darkkhaki: [189, 183, 107],
1870 darkmagenta: [139, 0, 139],
1871 darkolivegreen: [85, 107, 47],
1872 darkorange: [255, 140, 0],
1873 darkorchid: [153, 50, 204],
1874 darkred: [139, 0, 0],
1875 darksalmon: [233, 150, 122],
1876 darkseagreen: [143, 188, 143],
1877 darkslateblue: [72, 61, 139],
1878 darkslategray: [47, 79, 79],
1879 darkslategrey: [47, 79, 79],
1880 darkturquoise: [0, 206, 209],
1881 darkviolet: [148, 0, 211],
1882 deeppink: [255, 20, 147],
1883 deepskyblue: [0, 191, 255],
1884 dimgray: [105, 105, 105],
1885 dimgrey: [105, 105, 105],
1886 dodgerblue: [30, 144, 255],
1887 firebrick: [178, 34, 34],
1888 floralwhite: [255, 250, 240],
1889 forestgreen: [34, 139, 34],
1890 fuchsia: [255, 0, 255],
1891 gainsboro: [220, 220, 220],
1892 ghostwhite: [248, 248, 255],
1893 gold: [255, 215, 0],
1894 goldenrod: [218, 165, 32],
1895 gray: [128, 128, 128],
1896 grey: [128, 128, 128],
1897 green: [0, 128, 0],
1898 greenyellow: [173, 255, 47],
1899 honeydew: [240, 255, 240],
1900 hotpink: [255, 105, 180],
1901 indianred: [205, 92, 92],
1902 indigo: [75, 0, 130],
1903 ivory: [255, 255, 240],
1904 khaki: [240, 230, 140],
1905 lavender: [230, 230, 250],
1906 lavenderblush: [255, 240, 245],
1907 lawngreen: [124, 252, 0],
1908 lemonchiffon: [255, 250, 205],
1909 lightblue: [173, 216, 230],
1910 lightcoral: [240, 128, 128],
1911 lightcyan: [224, 255, 255],
1912 lightgoldenrodyellow: [250, 250, 210],
1913 lightgray: [211, 211, 211],
1914 lightgreen: [144, 238, 144],
1915 lightgrey: [211, 211, 211],
1916 lightpink: [255, 182, 193],
1917 lightsalmon: [255, 160, 122],
1918 lightseagreen: [32, 178, 170],
1919 lightskyblue: [135, 206, 250],
1920 lightslategray: [119, 136, 153],
1921 lightslategrey: [119, 136, 153],
1922 lightsteelblue: [176, 196, 222],
1923 lightyellow: [255, 255, 224],
1924 lime: [0, 255, 0],
1925 limegreen: [50, 205, 50],
1926 linen: [250, 240, 230],
1927 magenta: [255, 0, 255],
1928 maroon: [128, 0, 0],
1929 mediumaquamarine: [102, 205, 170],
1930 mediumblue: [0, 0, 205],
1931 mediumorchid: [186, 85, 211],
1932 mediumpurple: [147, 112, 219],
1933 mediumseagreen: [60, 179, 113],
1934 mediumslateblue: [123, 104, 238],
1935 mediumspringgreen: [0, 250, 154],
1936 mediumturquoise: [72, 209, 204],
1937 mediumvioletred: [199, 21, 133],
1938 midnightblue: [25, 25, 112],
1939 mintcream: [245, 255, 250],
1940 mistyrose: [255, 228, 225],
1941 moccasin: [255, 228, 181],
1942 navajowhite: [255, 222, 173],
1943 navy: [0, 0, 128],
1944 oldlace: [253, 245, 230],
1945 olive: [128, 128, 0],
1946 olivedrab: [107, 142, 35],
1947 orange: [255, 165, 0],
1948 orangered: [255, 69, 0],
1949 orchid: [218, 112, 214],
1950 palegoldenrod: [238, 232, 170],
1951 palegreen: [152, 251, 152],
1952 paleturquoise: [175, 238, 238],
1953 palevioletred: [219, 112, 147],
1954 papayawhip: [255, 239, 213],
1955 peachpuff: [255, 218, 185],
1956 peru: [205, 133, 63],
1957 pink: [255, 192, 203],
1958 plum: [221, 160, 221],
1959 powderblue: [176, 224, 230],
1960 purple: [128, 0, 128],
1961 red: [255, 0, 0],
1962 rosybrown: [188, 143, 143],
1963 royalblue: [65, 105, 225],
1964 saddlebrown: [139, 69, 19],
1965 salmon: [250, 128, 114],
1966 sandybrown: [244, 164, 96],
1967 seagreen: [46, 139, 87],
1968 seashell: [255, 245, 238],
1969 sienna: [160, 82, 45],
1970 silver: [192, 192, 192],
1971 skyblue: [135, 206, 235],
1972 slateblue: [106, 90, 205],
1973 slategray: [112, 128, 144],
1974 slategrey: [112, 128, 144],
1975 snow: [255, 250, 250],
1976 springgreen: [0, 255, 127],
1977 steelblue: [70, 130, 180],
1978 tan: [210, 180, 140],
1979 teal: [0, 128, 128],
1980 thistle: [216, 191, 216],
1981 tomato: [255, 99, 71],
1982 turquoise: [64, 224, 208],
1983 violet: [238, 130, 238],
1984 wheat: [245, 222, 179],
1985 white: [255, 255, 255],
1986 whitesmoke: [245, 245, 245],
1987 yellow: [255, 255, 0],
1988 yellowgreen: [154, 205, 50]
1989};
1990
1991var cx, cy, px, py, sx, sy;
1992cx = cy = px = py = sx = sy = 0;
1993var parameters = {
1994 A: 7,
1995 a: 7,
1996 C: 6,
1997 c: 6,
1998 H: 1,
1999 h: 1,
2000 L: 2,
2001 l: 2,
2002 M: 2,
2003 m: 2,
2004 Q: 4,
2005 q: 4,
2006 S: 4,
2007 s: 4,
2008 T: 2,
2009 t: 2,
2010 V: 1,
2011 v: 1,
2012 Z: 0,
2013 z: 0
2014};
2015
2016var parse = function parse(path) {
2017 var cmd;
2018 var ret = [];
2019 var args = [];
2020 var curArg = '';
2021 var foundDecimal = false;
2022 var params = 0;
2023
2024 for (var c of path) {
2025 if (parameters[c] != null) {
2026 params = parameters[c];
2027
2028 if (cmd) {
2029 // save existing command
2030 if (curArg.length > 0) {
2031 args[args.length] = +curArg;
2032 }
2033
2034 ret[ret.length] = {
2035 cmd,
2036 args
2037 };
2038 args = [];
2039 curArg = '';
2040 foundDecimal = false;
2041 }
2042
2043 cmd = c;
2044 } else if ([' ', ','].includes(c) || c === '-' && curArg.length > 0 && curArg[curArg.length - 1] !== 'e' || c === '.' && foundDecimal) {
2045 if (curArg.length === 0) {
2046 continue;
2047 }
2048
2049 if (args.length === params) {
2050 // handle reused commands
2051 ret[ret.length] = {
2052 cmd,
2053 args
2054 };
2055 args = [+curArg]; // handle assumed commands
2056
2057 if (cmd === 'M') {
2058 cmd = 'L';
2059 }
2060
2061 if (cmd === 'm') {
2062 cmd = 'l';
2063 }
2064 } else {
2065 args[args.length] = +curArg;
2066 }
2067
2068 foundDecimal = c === '.'; // fix for negative numbers or repeated decimals with no delimeter between commands
2069
2070 curArg = ['-', '.'].includes(c) ? c : '';
2071 } else {
2072 curArg += c;
2073
2074 if (c === '.') {
2075 foundDecimal = true;
2076 }
2077 }
2078 } // add the last command
2079
2080
2081 if (curArg.length > 0) {
2082 if (args.length === params) {
2083 // handle reused commands
2084 ret[ret.length] = {
2085 cmd,
2086 args
2087 };
2088 args = [+curArg]; // handle assumed commands
2089
2090 if (cmd === 'M') {
2091 cmd = 'L';
2092 }
2093
2094 if (cmd === 'm') {
2095 cmd = 'l';
2096 }
2097 } else {
2098 args[args.length] = +curArg;
2099 }
2100 }
2101
2102 ret[ret.length] = {
2103 cmd,
2104 args
2105 };
2106 return ret;
2107};
2108
2109var apply = function apply(commands, doc) {
2110 // current point, control point, and subpath starting point
2111 cx = cy = px = py = sx = sy = 0; // run the commands
2112
2113 for (var i = 0; i < commands.length; i++) {
2114 var c = commands[i];
2115
2116 if (typeof runners[c.cmd] === 'function') {
2117 runners[c.cmd](doc, c.args);
2118 }
2119 }
2120};
2121
2122var runners = {
2123 M(doc, a) {
2124 cx = a[0];
2125 cy = a[1];
2126 px = py = null;
2127 sx = cx;
2128 sy = cy;
2129 return doc.moveTo(cx, cy);
2130 },
2131
2132 m(doc, a) {
2133 cx += a[0];
2134 cy += a[1];
2135 px = py = null;
2136 sx = cx;
2137 sy = cy;
2138 return doc.moveTo(cx, cy);
2139 },
2140
2141 C(doc, a) {
2142 cx = a[4];
2143 cy = a[5];
2144 px = a[2];
2145 py = a[3];
2146 return doc.bezierCurveTo(...a);
2147 },
2148
2149 c(doc, a) {
2150 doc.bezierCurveTo(a[0] + cx, a[1] + cy, a[2] + cx, a[3] + cy, a[4] + cx, a[5] + cy);
2151 px = cx + a[2];
2152 py = cy + a[3];
2153 cx += a[4];
2154 return cy += a[5];
2155 },
2156
2157 S(doc, a) {
2158 if (px === null) {
2159 px = cx;
2160 py = cy;
2161 }
2162
2163 doc.bezierCurveTo(cx - (px - cx), cy - (py - cy), a[0], a[1], a[2], a[3]);
2164 px = a[0];
2165 py = a[1];
2166 cx = a[2];
2167 return cy = a[3];
2168 },
2169
2170 s(doc, a) {
2171 if (px === null) {
2172 px = cx;
2173 py = cy;
2174 }
2175
2176 doc.bezierCurveTo(cx - (px - cx), cy - (py - cy), cx + a[0], cy + a[1], cx + a[2], cy + a[3]);
2177 px = cx + a[0];
2178 py = cy + a[1];
2179 cx += a[2];
2180 return cy += a[3];
2181 },
2182
2183 Q(doc, a) {
2184 px = a[0];
2185 py = a[1];
2186 cx = a[2];
2187 cy = a[3];
2188 return doc.quadraticCurveTo(a[0], a[1], cx, cy);
2189 },
2190
2191 q(doc, a) {
2192 doc.quadraticCurveTo(a[0] + cx, a[1] + cy, a[2] + cx, a[3] + cy);
2193 px = cx + a[0];
2194 py = cy + a[1];
2195 cx += a[2];
2196 return cy += a[3];
2197 },
2198
2199 T(doc, a) {
2200 if (px === null) {
2201 px = cx;
2202 py = cy;
2203 } else {
2204 px = cx - (px - cx);
2205 py = cy - (py - cy);
2206 }
2207
2208 doc.quadraticCurveTo(px, py, a[0], a[1]);
2209 px = cx - (px - cx);
2210 py = cy - (py - cy);
2211 cx = a[0];
2212 return cy = a[1];
2213 },
2214
2215 t(doc, a) {
2216 if (px === null) {
2217 px = cx;
2218 py = cy;
2219 } else {
2220 px = cx - (px - cx);
2221 py = cy - (py - cy);
2222 }
2223
2224 doc.quadraticCurveTo(px, py, cx + a[0], cy + a[1]);
2225 cx += a[0];
2226 return cy += a[1];
2227 },
2228
2229 A(doc, a) {
2230 solveArc(doc, cx, cy, a);
2231 cx = a[5];
2232 return cy = a[6];
2233 },
2234
2235 a(doc, a) {
2236 a[5] += cx;
2237 a[6] += cy;
2238 solveArc(doc, cx, cy, a);
2239 cx = a[5];
2240 return cy = a[6];
2241 },
2242
2243 L(doc, a) {
2244 cx = a[0];
2245 cy = a[1];
2246 px = py = null;
2247 return doc.lineTo(cx, cy);
2248 },
2249
2250 l(doc, a) {
2251 cx += a[0];
2252 cy += a[1];
2253 px = py = null;
2254 return doc.lineTo(cx, cy);
2255 },
2256
2257 H(doc, a) {
2258 cx = a[0];
2259 px = py = null;
2260 return doc.lineTo(cx, cy);
2261 },
2262
2263 h(doc, a) {
2264 cx += a[0];
2265 px = py = null;
2266 return doc.lineTo(cx, cy);
2267 },
2268
2269 V(doc, a) {
2270 cy = a[0];
2271 px = py = null;
2272 return doc.lineTo(cx, cy);
2273 },
2274
2275 v(doc, a) {
2276 cy += a[0];
2277 px = py = null;
2278 return doc.lineTo(cx, cy);
2279 },
2280
2281 Z(doc) {
2282 doc.closePath();
2283 cx = sx;
2284 return cy = sy;
2285 },
2286
2287 z(doc) {
2288 doc.closePath();
2289 cx = sx;
2290 return cy = sy;
2291 }
2292
2293};
2294
2295var solveArc = function solveArc(doc, x, y, coords) {
2296 var [rx, ry, rot, large, sweep, ex, ey] = coords;
2297 var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y);
2298
2299 for (var seg of segs) {
2300 var bez = segmentToBezier(...seg);
2301 doc.bezierCurveTo(...bez);
2302 }
2303}; // from Inkscape svgtopdf, thanks!
2304
2305
2306var arcToSegments = function arcToSegments(x, y, rx, ry, large, sweep, rotateX, ox, oy) {
2307 var th = rotateX * (Math.PI / 180);
2308 var sin_th = Math.sin(th);
2309 var cos_th = Math.cos(th);
2310 rx = Math.abs(rx);
2311 ry = Math.abs(ry);
2312 px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
2313 py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
2314 var pl = px * px / (rx * rx) + py * py / (ry * ry);
2315
2316 if (pl > 1) {
2317 pl = Math.sqrt(pl);
2318 rx *= pl;
2319 ry *= pl;
2320 }
2321
2322 var a00 = cos_th / rx;
2323 var a01 = sin_th / rx;
2324 var a10 = -sin_th / ry;
2325 var a11 = cos_th / ry;
2326 var x0 = a00 * ox + a01 * oy;
2327 var y0 = a10 * ox + a11 * oy;
2328 var x1 = a00 * x + a01 * y;
2329 var y1 = a10 * x + a11 * y;
2330 var d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
2331 var sfactor_sq = 1 / d - 0.25;
2332
2333 if (sfactor_sq < 0) {
2334 sfactor_sq = 0;
2335 }
2336
2337 var sfactor = Math.sqrt(sfactor_sq);
2338
2339 if (sweep === large) {
2340 sfactor = -sfactor;
2341 }
2342
2343 var xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
2344 var yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
2345 var th0 = Math.atan2(y0 - yc, x0 - xc);
2346 var th1 = Math.atan2(y1 - yc, x1 - xc);
2347 var th_arc = th1 - th0;
2348
2349 if (th_arc < 0 && sweep === 1) {
2350 th_arc += 2 * Math.PI;
2351 } else if (th_arc > 0 && sweep === 0) {
2352 th_arc -= 2 * Math.PI;
2353 }
2354
2355 var segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001)));
2356 var result = [];
2357
2358 for (var i = 0; i < segments; i++) {
2359 var th2 = th0 + i * th_arc / segments;
2360 var th3 = th0 + (i + 1) * th_arc / segments;
2361 result[i] = [xc, yc, th2, th3, rx, ry, sin_th, cos_th];
2362 }
2363
2364 return result;
2365};
2366
2367var segmentToBezier = function segmentToBezier(cx, cy, th0, th1, rx, ry, sin_th, cos_th) {
2368 var a00 = cos_th * rx;
2369 var a01 = -sin_th * ry;
2370 var a10 = sin_th * rx;
2371 var a11 = cos_th * ry;
2372 var th_half = 0.5 * (th1 - th0);
2373 var t = 8 / 3 * Math.sin(th_half * 0.5) * Math.sin(th_half * 0.5) / Math.sin(th_half);
2374 var x1 = cx + Math.cos(th0) - t * Math.sin(th0);
2375 var y1 = cy + Math.sin(th0) + t * Math.cos(th0);
2376 var x3 = cx + Math.cos(th1);
2377 var y3 = cy + Math.sin(th1);
2378 var x2 = x3 + t * Math.sin(th1);
2379 var y2 = y3 - t * Math.cos(th1);
2380 return [a00 * x1 + a01 * y1, a10 * x1 + a11 * y1, a00 * x2 + a01 * y2, a10 * x2 + a11 * y2, a00 * x3 + a01 * y3, a10 * x3 + a11 * y3];
2381};
2382
2383class SVGPath {
2384 static apply(doc, path) {
2385 var commands = parse(path);
2386 apply(commands, doc);
2387 }
2388
2389}
2390
2391var {
2392 number: number$1
2393} = PDFObject; // This constant is used to approximate a symmetrical arc using a cubic
2394// Bezier curve.
2395
2396var KAPPA = 4.0 * ((Math.sqrt(2) - 1.0) / 3.0);
2397var VectorMixin = {
2398 initVector() {
2399 this._ctm = [1, 0, 0, 1, 0, 0]; // current transformation matrix
2400
2401 return this._ctmStack = [];
2402 },
2403
2404 save() {
2405 this._ctmStack.push(this._ctm.slice()); // TODO: save/restore colorspace and styles so not setting it unnessesarily all the time?
2406
2407
2408 return this.addContent('q');
2409 },
2410
2411 restore() {
2412 this._ctm = this._ctmStack.pop() || [1, 0, 0, 1, 0, 0];
2413 return this.addContent('Q');
2414 },
2415
2416 closePath() {
2417 return this.addContent('h');
2418 },
2419
2420 lineWidth(w) {
2421 return this.addContent("".concat(number$1(w), " w"));
2422 },
2423
2424 _CAP_STYLES: {
2425 BUTT: 0,
2426 ROUND: 1,
2427 SQUARE: 2
2428 },
2429
2430 lineCap(c) {
2431 if (typeof c === 'string') {
2432 c = this._CAP_STYLES[c.toUpperCase()];
2433 }
2434
2435 return this.addContent("".concat(c, " J"));
2436 },
2437
2438 _JOIN_STYLES: {
2439 MITER: 0,
2440 ROUND: 1,
2441 BEVEL: 2
2442 },
2443
2444 lineJoin(j) {
2445 if (typeof j === 'string') {
2446 j = this._JOIN_STYLES[j.toUpperCase()];
2447 }
2448
2449 return this.addContent("".concat(j, " j"));
2450 },
2451
2452 miterLimit(m) {
2453 return this.addContent("".concat(number$1(m), " M"));
2454 },
2455
2456 dash(length) {
2457 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2458 var originalLength = length;
2459
2460 if (!Array.isArray(length)) {
2461 length = [length, options.space || length];
2462 }
2463
2464 var valid = length.every(x => Number.isFinite(x) && x > 0);
2465
2466 if (!valid) {
2467 throw new Error("dash(".concat(JSON.stringify(originalLength), ", ").concat(JSON.stringify(options), ") invalid, lengths must be numeric and greater than zero"));
2468 }
2469
2470 length = length.map(number$1).join(' ');
2471 return this.addContent("[".concat(length, "] ").concat(number$1(options.phase || 0), " d"));
2472 },
2473
2474 undash() {
2475 return this.addContent('[] 0 d');
2476 },
2477
2478 moveTo(x, y) {
2479 return this.addContent("".concat(number$1(x), " ").concat(number$1(y), " m"));
2480 },
2481
2482 lineTo(x, y) {
2483 return this.addContent("".concat(number$1(x), " ").concat(number$1(y), " l"));
2484 },
2485
2486 bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
2487 return this.addContent("".concat(number$1(cp1x), " ").concat(number$1(cp1y), " ").concat(number$1(cp2x), " ").concat(number$1(cp2y), " ").concat(number$1(x), " ").concat(number$1(y), " c"));
2488 },
2489
2490 quadraticCurveTo(cpx, cpy, x, y) {
2491 return this.addContent("".concat(number$1(cpx), " ").concat(number$1(cpy), " ").concat(number$1(x), " ").concat(number$1(y), " v"));
2492 },
2493
2494 rect(x, y, w, h) {
2495 return this.addContent("".concat(number$1(x), " ").concat(number$1(y), " ").concat(number$1(w), " ").concat(number$1(h), " re"));
2496 },
2497
2498 roundedRect(x, y, w, h, r) {
2499 if (r == null) {
2500 r = 0;
2501 }
2502
2503 r = Math.min(r, 0.5 * w, 0.5 * h); // amount to inset control points from corners (see `ellipse`)
2504
2505 var c = r * (1.0 - KAPPA);
2506 this.moveTo(x + r, y);
2507 this.lineTo(x + w - r, y);
2508 this.bezierCurveTo(x + w - c, y, x + w, y + c, x + w, y + r);
2509 this.lineTo(x + w, y + h - r);
2510 this.bezierCurveTo(x + w, y + h - c, x + w - c, y + h, x + w - r, y + h);
2511 this.lineTo(x + r, y + h);
2512 this.bezierCurveTo(x + c, y + h, x, y + h - c, x, y + h - r);
2513 this.lineTo(x, y + r);
2514 this.bezierCurveTo(x, y + c, x + c, y, x + r, y);
2515 return this.closePath();
2516 },
2517
2518 ellipse(x, y, r1, r2) {
2519 // based on http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas/2173084#2173084
2520 if (r2 == null) {
2521 r2 = r1;
2522 }
2523
2524 x -= r1;
2525 y -= r2;
2526 var ox = r1 * KAPPA;
2527 var oy = r2 * KAPPA;
2528 var xe = x + r1 * 2;
2529 var ye = y + r2 * 2;
2530 var xm = x + r1;
2531 var ym = y + r2;
2532 this.moveTo(x, ym);
2533 this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
2534 this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
2535 this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
2536 this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
2537 return this.closePath();
2538 },
2539
2540 circle(x, y, radius) {
2541 return this.ellipse(x, y, radius);
2542 },
2543
2544 arc(x, y, radius, startAngle, endAngle, anticlockwise) {
2545 if (anticlockwise == null) {
2546 anticlockwise = false;
2547 }
2548
2549 var TWO_PI = 2.0 * Math.PI;
2550 var HALF_PI = 0.5 * Math.PI;
2551 var deltaAng = endAngle - startAngle;
2552
2553 if (Math.abs(deltaAng) > TWO_PI) {
2554 // draw only full circle if more than that is specified
2555 deltaAng = TWO_PI;
2556 } else if (deltaAng !== 0 && anticlockwise !== deltaAng < 0) {
2557 // necessary to flip direction of rendering
2558 var dir = anticlockwise ? -1 : 1;
2559 deltaAng = dir * TWO_PI + deltaAng;
2560 }
2561
2562 var numSegs = Math.ceil(Math.abs(deltaAng) / HALF_PI);
2563 var segAng = deltaAng / numSegs;
2564 var handleLen = segAng / HALF_PI * KAPPA * radius;
2565 var curAng = startAngle; // component distances between anchor point and control point
2566
2567 var deltaCx = -Math.sin(curAng) * handleLen;
2568 var deltaCy = Math.cos(curAng) * handleLen; // anchor point
2569
2570 var ax = x + Math.cos(curAng) * radius;
2571 var ay = y + Math.sin(curAng) * radius; // calculate and render segments
2572
2573 this.moveTo(ax, ay);
2574
2575 for (var segIdx = 0; segIdx < numSegs; segIdx++) {
2576 // starting control point
2577 var cp1x = ax + deltaCx;
2578 var cp1y = ay + deltaCy; // step angle
2579
2580 curAng += segAng; // next anchor point
2581
2582 ax = x + Math.cos(curAng) * radius;
2583 ay = y + Math.sin(curAng) * radius; // next control point delta
2584
2585 deltaCx = -Math.sin(curAng) * handleLen;
2586 deltaCy = Math.cos(curAng) * handleLen; // ending control point
2587
2588 var cp2x = ax - deltaCx;
2589 var cp2y = ay - deltaCy; // render segment
2590
2591 this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, ax, ay);
2592 }
2593
2594 return this;
2595 },
2596
2597 polygon() {
2598 for (var _len = arguments.length, points = new Array(_len), _key = 0; _key < _len; _key++) {
2599 points[_key] = arguments[_key];
2600 }
2601
2602 this.moveTo(...(points.shift() || []));
2603
2604 for (var point of points) {
2605 this.lineTo(...(point || []));
2606 }
2607
2608 return this.closePath();
2609 },
2610
2611 path(path) {
2612 SVGPath.apply(this, path);
2613 return this;
2614 },
2615
2616 _windingRule(rule) {
2617 if (/even-?odd/.test(rule)) {
2618 return '*';
2619 }
2620
2621 return '';
2622 },
2623
2624 fill(color, rule) {
2625 if (/(even-?odd)|(non-?zero)/.test(color)) {
2626 rule = color;
2627 color = null;
2628 }
2629
2630 if (color) {
2631 this.fillColor(color);
2632 }
2633
2634 return this.addContent("f".concat(this._windingRule(rule)));
2635 },
2636
2637 stroke(color) {
2638 if (color) {
2639 this.strokeColor(color);
2640 }
2641
2642 return this.addContent('S');
2643 },
2644
2645 fillAndStroke(fillColor, strokeColor, rule) {
2646 if (strokeColor == null) {
2647 strokeColor = fillColor;
2648 }
2649
2650 var isFillRule = /(even-?odd)|(non-?zero)/;
2651
2652 if (isFillRule.test(fillColor)) {
2653 rule = fillColor;
2654 fillColor = null;
2655 }
2656
2657 if (isFillRule.test(strokeColor)) {
2658 rule = strokeColor;
2659 strokeColor = fillColor;
2660 }
2661
2662 if (fillColor) {
2663 this.fillColor(fillColor);
2664 this.strokeColor(strokeColor);
2665 }
2666
2667 return this.addContent("B".concat(this._windingRule(rule)));
2668 },
2669
2670 clip(rule) {
2671 return this.addContent("W".concat(this._windingRule(rule), " n"));
2672 },
2673
2674 transform(m11, m12, m21, m22, dx, dy) {
2675 // keep track of the current transformation matrix
2676 if (m11 === 1 && m12 === 0 && m21 === 0 && m22 === 1 && dx === 0 && dy === 0) {
2677 // Ignore identity transforms
2678 return this;
2679 }
2680
2681 var m = this._ctm;
2682 var [m0, m1, m2, m3, m4, m5] = m;
2683 m[0] = m0 * m11 + m2 * m12;
2684 m[1] = m1 * m11 + m3 * m12;
2685 m[2] = m0 * m21 + m2 * m22;
2686 m[3] = m1 * m21 + m3 * m22;
2687 m[4] = m0 * dx + m2 * dy + m4;
2688 m[5] = m1 * dx + m3 * dy + m5;
2689 var values = [m11, m12, m21, m22, dx, dy].map(v => number$1(v)).join(' ');
2690 return this.addContent("".concat(values, " cm"));
2691 },
2692
2693 translate(x, y) {
2694 return this.transform(1, 0, 0, 1, x, y);
2695 },
2696
2697 rotate(angle) {
2698 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2699 var y;
2700 var rad = angle * Math.PI / 180;
2701 var cos = Math.cos(rad);
2702 var sin = Math.sin(rad);
2703 var x = y = 0;
2704
2705 if (options.origin != null) {
2706 [x, y] = options.origin;
2707 var x1 = x * cos - y * sin;
2708 var y1 = x * sin + y * cos;
2709 x -= x1;
2710 y -= y1;
2711 }
2712
2713 return this.transform(cos, sin, -sin, cos, x, y);
2714 },
2715
2716 scale(xFactor, yFactor) {
2717 var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
2718 var y;
2719
2720 if (yFactor == null) {
2721 yFactor = xFactor;
2722 }
2723
2724 if (typeof yFactor === 'object') {
2725 options = yFactor;
2726 yFactor = xFactor;
2727 }
2728
2729 var x = y = 0;
2730
2731 if (options.origin != null) {
2732 [x, y] = options.origin;
2733 x -= xFactor * x;
2734 y -= yFactor * y;
2735 }
2736
2737 return this.transform(xFactor, 0, 0, yFactor, x, y);
2738 }
2739
2740};
2741
2742var WIN_ANSI_MAP = {
2743 402: 131,
2744 8211: 150,
2745 8212: 151,
2746 8216: 145,
2747 8217: 146,
2748 8218: 130,
2749 8220: 147,
2750 8221: 148,
2751 8222: 132,
2752 8224: 134,
2753 8225: 135,
2754 8226: 149,
2755 8230: 133,
2756 8364: 128,
2757 8240: 137,
2758 8249: 139,
2759 8250: 155,
2760 710: 136,
2761 8482: 153,
2762 338: 140,
2763 339: 156,
2764 732: 152,
2765 352: 138,
2766 353: 154,
2767 376: 159,
2768 381: 142,
2769 382: 158
2770};
2771var characters = ".notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n \nspace exclam quotedbl numbersign\ndollar percent ampersand quotesingle\nparenleft parenright asterisk plus\ncomma hyphen period slash\nzero one two three\nfour five six seven\neight nine colon semicolon\nless equal greater question\n \nat A B C\nD E F G\nH I J K\nL M N O\nP Q R S\nT U V W\nX Y Z bracketleft\nbackslash bracketright asciicircum underscore\n \ngrave a b c\nd e f g\nh i j k\nl m n o\np q r s\nt u v w\nx y z braceleft\nbar braceright asciitilde .notdef\n \nEuro .notdef quotesinglbase florin\nquotedblbase ellipsis dagger daggerdbl\ncircumflex perthousand Scaron guilsinglleft\nOE .notdef Zcaron .notdef\n.notdef quoteleft quoteright quotedblleft\nquotedblright bullet endash emdash\ntilde trademark scaron guilsinglright\noe .notdef zcaron ydieresis\n \nspace exclamdown cent sterling\ncurrency yen brokenbar section\ndieresis copyright ordfeminine guillemotleft\nlogicalnot hyphen registered macron\ndegree plusminus twosuperior threesuperior\nacute mu paragraph periodcentered\ncedilla onesuperior ordmasculine guillemotright\nonequarter onehalf threequarters questiondown\n \nAgrave Aacute Acircumflex Atilde\nAdieresis Aring AE Ccedilla\nEgrave Eacute Ecircumflex Edieresis\nIgrave Iacute Icircumflex Idieresis\nEth Ntilde Ograve Oacute\nOcircumflex Otilde Odieresis multiply\nOslash Ugrave Uacute Ucircumflex\nUdieresis Yacute Thorn germandbls\n \nagrave aacute acircumflex atilde\nadieresis aring ae ccedilla\negrave eacute ecircumflex edieresis\nigrave iacute icircumflex idieresis\neth ntilde ograve oacute\nocircumflex otilde odieresis divide\noslash ugrave uacute ucircumflex\nudieresis yacute thorn ydieresis".split(/\s+/);
2772
2773class AFMFont {
2774 static open(filename) {
2775 return new AFMFont(fs.readFileSync(filename, 'utf8'));
2776 }
2777
2778 constructor(contents) {
2779 this.contents = contents;
2780 this.attributes = {};
2781 this.glyphWidths = {};
2782 this.boundingBoxes = {};
2783 this.kernPairs = {};
2784 this.parse(); // todo: remove charWidths since appears to not be used
2785
2786 this.charWidths = new Array(256);
2787
2788 for (var char = 0; char <= 255; char++) {
2789 this.charWidths[char] = this.glyphWidths[characters[char]];
2790 }
2791
2792 this.bbox = this.attributes['FontBBox'].split(/\s+/).map(e => +e);
2793 this.ascender = +(this.attributes['Ascender'] || 0);
2794 this.descender = +(this.attributes['Descender'] || 0);
2795 this.xHeight = +(this.attributes['XHeight'] || 0);
2796 this.capHeight = +(this.attributes['CapHeight'] || 0);
2797 this.lineGap = this.bbox[3] - this.bbox[1] - (this.ascender - this.descender);
2798 }
2799
2800 parse() {
2801 var section = '';
2802
2803 for (var line of this.contents.split('\n')) {
2804 var match;
2805 var a;
2806
2807 if (match = line.match(/^Start(\w+)/)) {
2808 section = match[1];
2809 continue;
2810 } else if (match = line.match(/^End(\w+)/)) {
2811 section = '';
2812 continue;
2813 }
2814
2815 switch (section) {
2816 case 'FontMetrics':
2817 match = line.match(/(^\w+)\s+(.*)/);
2818 var key = match[1];
2819 var value = match[2];
2820
2821 if (a = this.attributes[key]) {
2822 if (!Array.isArray(a)) {
2823 a = this.attributes[key] = [a];
2824 }
2825
2826 a.push(value);
2827 } else {
2828 this.attributes[key] = value;
2829 }
2830
2831 break;
2832
2833 case 'CharMetrics':
2834 if (!/^CH?\s/.test(line)) {
2835 continue;
2836 }
2837
2838 var name = line.match(/\bN\s+(\.?\w+)\s*;/)[1];
2839 this.glyphWidths[name] = +line.match(/\bWX\s+(\d+)\s*;/)[1];
2840 break;
2841
2842 case 'KernPairs':
2843 match = line.match(/^KPX\s+(\.?\w+)\s+(\.?\w+)\s+(-?\d+)/);
2844
2845 if (match) {
2846 this.kernPairs[match[1] + '\0' + match[2]] = parseInt(match[3]);
2847 }
2848
2849 break;
2850 }
2851 }
2852 }
2853
2854 encodeText(text) {
2855 var res = [];
2856
2857 for (var i = 0, len = text.length; i < len; i++) {
2858 var char = text.charCodeAt(i);
2859 char = WIN_ANSI_MAP[char] || char;
2860 res.push(char.toString(16));
2861 }
2862
2863 return res;
2864 }
2865
2866 glyphsForString(string) {
2867 var glyphs = [];
2868
2869 for (var i = 0, len = string.length; i < len; i++) {
2870 var charCode = string.charCodeAt(i);
2871 glyphs.push(this.characterToGlyph(charCode));
2872 }
2873
2874 return glyphs;
2875 }
2876
2877 characterToGlyph(character) {
2878 return characters[WIN_ANSI_MAP[character] || character] || '.notdef';
2879 }
2880
2881 widthOfGlyph(glyph) {
2882 return this.glyphWidths[glyph] || 0;
2883 }
2884
2885 getKernPair(left, right) {
2886 return this.kernPairs[left + '\0' + right] || 0;
2887 }
2888
2889 advancesForGlyphs(glyphs) {
2890 var advances = [];
2891
2892 for (var index = 0; index < glyphs.length; index++) {
2893 var left = glyphs[index];
2894 var right = glyphs[index + 1];
2895 advances.push(this.widthOfGlyph(left) + this.getKernPair(left, right));
2896 }
2897
2898 return advances;
2899 }
2900
2901}
2902
2903class PDFFont {
2904 constructor() {}
2905
2906 encode() {
2907 throw new Error('Must be implemented by subclasses');
2908 }
2909
2910 widthOfString() {
2911 throw new Error('Must be implemented by subclasses');
2912 }
2913
2914 ref() {
2915 return this.dictionary != null ? this.dictionary : this.dictionary = this.document.ref();
2916 }
2917
2918 finalize() {
2919 if (this.embedded || this.dictionary == null) {
2920 return;
2921 }
2922
2923 this.embed();
2924 return this.embedded = true;
2925 }
2926
2927 embed() {
2928 throw new Error('Must be implemented by subclasses');
2929 }
2930
2931 lineHeight(size, includeGap) {
2932 if (includeGap == null) {
2933 includeGap = false;
2934 }
2935
2936 var gap = includeGap ? this.lineGap : 0;
2937 return (this.ascender + gap - this.descender) / 1000 * size;
2938 }
2939
2940}
2941
2942var STANDARD_FONTS = {
2943 Courier() {
2944 return fs.readFileSync(__dirname + '/data/Courier.afm', 'utf8');
2945 },
2946
2947 'Courier-Bold'() {
2948 return fs.readFileSync(__dirname + '/data/Courier-Bold.afm', 'utf8');
2949 },
2950
2951 'Courier-Oblique'() {
2952 return fs.readFileSync(__dirname + '/data/Courier-Oblique.afm', 'utf8');
2953 },
2954
2955 'Courier-BoldOblique'() {
2956 return fs.readFileSync(__dirname + '/data/Courier-BoldOblique.afm', 'utf8');
2957 },
2958
2959 Helvetica() {
2960 return fs.readFileSync(__dirname + '/data/Helvetica.afm', 'utf8');
2961 },
2962
2963 'Helvetica-Bold'() {
2964 return fs.readFileSync(__dirname + '/data/Helvetica-Bold.afm', 'utf8');
2965 },
2966
2967 'Helvetica-Oblique'() {
2968 return fs.readFileSync(__dirname + '/data/Helvetica-Oblique.afm', 'utf8');
2969 },
2970
2971 'Helvetica-BoldOblique'() {
2972 return fs.readFileSync(__dirname + '/data/Helvetica-BoldOblique.afm', 'utf8');
2973 },
2974
2975 'Times-Roman'() {
2976 return fs.readFileSync(__dirname + '/data/Times-Roman.afm', 'utf8');
2977 },
2978
2979 'Times-Bold'() {
2980 return fs.readFileSync(__dirname + '/data/Times-Bold.afm', 'utf8');
2981 },
2982
2983 'Times-Italic'() {
2984 return fs.readFileSync(__dirname + '/data/Times-Italic.afm', 'utf8');
2985 },
2986
2987 'Times-BoldItalic'() {
2988 return fs.readFileSync(__dirname + '/data/Times-BoldItalic.afm', 'utf8');
2989 },
2990
2991 Symbol() {
2992 return fs.readFileSync(__dirname + '/data/Symbol.afm', 'utf8');
2993 },
2994
2995 ZapfDingbats() {
2996 return fs.readFileSync(__dirname + '/data/ZapfDingbats.afm', 'utf8');
2997 }
2998
2999};
3000
3001class StandardFont extends PDFFont {
3002 constructor(document, name, id) {
3003 super();
3004 this.document = document;
3005 this.name = name;
3006 this.id = id;
3007 this.font = new AFMFont(STANDARD_FONTS[this.name]());
3008 ({
3009 ascender: this.ascender,
3010 descender: this.descender,
3011 bbox: this.bbox,
3012 lineGap: this.lineGap,
3013 xHeight: this.xHeight,
3014 capHeight: this.capHeight
3015 } = this.font);
3016 }
3017
3018 embed() {
3019 this.dictionary.data = {
3020 Type: 'Font',
3021 BaseFont: this.name,
3022 Subtype: 'Type1',
3023 Encoding: 'WinAnsiEncoding'
3024 };
3025 return this.dictionary.end();
3026 }
3027
3028 encode(text) {
3029 var encoded = this.font.encodeText(text);
3030 var glyphs = this.font.glyphsForString("".concat(text));
3031 var advances = this.font.advancesForGlyphs(glyphs);
3032 var positions = [];
3033
3034 for (var i = 0; i < glyphs.length; i++) {
3035 var glyph = glyphs[i];
3036 positions.push({
3037 xAdvance: advances[i],
3038 yAdvance: 0,
3039 xOffset: 0,
3040 yOffset: 0,
3041 advanceWidth: this.font.widthOfGlyph(glyph)
3042 });
3043 }
3044
3045 return [encoded, positions];
3046 }
3047
3048 widthOfString(string, size) {
3049 var glyphs = this.font.glyphsForString("".concat(string));
3050 var advances = this.font.advancesForGlyphs(glyphs);
3051 var width = 0;
3052
3053 for (var advance of advances) {
3054 width += advance;
3055 }
3056
3057 var scale = size / 1000;
3058 return width * scale;
3059 }
3060
3061 static isStandardFont(name) {
3062 return name in STANDARD_FONTS;
3063 }
3064
3065}
3066
3067var toHex = function toHex(num) {
3068 return "0000".concat(num.toString(16)).slice(-4);
3069};
3070
3071class EmbeddedFont extends PDFFont {
3072 constructor(document, font, id) {
3073 super();
3074 this.document = document;
3075 this.font = font;
3076 this.id = id;
3077 this.subset = this.font.createSubset();
3078 this.unicode = [[0]];
3079 this.widths = [this.font.getGlyph(0).advanceWidth];
3080 this.name = this.font.postscriptName;
3081 this.scale = 1000 / this.font.unitsPerEm;
3082 this.ascender = this.font.ascent * this.scale;
3083 this.descender = this.font.descent * this.scale;
3084 this.xHeight = this.font.xHeight * this.scale;
3085 this.capHeight = this.font.capHeight * this.scale;
3086 this.lineGap = this.font.lineGap * this.scale;
3087 this.bbox = this.font.bbox;
3088
3089 if (document.options.fontLayoutCache !== false) {
3090 this.layoutCache = Object.create(null);
3091 }
3092 }
3093
3094 layoutRun(text, features) {
3095 var run = this.font.layout(text, features); // Normalize position values
3096
3097 for (var i = 0; i < run.positions.length; i++) {
3098 var position = run.positions[i];
3099
3100 for (var key in position) {
3101 position[key] *= this.scale;
3102 }
3103
3104 position.advanceWidth = run.glyphs[i].advanceWidth * this.scale;
3105 }
3106
3107 return run;
3108 }
3109
3110 layoutCached(text) {
3111 if (!this.layoutCache) {
3112 return this.layoutRun(text);
3113 }
3114
3115 var cached;
3116
3117 if (cached = this.layoutCache[text]) {
3118 return cached;
3119 }
3120
3121 var run = this.layoutRun(text);
3122 this.layoutCache[text] = run;
3123 return run;
3124 }
3125
3126 layout(text, features, onlyWidth) {
3127 // Skip the cache if any user defined features are applied
3128 if (features) {
3129 return this.layoutRun(text, features);
3130 }
3131
3132 var glyphs = onlyWidth ? null : [];
3133 var positions = onlyWidth ? null : [];
3134 var advanceWidth = 0; // Split the string by words to increase cache efficiency.
3135 // For this purpose, spaces and tabs are a good enough delimeter.
3136
3137 var last = 0;
3138 var index = 0;
3139
3140 while (index <= text.length) {
3141 var needle;
3142
3143 if (index === text.length && last < index || (needle = text.charAt(index), [' ', '\t'].includes(needle))) {
3144 var run = this.layoutCached(text.slice(last, ++index));
3145
3146 if (!onlyWidth) {
3147 glyphs = glyphs.concat(run.glyphs);
3148 positions = positions.concat(run.positions);
3149 }
3150
3151 advanceWidth += run.advanceWidth;
3152 last = index;
3153 } else {
3154 index++;
3155 }
3156 }
3157
3158 return {
3159 glyphs,
3160 positions,
3161 advanceWidth
3162 };
3163 }
3164
3165 encode(text, features) {
3166 var {
3167 glyphs,
3168 positions
3169 } = this.layout(text, features);
3170 var res = [];
3171
3172 for (var i = 0; i < glyphs.length; i++) {
3173 var glyph = glyphs[i];
3174 var gid = this.subset.includeGlyph(glyph.id);
3175 res.push("0000".concat(gid.toString(16)).slice(-4));
3176
3177 if (this.widths[gid] == null) {
3178 this.widths[gid] = glyph.advanceWidth * this.scale;
3179 }
3180
3181 if (this.unicode[gid] == null) {
3182 this.unicode[gid] = glyph.codePoints;
3183 }
3184 }
3185
3186 return [res, positions];
3187 }
3188
3189 widthOfString(string, size, features) {
3190 var width = this.layout(string, features, true).advanceWidth;
3191 var scale = size / 1000;
3192 return width * scale;
3193 }
3194
3195 embed() {
3196 var isCFF = this.subset.cff != null;
3197 var fontFile = this.document.ref();
3198
3199 if (isCFF) {
3200 fontFile.data.Subtype = 'CIDFontType0C';
3201 }
3202
3203 this.subset.encodeStream().on('data', data => fontFile.write(data)).on('end', () => fontFile.end());
3204 var familyClass = ((this.font['OS/2'] != null ? this.font['OS/2'].sFamilyClass : undefined) || 0) >> 8;
3205 var flags = 0;
3206
3207 if (this.font.post.isFixedPitch) {
3208 flags |= 1 << 0;
3209 }
3210
3211 if (1 <= familyClass && familyClass <= 7) {
3212 flags |= 1 << 1;
3213 }
3214
3215 flags |= 1 << 2; // assume the font uses non-latin characters
3216
3217 if (familyClass === 10) {
3218 flags |= 1 << 3;
3219 }
3220
3221 if (this.font.head.macStyle.italic) {
3222 flags |= 1 << 6;
3223 } // generate a tag (6 uppercase letters. 17 is the char code offset from '0' to 'A'. 73 will map to 'Z')
3224
3225
3226 var tag = [1, 2, 3, 4, 5, 6].map(i => String.fromCharCode((this.id.charCodeAt(i) || 73) + 17)).join('');
3227 var name = tag + '+' + this.font.postscriptName;
3228 var {
3229 bbox
3230 } = this.font;
3231 var descriptor = this.document.ref({
3232 Type: 'FontDescriptor',
3233 FontName: name,
3234 Flags: flags,
3235 FontBBox: [bbox.minX * this.scale, bbox.minY * this.scale, bbox.maxX * this.scale, bbox.maxY * this.scale],
3236 ItalicAngle: this.font.italicAngle,
3237 Ascent: this.ascender,
3238 Descent: this.descender,
3239 CapHeight: (this.font.capHeight || this.font.ascent) * this.scale,
3240 XHeight: (this.font.xHeight || 0) * this.scale,
3241 StemV: 0
3242 }); // not sure how to calculate this
3243
3244 if (isCFF) {
3245 descriptor.data.FontFile3 = fontFile;
3246 } else {
3247 descriptor.data.FontFile2 = fontFile;
3248 }
3249
3250 if (this.document.subset) {
3251 var CIDSet = Buffer.from('FFFFFFFFC0', 'hex');
3252 var CIDSetRef = this.document.ref();
3253 CIDSetRef.write(CIDSet);
3254 CIDSetRef.end();
3255 descriptor.data.CIDSet = CIDSetRef;
3256 }
3257
3258 descriptor.end();
3259 var descendantFontData = {
3260 Type: 'Font',
3261 Subtype: 'CIDFontType0',
3262 BaseFont: name,
3263 CIDSystemInfo: {
3264 Registry: new String('Adobe'),
3265 Ordering: new String('Identity'),
3266 Supplement: 0
3267 },
3268 FontDescriptor: descriptor,
3269 W: [0, this.widths]
3270 };
3271
3272 if (!isCFF) {
3273 descendantFontData.Subtype = 'CIDFontType2';
3274 descendantFontData.CIDToGIDMap = 'Identity';
3275 }
3276
3277 var descendantFont = this.document.ref(descendantFontData);
3278 descendantFont.end();
3279 this.dictionary.data = {
3280 Type: 'Font',
3281 Subtype: 'Type0',
3282 BaseFont: name,
3283 Encoding: 'Identity-H',
3284 DescendantFonts: [descendantFont],
3285 ToUnicode: this.toUnicodeCmap()
3286 };
3287 return this.dictionary.end();
3288 } // Maps the glyph ids encoded in the PDF back to unicode strings
3289 // Because of ligature substitutions and the like, there may be one or more
3290 // unicode characters represented by each glyph.
3291
3292
3293 toUnicodeCmap() {
3294 var cmap = this.document.ref();
3295 var entries = [];
3296
3297 for (var codePoints of this.unicode) {
3298 var encoded = []; // encode codePoints to utf16
3299
3300 for (var value of codePoints) {
3301 if (value > 0xffff) {
3302 value -= 0x10000;
3303 encoded.push(toHex(value >>> 10 & 0x3ff | 0xd800));
3304 value = 0xdc00 | value & 0x3ff;
3305 }
3306
3307 encoded.push(toHex(value));
3308 }
3309
3310 entries.push("<".concat(encoded.join(' '), ">"));
3311 }
3312
3313 var chunkSize = 256;
3314 var chunks = Math.ceil(entries.length / chunkSize);
3315 var ranges = [];
3316
3317 for (var i = 0; i < chunks; i++) {
3318 var start = i * chunkSize;
3319 var end = Math.min((i + 1) * chunkSize, entries.length);
3320 ranges.push("<".concat(toHex(start), "> <").concat(toHex(end - 1), "> [").concat(entries.slice(start, end).join(' '), "]"));
3321 }
3322
3323 cmap.end("/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo <<\n /Registry (Adobe)\n /Ordering (UCS)\n /Supplement 0\n>> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<0000><ffff>\nendcodespacerange\n1 beginbfrange\n".concat(ranges.join('\n'), "\nendbfrange\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend"));
3324 return cmap;
3325 }
3326
3327}
3328
3329class PDFFontFactory {
3330 static open(document, src, family, id) {
3331 var font;
3332
3333 if (typeof src === 'string') {
3334 if (StandardFont.isStandardFont(src)) {
3335 return new StandardFont(document, src, id);
3336 }
3337
3338 src = fs.readFileSync(src);
3339 }
3340
3341 if (Buffer.isBuffer(src)) {
3342 font = fontkit.create(src, family);
3343 } else if (src instanceof Uint8Array) {
3344 font = fontkit.create(Buffer.from(src), family);
3345 } else if (src instanceof ArrayBuffer) {
3346 font = fontkit.create(Buffer.from(new Uint8Array(src)), family);
3347 }
3348
3349 if (font == null) {
3350 throw new Error('Not a supported font format or standard PDF font.');
3351 }
3352
3353 return new EmbeddedFont(document, font, id);
3354 }
3355
3356}
3357
3358var FontsMixin = {
3359 initFonts() {
3360 var defaultFont = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'Helvetica';
3361 // Lookup table for embedded fonts
3362 this._fontFamilies = {};
3363 this._fontCount = 0; // Font state
3364
3365 this._fontSize = 12;
3366 this._font = null;
3367 this._registeredFonts = {}; // Set the default font
3368
3369 if (defaultFont) {
3370 this.font(defaultFont);
3371 }
3372 },
3373
3374 font(src, family, size) {
3375 var cacheKey, font;
3376
3377 if (typeof family === 'number') {
3378 size = family;
3379 family = null;
3380 } // check registered fonts if src is a string
3381
3382
3383 if (typeof src === 'string' && this._registeredFonts[src]) {
3384 cacheKey = src;
3385 ({
3386 src,
3387 family
3388 } = this._registeredFonts[src]);
3389 } else {
3390 cacheKey = family || src;
3391
3392 if (typeof cacheKey !== 'string') {
3393 cacheKey = null;
3394 }
3395 }
3396
3397 if (size != null) {
3398 this.fontSize(size);
3399 } // fast path: check if the font is already in the PDF
3400
3401
3402 if (font = this._fontFamilies[cacheKey]) {
3403 this._font = font;
3404 return this;
3405 } // load the font
3406
3407
3408 var id = "F".concat(++this._fontCount);
3409 this._font = PDFFontFactory.open(this, src, family, id); // check for existing font familes with the same name already in the PDF
3410 // useful if the font was passed as a buffer
3411
3412 if (font = this._fontFamilies[this._font.name]) {
3413 this._font = font;
3414 return this;
3415 } // save the font for reuse later
3416
3417
3418 if (cacheKey) {
3419 this._fontFamilies[cacheKey] = this._font;
3420 }
3421
3422 if (this._font.name) {
3423 this._fontFamilies[this._font.name] = this._font;
3424 }
3425
3426 return this;
3427 },
3428
3429 fontSize(_fontSize) {
3430 this._fontSize = _fontSize;
3431 return this;
3432 },
3433
3434 currentLineHeight(includeGap) {
3435 if (includeGap == null) {
3436 includeGap = false;
3437 }
3438
3439 return this._font.lineHeight(this._fontSize, includeGap);
3440 },
3441
3442 registerFont(name, src, family) {
3443 this._registeredFonts[name] = {
3444 src,
3445 family
3446 };
3447 return this;
3448 }
3449
3450};
3451
3452var SOFT_HYPHEN = '\u00AD';
3453var HYPHEN = '-';
3454
3455class LineWrapper extends EventEmitter {
3456 constructor(document, options) {
3457 super();
3458 this.document = document;
3459 this.indent = options.indent || 0;
3460 this.characterSpacing = options.characterSpacing || 0;
3461 this.wordSpacing = options.wordSpacing === 0;
3462 this.columns = options.columns || 1;
3463 this.columnGap = options.columnGap != null ? options.columnGap : 18; // 1/4 inch
3464
3465 this.lineWidth = (options.width - this.columnGap * (this.columns - 1)) / this.columns;
3466 this.spaceLeft = this.lineWidth;
3467 this.startX = this.document.x;
3468 this.startY = this.document.y;
3469 this.column = 1;
3470 this.ellipsis = options.ellipsis;
3471 this.continuedX = 0;
3472 this.features = options.features; // calculate the maximum Y position the text can appear at
3473
3474 if (options.height != null) {
3475 this.height = options.height;
3476 this.maxY = this.startY + options.height;
3477 } else {
3478 this.maxY = this.document.page.maxY();
3479 } // handle paragraph indents
3480
3481
3482 this.on('firstLine', options => {
3483 // if this is the first line of the text segment, and
3484 // we're continuing where we left off, indent that much
3485 // otherwise use the user specified indent option
3486 var indent = this.continuedX || this.indent;
3487 this.document.x += indent;
3488 this.lineWidth -= indent;
3489 return this.once('line', () => {
3490 this.document.x -= indent;
3491 this.lineWidth += indent;
3492
3493 if (options.continued && !this.continuedX) {
3494 this.continuedX = this.indent;
3495 }
3496
3497 if (!options.continued) {
3498 return this.continuedX = 0;
3499 }
3500 });
3501 }); // handle left aligning last lines of paragraphs
3502
3503 this.on('lastLine', options => {
3504 var {
3505 align
3506 } = options;
3507
3508 if (align === 'justify') {
3509 options.align = 'left';
3510 }
3511
3512 this.lastLine = true;
3513 return this.once('line', () => {
3514 this.document.y += options.paragraphGap || 0;
3515 options.align = align;
3516 return this.lastLine = false;
3517 });
3518 });
3519 }
3520
3521 wordWidth(word) {
3522 return this.document.widthOfString(word, this) + this.characterSpacing + this.wordSpacing;
3523 }
3524
3525 canFit(word, w) {
3526 if (word[word.length - 1] != SOFT_HYPHEN) {
3527 return w <= this.spaceLeft;
3528 }
3529
3530 return w + this.wordWidth(HYPHEN) <= this.spaceLeft;
3531 }
3532
3533 eachWord(text, fn) {
3534 // setup a unicode line breaker
3535 var bk;
3536 var breaker = new LineBreaker(text);
3537 var last = null;
3538 var wordWidths = Object.create(null);
3539
3540 while (bk = breaker.nextBreak()) {
3541 var shouldContinue;
3542 var word = text.slice((last != null ? last.position : undefined) || 0, bk.position);
3543 var w = wordWidths[word] != null ? wordWidths[word] : wordWidths[word] = this.wordWidth(word); // if the word is longer than the whole line, chop it up
3544 // TODO: break by grapheme clusters, not JS string characters
3545
3546 if (w > this.lineWidth + this.continuedX) {
3547 // make some fake break objects
3548 var lbk = last;
3549 var fbk = {};
3550
3551 while (word.length) {
3552 // fit as much of the word as possible into the space we have
3553 var l, mightGrow;
3554
3555 if (w > this.spaceLeft) {
3556 // start our check at the end of our available space - this method is faster than a loop of each character and it resolves
3557 // an issue with long loops when processing massive words, such as a huge number of spaces
3558 l = Math.ceil(this.spaceLeft / (w / word.length));
3559 w = this.wordWidth(word.slice(0, l));
3560 mightGrow = w <= this.spaceLeft && l < word.length;
3561 } else {
3562 l = word.length;
3563 }
3564
3565 var mustShrink = w > this.spaceLeft && l > 0; // shrink or grow word as necessary after our near-guess above
3566
3567 while (mustShrink || mightGrow) {
3568 if (mustShrink) {
3569 w = this.wordWidth(word.slice(0, --l));
3570 mustShrink = w > this.spaceLeft && l > 0;
3571 } else {
3572 w = this.wordWidth(word.slice(0, ++l));
3573 mustShrink = w > this.spaceLeft && l > 0;
3574 mightGrow = w <= this.spaceLeft && l < word.length;
3575 }
3576 } // check for the edge case where a single character cannot fit into a line.
3577
3578
3579 if (l === 0 && this.spaceLeft === this.lineWidth) {
3580 l = 1;
3581 } // send a required break unless this is the last piece and a linebreak is not specified
3582
3583
3584 fbk.required = bk.required || l < word.length;
3585 shouldContinue = fn(word.slice(0, l), w, fbk, lbk);
3586 lbk = {
3587 required: false
3588 }; // get the remaining piece of the word
3589
3590 word = word.slice(l);
3591 w = this.wordWidth(word);
3592
3593 if (shouldContinue === false) {
3594 break;
3595 }
3596 }
3597 } else {
3598 // otherwise just emit the break as it was given to us
3599 shouldContinue = fn(word, w, bk, last);
3600 }
3601
3602 if (shouldContinue === false) {
3603 break;
3604 }
3605
3606 last = bk;
3607 }
3608 }
3609
3610 wrap(text, options) {
3611 // override options from previous continued fragments
3612 if (options.indent != null) {
3613 this.indent = options.indent;
3614 }
3615
3616 if (options.characterSpacing != null) {
3617 this.characterSpacing = options.characterSpacing;
3618 }
3619
3620 if (options.wordSpacing != null) {
3621 this.wordSpacing = options.wordSpacing;
3622 }
3623
3624 if (options.ellipsis != null) {
3625 this.ellipsis = options.ellipsis;
3626 } // make sure we're actually on the page
3627 // and that the first line of is never by
3628 // itself at the bottom of a page (orphans)
3629
3630
3631 var nextY = this.document.y + this.document.currentLineHeight(true);
3632
3633 if (this.document.y > this.maxY || nextY > this.maxY) {
3634 this.nextSection();
3635 }
3636
3637 var buffer = '';
3638 var textWidth = 0;
3639 var wc = 0;
3640 var lc = 0;
3641 var {
3642 y
3643 } = this.document; // used to reset Y pos if options.continued (below)
3644
3645 var emitLine = () => {
3646 options.textWidth = textWidth + this.wordSpacing * (wc - 1);
3647 options.wordCount = wc;
3648 options.lineWidth = this.lineWidth;
3649 ({
3650 y
3651 } = this.document);
3652 this.emit('line', buffer, options, this);
3653 return lc++;
3654 };
3655
3656 this.emit('sectionStart', options, this);
3657 this.eachWord(text, (word, w, bk, last) => {
3658 if (last == null || last.required) {
3659 this.emit('firstLine', options, this);
3660 this.spaceLeft = this.lineWidth;
3661 }
3662
3663 if (this.canFit(word, w)) {
3664 buffer += word;
3665 textWidth += w;
3666 wc++;
3667 }
3668
3669 if (bk.required || !this.canFit(word, w)) {
3670 // if the user specified a max height and an ellipsis, and is about to pass the
3671 // max height and max columns after the next line, append the ellipsis
3672 var lh = this.document.currentLineHeight(true);
3673
3674 if (this.height != null && this.ellipsis && this.document.y + lh * 2 > this.maxY && this.column >= this.columns) {
3675 if (this.ellipsis === true) {
3676 this.ellipsis = '…';
3677 } // map default ellipsis character
3678
3679
3680 buffer = buffer.replace(/\s+$/, '');
3681 textWidth = this.wordWidth(buffer + this.ellipsis); // remove characters from the buffer until the ellipsis fits
3682 // to avoid infinite loop need to stop while-loop if buffer is empty string
3683
3684 while (buffer && textWidth > this.lineWidth) {
3685 buffer = buffer.slice(0, -1).replace(/\s+$/, '');
3686 textWidth = this.wordWidth(buffer + this.ellipsis);
3687 } // need to add ellipsis only if there is enough space for it
3688
3689
3690 if (textWidth <= this.lineWidth) {
3691 buffer = buffer + this.ellipsis;
3692 }
3693
3694 textWidth = this.wordWidth(buffer);
3695 }
3696
3697 if (bk.required) {
3698 if (w > this.spaceLeft) {
3699 emitLine();
3700 buffer = word;
3701 textWidth = w;
3702 wc = 1;
3703 }
3704
3705 this.emit('lastLine', options, this);
3706 } // Previous entry is a soft hyphen - add visible hyphen.
3707
3708
3709 if (buffer[buffer.length - 1] == SOFT_HYPHEN) {
3710 buffer = buffer.slice(0, -1) + HYPHEN;
3711 this.spaceLeft -= this.wordWidth(HYPHEN);
3712 }
3713
3714 emitLine(); // if we've reached the edge of the page,
3715 // continue on a new page or column
3716
3717 if (this.document.y + lh > this.maxY) {
3718 var shouldContinue = this.nextSection(); // stop if we reached the maximum height
3719
3720 if (!shouldContinue) {
3721 wc = 0;
3722 buffer = '';
3723 return false;
3724 }
3725 } // reset the space left and buffer
3726
3727
3728 if (bk.required) {
3729 this.spaceLeft = this.lineWidth;
3730 buffer = '';
3731 textWidth = 0;
3732 return wc = 0;
3733 } else {
3734 // reset the space left and buffer
3735 this.spaceLeft = this.lineWidth - w;
3736 buffer = word;
3737 textWidth = w;
3738 return wc = 1;
3739 }
3740 } else {
3741 return this.spaceLeft -= w;
3742 }
3743 });
3744
3745 if (wc > 0) {
3746 this.emit('lastLine', options, this);
3747 emitLine();
3748 }
3749
3750 this.emit('sectionEnd', options, this); // if the wrap is set to be continued, save the X position
3751 // to start the first line of the next segment at, and reset
3752 // the y position
3753
3754 if (options.continued === true) {
3755 if (lc > 1) {
3756 this.continuedX = 0;
3757 }
3758
3759 this.continuedX += options.textWidth || 0;
3760 return this.document.y = y;
3761 } else {
3762 return this.document.x = this.startX;
3763 }
3764 }
3765
3766 nextSection(options) {
3767 this.emit('sectionEnd', options, this);
3768
3769 if (++this.column > this.columns) {
3770 // if a max height was specified by the user, we're done.
3771 // otherwise, the default is to make a new page at the bottom.
3772 if (this.height != null) {
3773 return false;
3774 }
3775
3776 this.document.continueOnNewPage();
3777 this.column = 1;
3778 this.startY = this.document.page.margins.top;
3779 this.maxY = this.document.page.maxY();
3780 this.document.x = this.startX;
3781
3782 if (this.document._fillColor) {
3783 this.document.fillColor(...this.document._fillColor);
3784 }
3785
3786 this.emit('pageBreak', options, this);
3787 } else {
3788 this.document.x += this.lineWidth + this.columnGap;
3789 this.document.y = this.startY;
3790 this.emit('columnBreak', options, this);
3791 }
3792
3793 this.emit('sectionStart', options, this);
3794 return true;
3795 }
3796
3797}
3798
3799var {
3800 number: number$2
3801} = PDFObject;
3802var TextMixin = {
3803 initText() {
3804 this._line = this._line.bind(this); // Current coordinates
3805
3806 this.x = 0;
3807 this.y = 0;
3808 return this._lineGap = 0;
3809 },
3810
3811 lineGap(_lineGap) {
3812 this._lineGap = _lineGap;
3813 return this;
3814 },
3815
3816 moveDown(lines) {
3817 if (lines == null) {
3818 lines = 1;
3819 }
3820
3821 this.y += this.currentLineHeight(true) * lines + this._lineGap;
3822 return this;
3823 },
3824
3825 moveUp(lines) {
3826 if (lines == null) {
3827 lines = 1;
3828 }
3829
3830 this.y -= this.currentLineHeight(true) * lines + this._lineGap;
3831 return this;
3832 },
3833
3834 _text(text, x, y, options, lineCallback) {
3835 options = this._initOptions(x, y, options); // Convert text to a string
3836
3837 text = text == null ? '' : "".concat(text); // if the wordSpacing option is specified, remove multiple consecutive spaces
3838
3839 if (options.wordSpacing) {
3840 text = text.replace(/\s{2,}/g, ' ');
3841 }
3842
3843 var addStructure = () => {
3844 if (options.structParent) {
3845 options.structParent.add(this.struct(options.structType || 'P', [this.markStructureContent(options.structType || 'P')]));
3846 }
3847 }; // word wrapping
3848
3849
3850 if (options.width) {
3851 var wrapper = this._wrapper;
3852
3853 if (!wrapper) {
3854 wrapper = new LineWrapper(this, options);
3855 wrapper.on('line', lineCallback);
3856 wrapper.on('firstLine', addStructure);
3857 }
3858
3859 this._wrapper = options.continued ? wrapper : null;
3860 this._textOptions = options.continued ? options : null;
3861 wrapper.wrap(text, options); // render paragraphs as single lines
3862 } else {
3863 for (var line of text.split('\n')) {
3864 addStructure();
3865 lineCallback(line, options);
3866 }
3867 }
3868
3869 return this;
3870 },
3871
3872 text(text, x, y, options) {
3873 return this._text(text, x, y, options, this._line);
3874 },
3875
3876 widthOfString(string) {
3877 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
3878 return this._font.widthOfString(string, this._fontSize, options.features) + (options.characterSpacing || 0) * (string.length - 1);
3879 },
3880
3881 heightOfString(text, options) {
3882 var {
3883 x,
3884 y
3885 } = this;
3886 options = this._initOptions(options);
3887 options.height = Infinity; // don't break pages
3888
3889 var lineGap = options.lineGap || this._lineGap || 0;
3890
3891 this._text(text, this.x, this.y, options, () => {
3892 return this.y += this.currentLineHeight(true) + lineGap;
3893 });
3894
3895 var height = this.y - y;
3896 this.x = x;
3897 this.y = y;
3898 return height;
3899 },
3900
3901 list(list, x, y, options, wrapper) {
3902 options = this._initOptions(x, y, options);
3903 var listType = options.listType || 'bullet';
3904 var unit = Math.round(this._font.ascender / 1000 * this._fontSize);
3905 var midLine = unit / 2;
3906 var r = options.bulletRadius || unit / 3;
3907 var indent = options.textIndent || (listType === 'bullet' ? r * 5 : unit * 2);
3908 var itemIndent = options.bulletIndent || (listType === 'bullet' ? r * 8 : unit * 2);
3909 var level = 1;
3910 var items = [];
3911 var levels = [];
3912 var numbers = [];
3913
3914 var flatten = function flatten(list) {
3915 var n = 1;
3916
3917 for (var i = 0; i < list.length; i++) {
3918 var item = list[i];
3919
3920 if (Array.isArray(item)) {
3921 level++;
3922 flatten(item);
3923 level--;
3924 } else {
3925 items.push(item);
3926 levels.push(level);
3927
3928 if (listType !== 'bullet') {
3929 numbers.push(n++);
3930 }
3931 }
3932 }
3933 };
3934
3935 flatten(list);
3936
3937 var label = function label(n) {
3938 switch (listType) {
3939 case 'numbered':
3940 return "".concat(n, ".");
3941
3942 case 'lettered':
3943 var letter = String.fromCharCode((n - 1) % 26 + 65);
3944 var times = Math.floor((n - 1) / 26 + 1);
3945 var text = Array(times + 1).join(letter);
3946 return "".concat(text, ".");
3947 }
3948 };
3949
3950 var drawListItem = function drawListItem(listItem) {
3951 wrapper = new LineWrapper(this, options);
3952 wrapper.on('line', this._line);
3953 level = 1;
3954 var i = 0;
3955 wrapper.once('firstLine', () => {
3956 var item, itemType, labelType, bodyType;
3957
3958 if (options.structParent) {
3959 if (options.structTypes) {
3960 [itemType, labelType, bodyType] = options.structTypes;
3961 } else {
3962 [itemType, labelType, bodyType] = ['LI', 'Lbl', 'LBody'];
3963 }
3964 }
3965
3966 if (itemType) {
3967 item = this.struct(itemType);
3968 options.structParent.add(item);
3969 } else if (options.structParent) {
3970 item = options.structParent;
3971 }
3972
3973 var l;
3974
3975 if ((l = levels[i++]) !== level) {
3976 var diff = itemIndent * (l - level);
3977 this.x += diff;
3978 wrapper.lineWidth -= diff;
3979 level = l;
3980 }
3981
3982 if (item && (labelType || bodyType)) {
3983 item.add(this.struct(labelType || bodyType, [this.markStructureContent(labelType || bodyType)]));
3984 }
3985
3986 switch (listType) {
3987 case 'bullet':
3988 this.circle(this.x - indent + r, this.y + midLine, r);
3989 this.fill();
3990 break;
3991
3992 case 'numbered':
3993 case 'lettered':
3994 var text = label(numbers[i - 1]);
3995
3996 this._fragment(text, this.x - indent, this.y, options);
3997
3998 break;
3999 }
4000
4001 if (item && labelType && bodyType) {
4002 item.add(this.struct(bodyType, [this.markStructureContent(bodyType)]));
4003 }
4004
4005 if (item && item !== options.structParent) {
4006 item.end();
4007 }
4008 });
4009 wrapper.on('sectionStart', () => {
4010 var pos = indent + itemIndent * (level - 1);
4011 this.x += pos;
4012 return wrapper.lineWidth -= pos;
4013 });
4014 wrapper.on('sectionEnd', () => {
4015 var pos = indent + itemIndent * (level - 1);
4016 this.x -= pos;
4017 return wrapper.lineWidth += pos;
4018 });
4019 wrapper.wrap(listItem, options);
4020 };
4021
4022 for (var i = 0; i < items.length; i++) {
4023 drawListItem.call(this, items[i]);
4024 }
4025
4026 return this;
4027 },
4028
4029 _initOptions() {
4030 var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
4031 var y = arguments.length > 1 ? arguments[1] : undefined;
4032 var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
4033
4034 if (typeof x === 'object') {
4035 options = x;
4036 x = null;
4037 } // clone options object
4038
4039
4040 var result = Object.assign({}, options); // extend options with previous values for continued text
4041
4042 if (this._textOptions) {
4043 for (var key in this._textOptions) {
4044 var val = this._textOptions[key];
4045
4046 if (key !== 'continued') {
4047 if (result[key] === undefined) {
4048 result[key] = val;
4049 }
4050 }
4051 }
4052 } // Update the current position
4053
4054
4055 if (x != null) {
4056 this.x = x;
4057 }
4058
4059 if (y != null) {
4060 this.y = y;
4061 } // wrap to margins if no x or y position passed
4062
4063
4064 if (result.lineBreak !== false) {
4065 if (result.width == null) {
4066 result.width = this.page.width - this.x - this.page.margins.right;
4067 }
4068
4069 result.width = Math.max(result.width, 0);
4070 }
4071
4072 if (!result.columns) {
4073 result.columns = 0;
4074 }
4075
4076 if (result.columnGap == null) {
4077 result.columnGap = 18;
4078 } // 1/4 inch
4079
4080
4081 return result;
4082 },
4083
4084 _line(text) {
4085 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
4086 var wrapper = arguments.length > 2 ? arguments[2] : undefined;
4087
4088 this._fragment(text, this.x, this.y, options);
4089
4090 var lineGap = options.lineGap || this._lineGap || 0;
4091
4092 if (!wrapper) {
4093 return this.x += this.widthOfString(text);
4094 } else {
4095 return this.y += this.currentLineHeight(true) + lineGap;
4096 }
4097 },
4098
4099 _fragment(text, x, y, options) {
4100 var dy, encoded, i, positions, textWidth, words;
4101 text = "".concat(text).replace(/\n/g, '');
4102
4103 if (text.length === 0) {
4104 return;
4105 } // handle options
4106
4107
4108 var align = options.align || 'left';
4109 var wordSpacing = options.wordSpacing || 0;
4110 var characterSpacing = options.characterSpacing || 0; // text alignments
4111
4112 if (options.width) {
4113 switch (align) {
4114 case 'right':
4115 textWidth = this.widthOfString(text.replace(/\s+$/, ''), options);
4116 x += options.lineWidth - textWidth;
4117 break;
4118
4119 case 'center':
4120 x += options.lineWidth / 2 - options.textWidth / 2;
4121 break;
4122
4123 case 'justify':
4124 // calculate the word spacing value
4125 words = text.trim().split(/\s+/);
4126 textWidth = this.widthOfString(text.replace(/\s+/g, ''), options);
4127 var spaceWidth = this.widthOfString(' ') + characterSpacing;
4128 wordSpacing = Math.max(0, (options.lineWidth - textWidth) / Math.max(1, words.length - 1) - spaceWidth);
4129 break;
4130 }
4131 } // text baseline alignments based on http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
4132
4133
4134 if (typeof options.baseline === 'number') {
4135 dy = -options.baseline;
4136 } else {
4137 switch (options.baseline) {
4138 case 'svg-middle':
4139 dy = 0.5 * this._font.xHeight;
4140 break;
4141
4142 case 'middle':
4143 case 'svg-central':
4144 dy = 0.5 * (this._font.descender + this._font.ascender);
4145 break;
4146
4147 case 'bottom':
4148 case 'ideographic':
4149 dy = this._font.descender;
4150 break;
4151
4152 case 'alphabetic':
4153 dy = 0;
4154 break;
4155
4156 case 'mathematical':
4157 dy = 0.5 * this._font.ascender;
4158 break;
4159
4160 case 'hanging':
4161 dy = 0.8 * this._font.ascender;
4162 break;
4163
4164 case 'top':
4165 dy = this._font.ascender;
4166 break;
4167
4168 default:
4169 dy = this._font.ascender;
4170 }
4171
4172 dy = dy / 1000 * this._fontSize;
4173 } // calculate the actual rendered width of the string after word and character spacing
4174
4175
4176 var renderedWidth = options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1); // create link annotations if the link option is given
4177
4178 if (options.link != null) {
4179 this.link(x, y, renderedWidth, this.currentLineHeight(), options.link);
4180 }
4181
4182 if (options.goTo != null) {
4183 this.goTo(x, y, renderedWidth, this.currentLineHeight(), options.goTo);
4184 }
4185
4186 if (options.destination != null) {
4187 this.addNamedDestination(options.destination, 'XYZ', x, y, null);
4188 } // create underline
4189
4190
4191 if (options.underline) {
4192 this.save();
4193
4194 if (!options.stroke) {
4195 this.strokeColor(...(this._fillColor || []));
4196 }
4197
4198 var lineWidth = this._fontSize < 10 ? 0.5 : Math.floor(this._fontSize / 10);
4199 this.lineWidth(lineWidth);
4200 var lineY = y + this.currentLineHeight() - lineWidth;
4201 this.moveTo(x, lineY);
4202 this.lineTo(x + renderedWidth, lineY);
4203 this.stroke();
4204 this.restore();
4205 } // create strikethrough line
4206
4207
4208 if (options.strike) {
4209 this.save();
4210
4211 if (!options.stroke) {
4212 this.strokeColor(...(this._fillColor || []));
4213 }
4214
4215 var _lineWidth = this._fontSize < 10 ? 0.5 : Math.floor(this._fontSize / 10);
4216
4217 this.lineWidth(_lineWidth);
4218
4219 var _lineY = y + this.currentLineHeight() / 2;
4220
4221 this.moveTo(x, _lineY);
4222 this.lineTo(x + renderedWidth, _lineY);
4223 this.stroke();
4224 this.restore();
4225 }
4226
4227 this.save(); // oblique (angle in degrees or boolean)
4228
4229 if (options.oblique) {
4230 var skew;
4231
4232 if (typeof options.oblique === 'number') {
4233 skew = -Math.tan(options.oblique * Math.PI / 180);
4234 } else {
4235 skew = -0.25;
4236 }
4237
4238 this.transform(1, 0, 0, 1, x, y);
4239 this.transform(1, 0, skew, 1, -skew * dy, 0);
4240 this.transform(1, 0, 0, 1, -x, -y);
4241 } // flip coordinate system
4242
4243
4244 this.transform(1, 0, 0, -1, 0, this.page.height);
4245 y = this.page.height - y - dy; // add current font to page if necessary
4246
4247 if (this.page.fonts[this._font.id] == null) {
4248 this.page.fonts[this._font.id] = this._font.ref();
4249 } // begin the text object
4250
4251
4252 this.addContent('BT'); // text position
4253
4254 this.addContent("1 0 0 1 ".concat(number$2(x), " ").concat(number$2(y), " Tm")); // font and font size
4255
4256 this.addContent("/".concat(this._font.id, " ").concat(number$2(this._fontSize), " Tf")); // rendering mode
4257
4258 var mode = options.fill && options.stroke ? 2 : options.stroke ? 1 : 0;
4259
4260 if (mode) {
4261 this.addContent("".concat(mode, " Tr"));
4262 } // Character spacing
4263
4264
4265 if (characterSpacing) {
4266 this.addContent("".concat(number$2(characterSpacing), " Tc"));
4267 } // Add the actual text
4268 // If we have a word spacing value, we need to encode each word separately
4269 // since the normal Tw operator only works on character code 32, which isn't
4270 // used for embedded fonts.
4271
4272
4273 if (wordSpacing) {
4274 words = text.trim().split(/\s+/);
4275 wordSpacing += this.widthOfString(' ') + characterSpacing;
4276 wordSpacing *= 1000 / this._fontSize;
4277 encoded = [];
4278 positions = [];
4279
4280 for (var word of words) {
4281 var [encodedWord, positionsWord] = this._font.encode(word, options.features);
4282
4283 encoded = encoded.concat(encodedWord);
4284 positions = positions.concat(positionsWord); // add the word spacing to the end of the word
4285 // clone object because of cache
4286
4287 var space = {};
4288 var object = positions[positions.length - 1];
4289
4290 for (var key in object) {
4291 var val = object[key];
4292 space[key] = val;
4293 }
4294
4295 space.xAdvance += wordSpacing;
4296 positions[positions.length - 1] = space;
4297 }
4298 } else {
4299 [encoded, positions] = this._font.encode(text, options.features);
4300 }
4301
4302 var scale = this._fontSize / 1000;
4303 var commands = [];
4304 var last = 0;
4305 var hadOffset = false; // Adds a segment of text to the TJ command buffer
4306
4307 var addSegment = cur => {
4308 if (last < cur) {
4309 var hex = encoded.slice(last, cur).join('');
4310 var advance = positions[cur - 1].xAdvance - positions[cur - 1].advanceWidth;
4311 commands.push("<".concat(hex, "> ").concat(number$2(-advance)));
4312 }
4313
4314 return last = cur;
4315 }; // Flushes the current TJ commands to the output stream
4316
4317
4318 var flush = i => {
4319 addSegment(i);
4320
4321 if (commands.length > 0) {
4322 this.addContent("[".concat(commands.join(' '), "] TJ"));
4323 return commands.length = 0;
4324 }
4325 };
4326
4327 for (i = 0; i < positions.length; i++) {
4328 // If we have an x or y offset, we have to break out of the current TJ command
4329 // so we can move the text position.
4330 var pos = positions[i];
4331
4332 if (pos.xOffset || pos.yOffset) {
4333 // Flush the current buffer
4334 flush(i); // Move the text position and flush just the current character
4335
4336 this.addContent("1 0 0 1 ".concat(number$2(x + pos.xOffset * scale), " ").concat(number$2(y + pos.yOffset * scale), " Tm"));
4337 flush(i + 1);
4338 hadOffset = true;
4339 } else {
4340 // If the last character had an offset, reset the text position
4341 if (hadOffset) {
4342 this.addContent("1 0 0 1 ".concat(number$2(x), " ").concat(number$2(y), " Tm"));
4343 hadOffset = false;
4344 } // Group segments that don't have any advance adjustments
4345
4346
4347 if (pos.xAdvance - pos.advanceWidth !== 0) {
4348 addSegment(i + 1);
4349 }
4350 }
4351
4352 x += pos.xAdvance * scale;
4353 } // Flush any remaining commands
4354
4355
4356 flush(i); // end the text object
4357
4358 this.addContent('ET'); // restore flipped coordinate system
4359
4360 return this.restore();
4361 }
4362
4363};
4364
4365var MARKERS = [0xffc0, 0xffc1, 0xffc2, 0xffc3, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9, 0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf];
4366var COLOR_SPACE_MAP = {
4367 1: 'DeviceGray',
4368 3: 'DeviceRGB',
4369 4: 'DeviceCMYK'
4370};
4371
4372class JPEG {
4373 constructor(data, label) {
4374 var marker;
4375 this.data = data;
4376 this.label = label;
4377
4378 if (this.data.readUInt16BE(0) !== 0xffd8) {
4379 throw 'SOI not found in JPEG';
4380 } // Parse the EXIF orientation
4381
4382
4383 this.orientation = exif.fromBuffer(this.data).Orientation || 1;
4384 var pos = 2;
4385
4386 while (pos < this.data.length) {
4387 marker = this.data.readUInt16BE(pos);
4388 pos += 2;
4389
4390 if (MARKERS.includes(marker)) {
4391 break;
4392 }
4393
4394 pos += this.data.readUInt16BE(pos);
4395 }
4396
4397 if (!MARKERS.includes(marker)) {
4398 throw 'Invalid JPEG.';
4399 }
4400
4401 pos += 2;
4402 this.bits = this.data[pos++];
4403 this.height = this.data.readUInt16BE(pos);
4404 pos += 2;
4405 this.width = this.data.readUInt16BE(pos);
4406 pos += 2;
4407 var channels = this.data[pos++];
4408 this.colorSpace = COLOR_SPACE_MAP[channels];
4409 this.obj = null;
4410 }
4411
4412 embed(document) {
4413 if (this.obj) {
4414 return;
4415 }
4416
4417 this.obj = document.ref({
4418 Type: 'XObject',
4419 Subtype: 'Image',
4420 BitsPerComponent: this.bits,
4421 Width: this.width,
4422 Height: this.height,
4423 ColorSpace: this.colorSpace,
4424 Filter: 'DCTDecode'
4425 }); // add extra decode params for CMYK images. By swapping the
4426 // min and max values from the default, we invert the colors. See
4427 // section 4.8.4 of the spec.
4428
4429 if (this.colorSpace === 'DeviceCMYK') {
4430 this.obj.data['Decode'] = [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0];
4431 }
4432
4433 this.obj.end(this.data); // free memory
4434
4435 return this.data = null;
4436 }
4437
4438}
4439
4440class PNGImage {
4441 constructor(data, label) {
4442 this.label = label;
4443 this.image = new PNG(data);
4444 this.width = this.image.width;
4445 this.height = this.image.height;
4446 this.imgData = this.image.imgData;
4447 this.obj = null;
4448 }
4449
4450 embed(document) {
4451 var dataDecoded = false;
4452 this.document = document;
4453
4454 if (this.obj) {
4455 return;
4456 }
4457
4458 var hasAlphaChannel = this.image.hasAlphaChannel;
4459 var isInterlaced = this.image.interlaceMethod === 1;
4460 this.obj = this.document.ref({
4461 Type: 'XObject',
4462 Subtype: 'Image',
4463 BitsPerComponent: hasAlphaChannel ? 8 : this.image.bits,
4464 Width: this.width,
4465 Height: this.height,
4466 Filter: 'FlateDecode'
4467 });
4468
4469 if (!hasAlphaChannel) {
4470 var params = this.document.ref({
4471 Predictor: isInterlaced ? 1 : 15,
4472 Colors: this.image.colors,
4473 BitsPerComponent: this.image.bits,
4474 Columns: this.width
4475 });
4476 this.obj.data['DecodeParms'] = params;
4477 params.end();
4478 }
4479
4480 if (this.image.palette.length === 0) {
4481 this.obj.data['ColorSpace'] = this.image.colorSpace;
4482 } else {
4483 // embed the color palette in the PDF as an object stream
4484 var palette = this.document.ref();
4485 palette.end(Buffer.from(this.image.palette)); // build the color space array for the image
4486
4487 this.obj.data['ColorSpace'] = ['Indexed', 'DeviceRGB', this.image.palette.length / 3 - 1, palette];
4488 } // For PNG color types 0, 2 and 3, the transparency data is stored in
4489 // a dedicated PNG chunk.
4490
4491
4492 if (this.image.transparency.grayscale != null) {
4493 // Use Color Key Masking (spec section 4.8.5)
4494 // An array with N elements, where N is two times the number of color components.
4495 var val = this.image.transparency.grayscale;
4496 this.obj.data['Mask'] = [val, val];
4497 } else if (this.image.transparency.rgb) {
4498 // Use Color Key Masking (spec section 4.8.5)
4499 // An array with N elements, where N is two times the number of color components.
4500 var {
4501 rgb
4502 } = this.image.transparency;
4503 var mask = [];
4504
4505 for (var x of rgb) {
4506 mask.push(x, x);
4507 }
4508
4509 this.obj.data['Mask'] = mask;
4510 } else if (this.image.transparency.indexed) {
4511 // Create a transparency SMask for the image based on the data
4512 // in the PLTE and tRNS sections. See below for details on SMasks.
4513 dataDecoded = true;
4514 return this.loadIndexedAlphaChannel();
4515 } else if (hasAlphaChannel) {
4516 // For PNG color types 4 and 6, the transparency data is stored as a alpha
4517 // channel mixed in with the main image data. Separate this data out into an
4518 // SMask object and store it separately in the PDF.
4519 dataDecoded = true;
4520 return this.splitAlphaChannel();
4521 }
4522
4523 if (isInterlaced && !dataDecoded) {
4524 return this.decodeData();
4525 }
4526
4527 this.finalize();
4528 }
4529
4530 finalize() {
4531 if (this.alphaChannel) {
4532 var sMask = this.document.ref({
4533 Type: 'XObject',
4534 Subtype: 'Image',
4535 Height: this.height,
4536 Width: this.width,
4537 BitsPerComponent: 8,
4538 Filter: 'FlateDecode',
4539 ColorSpace: 'DeviceGray',
4540 Decode: [0, 1]
4541 });
4542 sMask.end(this.alphaChannel);
4543 this.obj.data['SMask'] = sMask;
4544 } // add the actual image data
4545
4546
4547 this.obj.end(this.imgData); // free memory
4548
4549 this.image = null;
4550 return this.imgData = null;
4551 }
4552
4553 splitAlphaChannel() {
4554 return this.image.decodePixels(pixels => {
4555 var a, p;
4556 var colorCount = this.image.colors;
4557 var pixelCount = this.width * this.height;
4558 var imgData = Buffer.alloc(pixelCount * colorCount);
4559 var alphaChannel = Buffer.alloc(pixelCount);
4560 var i = p = a = 0;
4561 var len = pixels.length; // For 16bit images copy only most significant byte (MSB) - PNG data is always stored in network byte order (MSB first)
4562
4563 var skipByteCount = this.image.bits === 16 ? 1 : 0;
4564
4565 while (i < len) {
4566 for (var colorIndex = 0; colorIndex < colorCount; colorIndex++) {
4567 imgData[p++] = pixels[i++];
4568 i += skipByteCount;
4569 }
4570
4571 alphaChannel[a++] = pixels[i++];
4572 i += skipByteCount;
4573 }
4574
4575 this.imgData = zlib.deflateSync(imgData);
4576 this.alphaChannel = zlib.deflateSync(alphaChannel);
4577 return this.finalize();
4578 });
4579 }
4580
4581 loadIndexedAlphaChannel() {
4582 var transparency = this.image.transparency.indexed;
4583 return this.image.decodePixels(pixels => {
4584 var alphaChannel = Buffer.alloc(this.width * this.height);
4585 var i = 0;
4586
4587 for (var j = 0, end = pixels.length; j < end; j++) {
4588 alphaChannel[i++] = transparency[pixels[j]];
4589 }
4590
4591 this.alphaChannel = zlib.deflateSync(alphaChannel);
4592 return this.finalize();
4593 });
4594 }
4595
4596 decodeData() {
4597 this.image.decodePixels(pixels => {
4598 this.imgData = zlib.deflateSync(pixels);
4599 this.finalize();
4600 });
4601 }
4602
4603}
4604
4605/*
4606PDFImage - embeds images in PDF documents
4607By Devon Govett
4608*/
4609
4610class PDFImage {
4611 static open(src, label) {
4612 var data;
4613
4614 if (Buffer.isBuffer(src)) {
4615 data = src;
4616 } else if (src instanceof ArrayBuffer) {
4617 data = Buffer.from(new Uint8Array(src));
4618 } else {
4619 var match;
4620
4621 if (match = /^data:.+?;base64,(.*)$/.exec(src)) {
4622 data = Buffer.from(match[1], 'base64');
4623 } else {
4624 data = fs.readFileSync(src);
4625
4626 if (!data) {
4627 return;
4628 }
4629 }
4630 }
4631
4632 if (data[0] === 0xff && data[1] === 0xd8) {
4633 return new JPEG(data, label);
4634 } else if (data[0] === 0x89 && data.toString('ascii', 1, 4) === 'PNG') {
4635 return new PNGImage(data, label);
4636 } else {
4637 throw new Error('Unknown image format.');
4638 }
4639 }
4640
4641}
4642
4643var ImagesMixin = {
4644 initImages() {
4645 this._imageRegistry = {};
4646 return this._imageCount = 0;
4647 },
4648
4649 image(src, x, y) {
4650 var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
4651 var bh, bp, bw, image, ip, left, left1, rotateAngle, originX, originY;
4652
4653 if (typeof x === 'object') {
4654 options = x;
4655 x = null;
4656 } // Ignore orientation based on document options or image options
4657
4658
4659 var ignoreOrientation = options.ignoreOrientation || options.ignoreOrientation !== false && this.options.ignoreOrientation;
4660 x = (left = x != null ? x : options.x) != null ? left : this.x;
4661 y = (left1 = y != null ? y : options.y) != null ? left1 : this.y;
4662
4663 if (typeof src === 'string') {
4664 image = this._imageRegistry[src];
4665 }
4666
4667 if (!image) {
4668 if (src.width && src.height) {
4669 image = src;
4670 } else {
4671 image = this.openImage(src);
4672 }
4673 }
4674
4675 if (!image.obj) {
4676 image.embed(this);
4677 }
4678
4679 if (this.page.xobjects[image.label] == null) {
4680 this.page.xobjects[image.label] = image.obj;
4681 }
4682
4683 var {
4684 width,
4685 height
4686 } = image; // If EXIF orientation calls for it, swap width and height
4687
4688 if (!ignoreOrientation && image.orientation > 4) {
4689 [width, height] = [height, width];
4690 }
4691
4692 var w = options.width || width;
4693 var h = options.height || height;
4694
4695 if (options.width && !options.height) {
4696 var wp = w / width;
4697 w = width * wp;
4698 h = height * wp;
4699 } else if (options.height && !options.width) {
4700 var hp = h / height;
4701 w = width * hp;
4702 h = height * hp;
4703 } else if (options.scale) {
4704 w = width * options.scale;
4705 h = height * options.scale;
4706 } else if (options.fit) {
4707 [bw, bh] = options.fit;
4708 bp = bw / bh;
4709 ip = width / height;
4710
4711 if (ip > bp) {
4712 w = bw;
4713 h = bw / ip;
4714 } else {
4715 h = bh;
4716 w = bh * ip;
4717 }
4718 } else if (options.cover) {
4719 [bw, bh] = options.cover;
4720 bp = bw / bh;
4721 ip = width / height;
4722
4723 if (ip > bp) {
4724 h = bh;
4725 w = bh * ip;
4726 } else {
4727 w = bw;
4728 h = bw / ip;
4729 }
4730 }
4731
4732 if (options.fit || options.cover) {
4733 if (options.align === 'center') {
4734 x = x + bw / 2 - w / 2;
4735 } else if (options.align === 'right') {
4736 x = x + bw - w;
4737 }
4738
4739 if (options.valign === 'center') {
4740 y = y + bh / 2 - h / 2;
4741 } else if (options.valign === 'bottom') {
4742 y = y + bh - h;
4743 }
4744 }
4745
4746 if (!ignoreOrientation) {
4747 switch (image.orientation) {
4748 // No orientation (need to flip image, though, because of the default transform matrix on the document)
4749 default:
4750 case 1:
4751 h = -h;
4752 y -= h;
4753 rotateAngle = 0;
4754 break;
4755 // Flip Horizontal
4756
4757 case 2:
4758 w = -w;
4759 h = -h;
4760 x -= w;
4761 y -= h;
4762 rotateAngle = 0;
4763 break;
4764 // Rotate 180 degrees
4765
4766 case 3:
4767 originX = x;
4768 originY = y;
4769 h = -h;
4770 x -= w;
4771 rotateAngle = 180;
4772 break;
4773 // Flip vertical
4774
4775 case 4:
4776 // Do nothing, image will be flipped
4777 break;
4778 // Flip horizontally and rotate 270 degrees CW
4779
4780 case 5:
4781 originX = x;
4782 originY = y;
4783 [w, h] = [h, w];
4784 y -= h;
4785 rotateAngle = 90;
4786 break;
4787 // Rotate 90 degrees CW
4788
4789 case 6:
4790 originX = x;
4791 originY = y;
4792 [w, h] = [h, w];
4793 h = -h;
4794 rotateAngle = 90;
4795 break;
4796 // Flip horizontally and rotate 90 degrees CW
4797
4798 case 7:
4799 originX = x;
4800 originY = y;
4801 [w, h] = [h, w];
4802 h = -h;
4803 w = -w;
4804 x -= w;
4805 rotateAngle = 90;
4806 break;
4807 // Rotate 270 degrees CW
4808
4809 case 8:
4810 originX = x;
4811 originY = y;
4812 [w, h] = [h, w];
4813 h = -h;
4814 x -= w;
4815 y -= h;
4816 rotateAngle = -90;
4817 break;
4818 }
4819 } else {
4820 h = -h;
4821 y -= h;
4822 rotateAngle = 0;
4823 } // create link annotations if the link option is given
4824
4825
4826 if (options.link != null) {
4827 this.link(x, y, w, h, options.link);
4828 }
4829
4830 if (options.goTo != null) {
4831 this.goTo(x, y, w, h, options.goTo);
4832 }
4833
4834 if (options.destination != null) {
4835 this.addNamedDestination(options.destination, 'XYZ', x, y, null);
4836 } // Set the current y position to below the image if it is in the document flow
4837
4838
4839 if (this.y === y) {
4840 this.y += h;
4841 }
4842
4843 this.save();
4844
4845 if (rotateAngle) {
4846 this.rotate(rotateAngle, {
4847 origin: [originX, originY]
4848 });
4849 }
4850
4851 this.transform(w, 0, 0, h, x, y);
4852 this.addContent("/".concat(image.label, " Do"));
4853 this.restore();
4854 return this;
4855 },
4856
4857 openImage(src) {
4858 var image;
4859
4860 if (typeof src === 'string') {
4861 image = this._imageRegistry[src];
4862 }
4863
4864 if (!image) {
4865 image = PDFImage.open(src, "I".concat(++this._imageCount));
4866
4867 if (typeof src === 'string') {
4868 this._imageRegistry[src] = image;
4869 }
4870 }
4871
4872 return image;
4873 }
4874
4875};
4876
4877var AnnotationsMixin = {
4878 annotate(x, y, w, h, options) {
4879 options.Type = 'Annot';
4880 options.Rect = this._convertRect(x, y, w, h);
4881 options.Border = [0, 0, 0];
4882
4883 if (options.Subtype === 'Link' && typeof options.F === 'undefined') {
4884 options.F = 1 << 2; // Print Annotation Flag
4885 }
4886
4887 if (options.Subtype !== 'Link') {
4888 if (options.C == null) {
4889 options.C = this._normalizeColor(options.color || [0, 0, 0]);
4890 }
4891 } // convert colors
4892
4893
4894 delete options.color;
4895
4896 if (typeof options.Dest === 'string') {
4897 options.Dest = new String(options.Dest);
4898 } // Capitalize keys
4899
4900
4901 for (var key in options) {
4902 var val = options[key];
4903 options[key[0].toUpperCase() + key.slice(1)] = val;
4904 }
4905
4906 var ref = this.ref(options);
4907 this.page.annotations.push(ref);
4908 ref.end();
4909 return this;
4910 },
4911
4912 note(x, y, w, h, contents) {
4913 var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
4914 options.Subtype = 'Text';
4915 options.Contents = new String(contents);
4916 options.Name = 'Comment';
4917
4918 if (options.color == null) {
4919 options.color = [243, 223, 92];
4920 }
4921
4922 return this.annotate(x, y, w, h, options);
4923 },
4924
4925 goTo(x, y, w, h, name) {
4926 var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
4927 options.Subtype = 'Link';
4928 options.A = this.ref({
4929 S: 'GoTo',
4930 D: new String(name)
4931 });
4932 options.A.end();
4933 return this.annotate(x, y, w, h, options);
4934 },
4935
4936 link(x, y, w, h, url) {
4937 var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
4938 options.Subtype = 'Link';
4939
4940 if (typeof url === 'number') {
4941 // Link to a page in the document (the page must already exist)
4942 var pages = this._root.data.Pages.data;
4943
4944 if (url >= 0 && url < pages.Kids.length) {
4945 options.A = this.ref({
4946 S: 'GoTo',
4947 D: [pages.Kids[url], 'XYZ', null, null, null]
4948 });
4949 options.A.end();
4950 } else {
4951 throw new Error("The document has no page ".concat(url));
4952 }
4953 } else {
4954 // Link to an external url
4955 options.A = this.ref({
4956 S: 'URI',
4957 URI: new String(url)
4958 });
4959 options.A.end();
4960 }
4961
4962 return this.annotate(x, y, w, h, options);
4963 },
4964
4965 _markup(x, y, w, h) {
4966 var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
4967
4968 var [x1, y1, x2, y2] = this._convertRect(x, y, w, h);
4969
4970 options.QuadPoints = [x1, y2, x2, y2, x1, y1, x2, y1];
4971 options.Contents = new String();
4972 return this.annotate(x, y, w, h, options);
4973 },
4974
4975 highlight(x, y, w, h) {
4976 var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
4977 options.Subtype = 'Highlight';
4978
4979 if (options.color == null) {
4980 options.color = [241, 238, 148];
4981 }
4982
4983 return this._markup(x, y, w, h, options);
4984 },
4985
4986 underline(x, y, w, h) {
4987 var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
4988 options.Subtype = 'Underline';
4989 return this._markup(x, y, w, h, options);
4990 },
4991
4992 strike(x, y, w, h) {
4993 var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
4994 options.Subtype = 'StrikeOut';
4995 return this._markup(x, y, w, h, options);
4996 },
4997
4998 lineAnnotation(x1, y1, x2, y2) {
4999 var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
5000 options.Subtype = 'Line';
5001 options.Contents = new String();
5002 options.L = [x1, this.page.height - y1, x2, this.page.height - y2];
5003 return this.annotate(x1, y1, x2, y2, options);
5004 },
5005
5006 rectAnnotation(x, y, w, h) {
5007 var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
5008 options.Subtype = 'Square';
5009 options.Contents = new String();
5010 return this.annotate(x, y, w, h, options);
5011 },
5012
5013 ellipseAnnotation(x, y, w, h) {
5014 var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
5015 options.Subtype = 'Circle';
5016 options.Contents = new String();
5017 return this.annotate(x, y, w, h, options);
5018 },
5019
5020 textAnnotation(x, y, w, h, text) {
5021 var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
5022 options.Subtype = 'FreeText';
5023 options.Contents = new String(text);
5024 options.DA = new String();
5025 return this.annotate(x, y, w, h, options);
5026 },
5027
5028 fileAnnotation(x, y, w, h) {
5029 var file = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
5030 var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
5031 // create hidden file
5032 var filespec = this.file(file.src, Object.assign({
5033 hidden: true
5034 }, file));
5035 options.Subtype = 'FileAttachment';
5036 options.FS = filespec; // add description from filespec unless description (Contents) has already been set
5037
5038 if (options.Contents) {
5039 options.Contents = new String(options.Contents);
5040 } else if (filespec.data.Desc) {
5041 options.Contents = filespec.data.Desc;
5042 }
5043
5044 return this.annotate(x, y, w, h, options);
5045 },
5046
5047 _convertRect(x1, y1, w, h) {
5048 // flip y1 and y2
5049 var y2 = y1;
5050 y1 += h; // make x2
5051
5052 var x2 = x1 + w; // apply current transformation matrix to points
5053
5054 var [m0, m1, m2, m3, m4, m5] = this._ctm;
5055 x1 = m0 * x1 + m2 * y1 + m4;
5056 y1 = m1 * x1 + m3 * y1 + m5;
5057 x2 = m0 * x2 + m2 * y2 + m4;
5058 y2 = m1 * x2 + m3 * y2 + m5;
5059 return [x1, y1, x2, y2];
5060 }
5061
5062};
5063
5064class PDFOutline {
5065 constructor(document, parent, title, dest) {
5066 var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {
5067 expanded: false
5068 };
5069 this.document = document;
5070 this.options = options;
5071 this.outlineData = {};
5072
5073 if (dest !== null) {
5074 this.outlineData['Dest'] = [dest.dictionary, 'Fit'];
5075 }
5076
5077 if (parent !== null) {
5078 this.outlineData['Parent'] = parent;
5079 }
5080
5081 if (title !== null) {
5082 this.outlineData['Title'] = new String(title);
5083 }
5084
5085 this.dictionary = this.document.ref(this.outlineData);
5086 this.children = [];
5087 }
5088
5089 addItem(title) {
5090 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
5091 expanded: false
5092 };
5093 var result = new PDFOutline(this.document, this.dictionary, title, this.document.page, options);
5094 this.children.push(result);
5095 return result;
5096 }
5097
5098 endOutline() {
5099 if (this.children.length > 0) {
5100 if (this.options.expanded) {
5101 this.outlineData.Count = this.children.length;
5102 }
5103
5104 var first = this.children[0],
5105 last = this.children[this.children.length - 1];
5106 this.outlineData.First = first.dictionary;
5107 this.outlineData.Last = last.dictionary;
5108
5109 for (var i = 0, len = this.children.length; i < len; i++) {
5110 var child = this.children[i];
5111
5112 if (i > 0) {
5113 child.outlineData.Prev = this.children[i - 1].dictionary;
5114 }
5115
5116 if (i < this.children.length - 1) {
5117 child.outlineData.Next = this.children[i + 1].dictionary;
5118 }
5119
5120 child.endOutline();
5121 }
5122 }
5123
5124 return this.dictionary.end();
5125 }
5126
5127}
5128
5129var OutlineMixin = {
5130 initOutline() {
5131 return this.outline = new PDFOutline(this, null, null, null);
5132 },
5133
5134 endOutline() {
5135 this.outline.endOutline();
5136
5137 if (this.outline.children.length > 0) {
5138 this._root.data.Outlines = this.outline.dictionary;
5139 return this._root.data.PageMode = 'UseOutlines';
5140 }
5141 }
5142
5143};
5144
5145function _defineProperty(obj, key, value) {
5146 if (key in obj) {
5147 Object.defineProperty(obj, key, {
5148 value: value,
5149 enumerable: true,
5150 configurable: true,
5151 writable: true
5152 });
5153 } else {
5154 obj[key] = value;
5155 }
5156
5157 return obj;
5158}
5159
5160function ownKeys(object, enumerableOnly) {
5161 var keys = Object.keys(object);
5162
5163 if (Object.getOwnPropertySymbols) {
5164 var symbols = Object.getOwnPropertySymbols(object);
5165 if (enumerableOnly) symbols = symbols.filter(function (sym) {
5166 return Object.getOwnPropertyDescriptor(object, sym).enumerable;
5167 });
5168 keys.push.apply(keys, symbols);
5169 }
5170
5171 return keys;
5172}
5173
5174function _objectSpread2(target) {
5175 for (var i = 1; i < arguments.length; i++) {
5176 var source = arguments[i] != null ? arguments[i] : {};
5177
5178 if (i % 2) {
5179 ownKeys(Object(source), true).forEach(function (key) {
5180 _defineProperty(target, key, source[key]);
5181 });
5182 } else if (Object.getOwnPropertyDescriptors) {
5183 Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
5184 } else {
5185 ownKeys(Object(source)).forEach(function (key) {
5186 Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
5187 });
5188 }
5189 }
5190
5191 return target;
5192}
5193
5194/*
5195PDFStructureContent - a reference to a marked structure content
5196By Ben Schmidt
5197*/
5198class PDFStructureContent {
5199 constructor(pageRef, mcid) {
5200 this.refs = [{
5201 pageRef,
5202 mcid
5203 }];
5204 }
5205
5206 push(structContent) {
5207 structContent.refs.forEach(ref => this.refs.push(ref));
5208 }
5209
5210}
5211
5212/*
5213PDFStructureElement - represents an element in the PDF logical structure tree
5214By Ben Schmidt
5215*/
5216
5217class PDFStructureElement {
5218 constructor(document, type) {
5219 var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
5220 var children = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
5221 this.document = document;
5222 this._attached = false;
5223 this._ended = false;
5224 this._flushed = false;
5225 this.dictionary = document.ref({
5226 // Type: "StructElem",
5227 S: type
5228 });
5229 var data = this.dictionary.data;
5230
5231 if (Array.isArray(options) || this._isValidChild(options)) {
5232 children = options;
5233 options = {};
5234 }
5235
5236 if (typeof options.title !== 'undefined') {
5237 data.T = new String(options.title);
5238 }
5239
5240 if (typeof options.lang !== 'undefined') {
5241 data.Lang = new String(options.lang);
5242 }
5243
5244 if (typeof options.alt !== 'undefined') {
5245 data.Alt = new String(options.alt);
5246 }
5247
5248 if (typeof options.expanded !== 'undefined') {
5249 data.E = new String(options.expanded);
5250 }
5251
5252 if (typeof options.actual !== 'undefined') {
5253 data.ActualText = new String(options.actual);
5254 }
5255
5256 this._children = [];
5257
5258 if (children) {
5259 if (!Array.isArray(children)) {
5260 children = [children];
5261 }
5262
5263 children.forEach(child => this.add(child));
5264 this.end();
5265 }
5266 }
5267
5268 add(child) {
5269 if (this._ended) {
5270 throw new Error("Cannot add child to already-ended structure element");
5271 }
5272
5273 if (!this._isValidChild(child)) {
5274 throw new Error("Invalid structure element child");
5275 }
5276
5277 if (child instanceof PDFStructureElement) {
5278 child.setParent(this.dictionary);
5279
5280 if (this._attached) {
5281 child.setAttached();
5282 }
5283 }
5284
5285 if (child instanceof PDFStructureContent) {
5286 this._addContentToParentTree(child);
5287 }
5288
5289 if (typeof child === 'function' && this._attached) {
5290 // _contentForClosure() adds the content to the parent tree
5291 child = this._contentForClosure(child);
5292 }
5293
5294 this._children.push(child);
5295
5296 return this;
5297 }
5298
5299 _addContentToParentTree(content) {
5300 content.refs.forEach((_ref) => {
5301 var {
5302 pageRef,
5303 mcid
5304 } = _ref;
5305 var pageStructParents = this.document.getStructParentTree().get(pageRef.data.StructParents);
5306 pageStructParents[mcid] = this.dictionary;
5307 });
5308 }
5309
5310 setParent(parentRef) {
5311 if (this.dictionary.data.P) {
5312 throw new Error("Structure element added to more than one parent");
5313 }
5314
5315 this.dictionary.data.P = parentRef;
5316
5317 this._flush();
5318 }
5319
5320 setAttached() {
5321 if (this._attached) {
5322 return;
5323 }
5324
5325 this._children.forEach((child, index) => {
5326 if (child instanceof PDFStructureElement) {
5327 child.setAttached();
5328 }
5329
5330 if (typeof child === 'function') {
5331 this._children[index] = this._contentForClosure(child);
5332 }
5333 });
5334
5335 this._attached = true;
5336
5337 this._flush();
5338 }
5339
5340 end() {
5341 if (this._ended) {
5342 return;
5343 }
5344
5345 this._children.filter(child => child instanceof PDFStructureElement).forEach(child => child.end());
5346
5347 this._ended = true;
5348
5349 this._flush();
5350 }
5351
5352 _isValidChild(child) {
5353 return child instanceof PDFStructureElement || child instanceof PDFStructureContent || typeof child === 'function';
5354 }
5355
5356 _contentForClosure(closure) {
5357 var content = this.document.markStructureContent(this.dictionary.data.S);
5358 closure();
5359 this.document.endMarkedContent();
5360
5361 this._addContentToParentTree(content);
5362
5363 return content;
5364 }
5365
5366 _isFlushable() {
5367 if (!this.dictionary.data.P || !this._ended) {
5368 return false;
5369 }
5370
5371 return this._children.every(child => {
5372 if (typeof child === 'function') {
5373 return false;
5374 }
5375
5376 if (child instanceof PDFStructureElement) {
5377 return child._isFlushable();
5378 }
5379
5380 return true;
5381 });
5382 }
5383
5384 _flush() {
5385 if (this._flushed || !this._isFlushable()) {
5386 return;
5387 }
5388
5389 this.dictionary.data.K = [];
5390
5391 this._children.forEach(child => this._flushChild(child));
5392
5393 this.dictionary.end(); // free memory used by children; the dictionary itself may still be
5394 // referenced by a parent structure element or root, but we can
5395 // at least trim the tree here
5396
5397 this._children = [];
5398 this.dictionary.data.K = null;
5399 this._flushed = true;
5400 }
5401
5402 _flushChild(child) {
5403 if (child instanceof PDFStructureElement) {
5404 this.dictionary.data.K.push(child.dictionary);
5405 }
5406
5407 if (child instanceof PDFStructureContent) {
5408 child.refs.forEach((_ref2) => {
5409 var {
5410 pageRef,
5411 mcid
5412 } = _ref2;
5413
5414 if (!this.dictionary.data.Pg) {
5415 this.dictionary.data.Pg = pageRef;
5416 }
5417
5418 if (this.dictionary.data.Pg === pageRef) {
5419 this.dictionary.data.K.push(mcid);
5420 } else {
5421 this.dictionary.data.K.push({
5422 Type: "MCR",
5423 Pg: pageRef,
5424 MCID: mcid
5425 });
5426 }
5427 });
5428 }
5429 }
5430
5431}
5432
5433/*
5434PDFNumberTree - represents a number tree object
5435*/
5436
5437class PDFNumberTree extends PDFTree {
5438 _compareKeys(a, b) {
5439 return parseInt(a) - parseInt(b);
5440 }
5441
5442 _keysName() {
5443 return "Nums";
5444 }
5445
5446 _dataForKey(k) {
5447 return parseInt(k);
5448 }
5449
5450}
5451
5452var MarkingsMixin = {
5453 initMarkings(options) {
5454 this.structChildren = [];
5455
5456 if (options.tagged) {
5457 this.getMarkInfoDictionary().data.Marked = true;
5458 this.getStructTreeRoot();
5459 }
5460 },
5461
5462 markContent(tag) {
5463 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
5464
5465 if (tag === 'Artifact' || options && options.mcid) {
5466 var toClose = 0;
5467 this.page.markings.forEach(marking => {
5468 if (toClose || marking.structContent || marking.tag === 'Artifact') {
5469 toClose++;
5470 }
5471 });
5472
5473 while (toClose--) {
5474 this.endMarkedContent();
5475 }
5476 }
5477
5478 if (!options) {
5479 this.page.markings.push({
5480 tag
5481 });
5482 this.addContent("/".concat(tag, " BMC"));
5483 return this;
5484 }
5485
5486 this.page.markings.push({
5487 tag,
5488 options
5489 });
5490 var dictionary = {};
5491
5492 if (typeof options.mcid !== 'undefined') {
5493 dictionary.MCID = options.mcid;
5494 }
5495
5496 if (tag === 'Artifact') {
5497 if (typeof options.type === 'string') {
5498 dictionary.Type = options.type;
5499 }
5500
5501 if (Array.isArray(options.bbox)) {
5502 dictionary.BBox = [options.bbox[0], this.page.height - options.bbox[3], options.bbox[2], this.page.height - options.bbox[1]];
5503 }
5504
5505 if (Array.isArray(options.attached) && options.attached.every(val => typeof val === 'string')) {
5506 dictionary.Attached = options.attached;
5507 }
5508 }
5509
5510 if (tag === 'Span') {
5511 if (options.lang) {
5512 dictionary.Lang = new String(options.lang);
5513 }
5514
5515 if (options.alt) {
5516 dictionary.Alt = new String(options.alt);
5517 }
5518
5519 if (options.expanded) {
5520 dictionary.E = new String(options.expanded);
5521 }
5522
5523 if (options.actual) {
5524 dictionary.ActualText = new String(options.actual);
5525 }
5526 }
5527
5528 this.addContent("/".concat(tag, " ").concat(PDFObject.convert(dictionary), " BDC"));
5529 return this;
5530 },
5531
5532 markStructureContent(tag) {
5533 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
5534 var pageStructParents = this.getStructParentTree().get(this.page.structParentTreeKey);
5535 var mcid = pageStructParents.length;
5536 pageStructParents.push(null);
5537 this.markContent(tag, _objectSpread2(_objectSpread2({}, options), {}, {
5538 mcid
5539 }));
5540 var structContent = new PDFStructureContent(this.page.dictionary, mcid);
5541 this.page.markings.slice(-1)[0].structContent = structContent;
5542 return structContent;
5543 },
5544
5545 endMarkedContent() {
5546 this.page.markings.pop();
5547 this.addContent('EMC');
5548 return this;
5549 },
5550
5551 struct(type) {
5552 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
5553 var children = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
5554 return new PDFStructureElement(this, type, options, children);
5555 },
5556
5557 addStructure(structElem) {
5558 var structTreeRoot = this.getStructTreeRoot();
5559 structElem.setParent(structTreeRoot);
5560 structElem.setAttached();
5561 this.structChildren.push(structElem);
5562
5563 if (!structTreeRoot.data.K) {
5564 structTreeRoot.data.K = [];
5565 }
5566
5567 structTreeRoot.data.K.push(structElem.dictionary);
5568 return this;
5569 },
5570
5571 initPageMarkings(pageMarkings) {
5572 pageMarkings.forEach(marking => {
5573 if (marking.structContent) {
5574 var structContent = marking.structContent;
5575 var newStructContent = this.markStructureContent(marking.tag, marking.options);
5576 structContent.push(newStructContent);
5577 this.page.markings.slice(-1)[0].structContent = structContent;
5578 } else {
5579 this.markContent(marking.tag, marking.options);
5580 }
5581 });
5582 },
5583
5584 endPageMarkings(page) {
5585 var pageMarkings = page.markings;
5586 pageMarkings.forEach(() => page.write('EMC'));
5587 page.markings = [];
5588 return pageMarkings;
5589 },
5590
5591 getMarkInfoDictionary() {
5592 if (!this._root.data.MarkInfo) {
5593 this._root.data.MarkInfo = this.ref({});
5594 }
5595
5596 return this._root.data.MarkInfo;
5597 },
5598
5599 getStructTreeRoot() {
5600 if (!this._root.data.StructTreeRoot) {
5601 this._root.data.StructTreeRoot = this.ref({
5602 Type: 'StructTreeRoot',
5603 ParentTree: new PDFNumberTree(),
5604 ParentTreeNextKey: 0
5605 });
5606 }
5607
5608 return this._root.data.StructTreeRoot;
5609 },
5610
5611 getStructParentTree() {
5612 return this.getStructTreeRoot().data.ParentTree;
5613 },
5614
5615 createStructParentTreeNextKey() {
5616 // initialise the MarkInfo dictionary
5617 this.getMarkInfoDictionary();
5618 var structTreeRoot = this.getStructTreeRoot();
5619 var key = structTreeRoot.data.ParentTreeNextKey++;
5620 structTreeRoot.data.ParentTree.add(key, []);
5621 return key;
5622 },
5623
5624 endMarkings() {
5625 var structTreeRoot = this._root.data.StructTreeRoot;
5626
5627 if (structTreeRoot) {
5628 structTreeRoot.end();
5629 this.structChildren.forEach(structElem => structElem.end());
5630 }
5631
5632 if (this._root.data.MarkInfo) {
5633 this._root.data.MarkInfo.end();
5634 }
5635 }
5636
5637};
5638
5639var FIELD_FLAGS = {
5640 readOnly: 1,
5641 required: 2,
5642 noExport: 4,
5643 multiline: 0x1000,
5644 password: 0x2000,
5645 toggleToOffButton: 0x4000,
5646 radioButton: 0x8000,
5647 pushButton: 0x10000,
5648 combo: 0x20000,
5649 edit: 0x40000,
5650 sort: 0x80000,
5651 multiSelect: 0x200000,
5652 noSpell: 0x400000
5653};
5654var FIELD_JUSTIFY = {
5655 left: 0,
5656 center: 1,
5657 right: 2
5658};
5659var VALUE_MAP = {
5660 value: 'V',
5661 defaultValue: 'DV'
5662};
5663var FORMAT_SPECIAL = {
5664 zip: '0',
5665 zipPlus4: '1',
5666 zip4: '1',
5667 phone: '2',
5668 ssn: '3'
5669};
5670var FORMAT_DEFAULT = {
5671 number: {
5672 nDec: 0,
5673 sepComma: false,
5674 negStyle: 'MinusBlack',
5675 currency: '',
5676 currencyPrepend: true
5677 },
5678 percent: {
5679 nDec: 0,
5680 sepComma: false
5681 }
5682};
5683var AcroFormMixin = {
5684 /**
5685 * Must call if adding AcroForms to a document. Must also call font() before
5686 * this method to set the default font.
5687 */
5688 initForm() {
5689 if (!this._font) {
5690 throw new Error('Must set a font before calling initForm method');
5691 }
5692
5693 this._acroform = {
5694 fonts: {},
5695 defaultFont: this._font.name
5696 };
5697 this._acroform.fonts[this._font.id] = this._font.ref();
5698 var data = {
5699 Fields: [],
5700 NeedAppearances: true,
5701 DA: new String("/".concat(this._font.id, " 0 Tf 0 g")),
5702 DR: {
5703 Font: {}
5704 }
5705 };
5706 data.DR.Font[this._font.id] = this._font.ref();
5707 var AcroForm = this.ref(data);
5708 this._root.data.AcroForm = AcroForm;
5709 return this;
5710 },
5711
5712 /**
5713 * Called automatically by document.js
5714 */
5715 endAcroForm() {
5716 if (this._root.data.AcroForm) {
5717 if (!Object.keys(this._acroform.fonts).length && !this._acroform.defaultFont) {
5718 throw new Error('No fonts specified for PDF form');
5719 }
5720
5721 var fontDict = this._root.data.AcroForm.data.DR.Font;
5722 Object.keys(this._acroform.fonts).forEach(name => {
5723 fontDict[name] = this._acroform.fonts[name];
5724 });
5725
5726 this._root.data.AcroForm.data.Fields.forEach(fieldRef => {
5727 this._endChild(fieldRef);
5728 });
5729
5730 this._root.data.AcroForm.end();
5731 }
5732
5733 return this;
5734 },
5735
5736 _endChild(ref) {
5737 if (Array.isArray(ref.data.Kids)) {
5738 ref.data.Kids.forEach(childRef => {
5739 this._endChild(childRef);
5740 });
5741 ref.end();
5742 }
5743
5744 return this;
5745 },
5746
5747 /**
5748 * Creates and adds a form field to the document. Form fields are intermediate
5749 * nodes in a PDF form that are used to specify form name heirarchy and form
5750 * value defaults.
5751 * @param {string} name - field name (T attribute in field dictionary)
5752 * @param {object} options - other attributes to include in field dictionary
5753 */
5754 formField(name) {
5755 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
5756
5757 var fieldDict = this._fieldDict(name, null, options);
5758
5759 var fieldRef = this.ref(fieldDict);
5760
5761 this._addToParent(fieldRef);
5762
5763 return fieldRef;
5764 },
5765
5766 /**
5767 * Creates and adds a Form Annotation to the document. Form annotations are
5768 * called Widget annotations internally within a PDF file.
5769 * @param {string} name - form field name (T attribute of widget annotation
5770 * dictionary)
5771 * @param {number} x
5772 * @param {number} y
5773 * @param {number} w
5774 * @param {number} h
5775 * @param {object} options
5776 */
5777 formAnnotation(name, type, x, y, w, h) {
5778 var options = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {};
5779
5780 var fieldDict = this._fieldDict(name, type, options);
5781
5782 fieldDict.Subtype = 'Widget';
5783
5784 if (fieldDict.F === undefined) {
5785 fieldDict.F = 4; // print the annotation
5786 } // Add Field annot to page, and get it's ref
5787
5788
5789 this.annotate(x, y, w, h, fieldDict);
5790 var annotRef = this.page.annotations[this.page.annotations.length - 1];
5791 return this._addToParent(annotRef);
5792 },
5793
5794 formText(name, x, y, w, h) {
5795 var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
5796 return this.formAnnotation(name, 'text', x, y, w, h, options);
5797 },
5798
5799 formPushButton(name, x, y, w, h) {
5800 var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
5801 return this.formAnnotation(name, 'pushButton', x, y, w, h, options);
5802 },
5803
5804 formCombo(name, x, y, w, h) {
5805 var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
5806 return this.formAnnotation(name, 'combo', x, y, w, h, options);
5807 },
5808
5809 formList(name, x, y, w, h) {
5810 var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
5811 return this.formAnnotation(name, 'list', x, y, w, h, options);
5812 },
5813
5814 formRadioButton(name, x, y, w, h) {
5815 var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
5816 return this.formAnnotation(name, 'radioButton', x, y, w, h, options);
5817 },
5818
5819 formCheckbox(name, x, y, w, h) {
5820 var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
5821 return this.formAnnotation(name, 'checkbox', x, y, w, h, options);
5822 },
5823
5824 _addToParent(fieldRef) {
5825 var parent = fieldRef.data.Parent;
5826
5827 if (parent) {
5828 if (!parent.data.Kids) {
5829 parent.data.Kids = [];
5830 }
5831
5832 parent.data.Kids.push(fieldRef);
5833 } else {
5834 this._root.data.AcroForm.data.Fields.push(fieldRef);
5835 }
5836
5837 return this;
5838 },
5839
5840 _fieldDict(name, type) {
5841 var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
5842
5843 if (!this._acroform) {
5844 throw new Error('Call document.initForms() method before adding form elements to document');
5845 }
5846
5847 var opts = Object.assign({}, options);
5848
5849 if (type !== null) {
5850 opts = this._resolveType(type, options);
5851 }
5852
5853 opts = this._resolveFlags(opts);
5854 opts = this._resolveJustify(opts);
5855 opts = this._resolveFont(opts);
5856 opts = this._resolveStrings(opts);
5857 opts = this._resolveColors(opts);
5858 opts = this._resolveFormat(opts);
5859 opts.T = new String(name);
5860
5861 if (opts.parent) {
5862 opts.Parent = opts.parent;
5863 delete opts.parent;
5864 }
5865
5866 return opts;
5867 },
5868
5869 _resolveType(type, opts) {
5870 if (type === 'text') {
5871 opts.FT = 'Tx';
5872 } else if (type === 'pushButton') {
5873 opts.FT = 'Btn';
5874 opts.pushButton = true;
5875 } else if (type === 'radioButton') {
5876 opts.FT = 'Btn';
5877 opts.radioButton = true;
5878 } else if (type === 'checkbox') {
5879 opts.FT = 'Btn';
5880 } else if (type === 'combo') {
5881 opts.FT = 'Ch';
5882 opts.combo = true;
5883 } else if (type === 'list') {
5884 opts.FT = 'Ch';
5885 } else {
5886 throw new Error("Invalid form annotation type '".concat(type, "'"));
5887 }
5888
5889 return opts;
5890 },
5891
5892 _resolveFormat(opts) {
5893 var f = opts.format;
5894
5895 if (f && f.type) {
5896 var fnKeystroke;
5897 var fnFormat;
5898 var params = '';
5899
5900 if (FORMAT_SPECIAL[f.type] !== undefined) {
5901 fnKeystroke = "AFSpecial_Keystroke";
5902 fnFormat = "AFSpecial_Format";
5903 params = FORMAT_SPECIAL[f.type];
5904 } else {
5905 var format = f.type.charAt(0).toUpperCase() + f.type.slice(1);
5906 fnKeystroke = "AF".concat(format, "_Keystroke");
5907 fnFormat = "AF".concat(format, "_Format");
5908
5909 if (f.type === 'date') {
5910 fnKeystroke += 'Ex';
5911 params = String(f.param);
5912 } else if (f.type === 'time') {
5913 params = String(f.param);
5914 } else if (f.type === 'number') {
5915 var p = Object.assign({}, FORMAT_DEFAULT.number, f);
5916 params = String([String(p.nDec), p.sepComma ? '0' : '1', '"' + p.negStyle + '"', 'null', '"' + p.currency + '"', String(p.currencyPrepend)].join(','));
5917 } else if (f.type === 'percent') {
5918 var _p = Object.assign({}, FORMAT_DEFAULT.percent, f);
5919
5920 params = String([String(_p.nDec), _p.sepComma ? '0' : '1'].join(','));
5921 }
5922 }
5923
5924 opts.AA = opts.AA ? opts.AA : {};
5925 opts.AA.K = {
5926 S: 'JavaScript',
5927 JS: new String("".concat(fnKeystroke, "(").concat(params, ");"))
5928 };
5929 opts.AA.F = {
5930 S: 'JavaScript',
5931 JS: new String("".concat(fnFormat, "(").concat(params, ");"))
5932 };
5933 }
5934
5935 delete opts.format;
5936 return opts;
5937 },
5938
5939 _resolveColors(opts) {
5940 var color = this._normalizeColor(opts.backgroundColor);
5941
5942 if (color) {
5943 if (!opts.MK) {
5944 opts.MK = {};
5945 }
5946
5947 opts.MK.BG = color;
5948 }
5949
5950 color = this._normalizeColor(opts.borderColor);
5951
5952 if (color) {
5953 if (!opts.MK) {
5954 opts.MK = {};
5955 }
5956
5957 opts.MK.BC = color;
5958 }
5959
5960 delete opts.backgroundColor;
5961 delete opts.borderColor;
5962 return opts;
5963 },
5964
5965 _resolveFlags(options) {
5966 var result = 0;
5967 Object.keys(options).forEach(key => {
5968 if (FIELD_FLAGS[key]) {
5969 if (options[key]) {
5970 result |= FIELD_FLAGS[key];
5971 }
5972
5973 delete options[key];
5974 }
5975 });
5976
5977 if (result !== 0) {
5978 options.Ff = options.Ff ? options.Ff : 0;
5979 options.Ff |= result;
5980 }
5981
5982 return options;
5983 },
5984
5985 _resolveJustify(options) {
5986 var result = 0;
5987
5988 if (options.align !== undefined) {
5989 if (typeof FIELD_JUSTIFY[options.align] === 'number') {
5990 result = FIELD_JUSTIFY[options.align];
5991 }
5992
5993 delete options.align;
5994 }
5995
5996 if (result !== 0) {
5997 options.Q = result; // default
5998 }
5999
6000 return options;
6001 },
6002
6003 _resolveFont(options) {
6004 // add current font to document-level AcroForm dict if necessary
6005 if (this._acroform.fonts[this._font.id] === null) {
6006 this._acroform.fonts[this._font.id] = this._font.ref();
6007 } // add current font to field's resource dict (RD) if not the default acroform font
6008
6009
6010 if (this._acroform.defaultFont !== this._font.name) {
6011 options.DR = {
6012 Font: {}
6013 }; // Get the fontSize option. If not set use auto sizing
6014
6015 var fontSize = options.fontSize || 0;
6016 options.DR.Font[this._font.id] = this._font.ref();
6017 options.DA = new String("/".concat(this._font.id, " ").concat(fontSize, " Tf 0 g"));
6018 }
6019
6020 return options;
6021 },
6022
6023 _resolveStrings(options) {
6024 var select = [];
6025
6026 function appendChoices(a) {
6027 if (Array.isArray(a)) {
6028 for (var idx = 0; idx < a.length; idx++) {
6029 if (typeof a[idx] === 'string') {
6030 select.push(new String(a[idx]));
6031 } else {
6032 select.push(a[idx]);
6033 }
6034 }
6035 }
6036 }
6037
6038 appendChoices(options.Opt);
6039
6040 if (options.select) {
6041 appendChoices(options.select);
6042 delete options.select;
6043 }
6044
6045 if (select.length) {
6046 options.Opt = select;
6047 }
6048
6049 Object.keys(VALUE_MAP).forEach(key => {
6050 if (options[key] !== undefined) {
6051 options[VALUE_MAP[key]] = options[key];
6052 delete options[key];
6053 }
6054 });
6055 ['V', 'DV'].forEach(key => {
6056 if (typeof options[key] === 'string') {
6057 options[key] = new String(options[key]);
6058 }
6059 });
6060
6061 if (options.MK && options.MK.CA) {
6062 options.MK.CA = new String(options.MK.CA);
6063 }
6064
6065 if (options.label) {
6066 options.MK = options.MK ? options.MK : {};
6067 options.MK.CA = new String(options.label);
6068 delete options.label;
6069 }
6070
6071 return options;
6072 }
6073
6074};
6075
6076var AttachmentsMixin = {
6077 /**
6078 * Embed contents of `src` in PDF
6079 * @param {Buffer | ArrayBuffer | string} src input Buffer, ArrayBuffer, base64 encoded string or path to file
6080 * @param {object} options
6081 * * options.name: filename to be shown in PDF, will use `src` if none set
6082 * * options.type: filetype to be shown in PDF
6083 * * options.description: description to be shown in PDF
6084 * * options.hidden: if true, do not add attachment to EmbeddedFiles dictionary. Useful for file attachment annotations
6085 * * options.creationDate: override creation date
6086 * * options.modifiedDate: override modified date
6087 * @returns filespec reference
6088 */
6089 file(src) {
6090 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
6091 options.name = options.name || src;
6092 var refBody = {
6093 Type: 'EmbeddedFile',
6094 Params: {}
6095 };
6096 var data;
6097
6098 if (!src) {
6099 throw new Error('No src specified');
6100 }
6101
6102 if (Buffer.isBuffer(src)) {
6103 data = src;
6104 } else if (src instanceof ArrayBuffer) {
6105 data = Buffer.from(new Uint8Array(src));
6106 } else {
6107 var match;
6108
6109 if (match = /^data:(.*?);base64,(.*)$/.exec(src)) {
6110 if (match[1]) {
6111 refBody.Subtype = match[1].replace('/', '#2F');
6112 }
6113
6114 data = Buffer.from(match[2], 'base64');
6115 } else {
6116 data = fs.readFileSync(src);
6117
6118 if (!data) {
6119 throw new Error("Could not read contents of file at filepath ".concat(src));
6120 } // update CreationDate and ModDate
6121
6122
6123 var {
6124 birthtime,
6125 ctime
6126 } = fs.statSync(src);
6127 refBody.Params.CreationDate = birthtime;
6128 refBody.Params.ModDate = ctime;
6129 }
6130 } // override creation date and modified date
6131
6132
6133 if (options.creationDate instanceof Date) {
6134 refBody.Params.CreationDate = options.creationDate;
6135 }
6136
6137 if (options.modifiedDate instanceof Date) {
6138 refBody.Params.ModDate = options.modifiedDate;
6139 } // add optional subtype
6140
6141
6142 if (options.type) {
6143 refBody.Subtype = options.type.replace('/', '#2F');
6144 } // add checksum and size information
6145
6146
6147 var checksum = CryptoJS.MD5(CryptoJS.lib.WordArray.create(new Uint8Array(data)));
6148 refBody.Params.CheckSum = new String(checksum);
6149 refBody.Params.Size = data.byteLength; // save some space when embedding the same file again
6150 // if a file with the same name and metadata exists, reuse its reference
6151
6152 var ref;
6153 if (!this._fileRegistry) this._fileRegistry = {};
6154 var file = this._fileRegistry[options.name];
6155
6156 if (file && isEqual(refBody, file)) {
6157 ref = file.ref;
6158 } else {
6159 ref = this.ref(refBody);
6160 ref.end(data);
6161 this._fileRegistry[options.name] = _objectSpread2(_objectSpread2({}, refBody), {}, {
6162 ref
6163 });
6164 } // add filespec for embedded file
6165
6166
6167 var fileSpecBody = {
6168 Type: 'Filespec',
6169 F: new String(options.name),
6170 EF: {
6171 F: ref
6172 },
6173 UF: new String(options.name)
6174 };
6175
6176 if (options.description) {
6177 fileSpecBody.Desc = new String(options.description);
6178 }
6179
6180 var filespec = this.ref(fileSpecBody);
6181 filespec.end();
6182
6183 if (!options.hidden) {
6184 this.addNamedEmbeddedFile(options.name, filespec);
6185 }
6186
6187 return filespec;
6188 }
6189
6190};
6191/** check two embedded file metadata objects for equality */
6192
6193function isEqual(a, b) {
6194 return a.Subtype === b.Subtype && a.Params.CheckSum.toString() === b.Params.CheckSum.toString() && a.Params.Size === b.Params.Size && a.Params.CreationDate === b.Params.CreationDate && a.Params.ModDate === b.Params.ModDate;
6195}
6196
6197var PDFA = {
6198 initPDFA(pSubset) {
6199 if (pSubset.charAt(pSubset.length - 3) === '-') {
6200 this.subset_conformance = pSubset.charAt(pSubset.length - 1).toUpperCase();
6201 this.subset = parseInt(pSubset.charAt(pSubset.length - 2));
6202 } else {
6203 // Default to Basic conformance when user doesn't specify
6204 this.subset_conformance = 'B';
6205 this.subset = parseInt(pSubset.charAt(pSubset.length - 1));
6206 }
6207 },
6208
6209 endSubset() {
6210 this._addPdfaMetadata();
6211
6212 var jsPath = "".concat(__dirname, "/data/sRGB_IEC61966_2_1.icc");
6213 var jestPath = "".concat(__dirname, "/../color_profiles/sRGB_IEC61966_2_1.icc");
6214
6215 this._addColorOutputIntent(fs.existsSync(jsPath) ? jsPath : jestPath);
6216 },
6217
6218 _addColorOutputIntent(pICCPath) {
6219 var iccProfile = fs.readFileSync(pICCPath);
6220 var colorProfileRef = this.ref({
6221 Length: iccProfile.length,
6222 N: 3
6223 });
6224 colorProfileRef.write(iccProfile);
6225 colorProfileRef.end();
6226 var intentRef = this.ref({
6227 Type: 'OutputIntent',
6228 S: 'GTS_PDFA1',
6229 Info: new String('sRGB IEC61966-2.1'),
6230 OutputConditionIdentifier: new String('sRGB IEC61966-2.1'),
6231 DestOutputProfile: colorProfileRef
6232 });
6233 intentRef.end();
6234 this._root.data.OutputIntents = [intentRef];
6235 },
6236
6237 _getPdfaid() {
6238 return "\n <rdf:Description xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\" rdf:about=\"\">\n <pdfaid:part>".concat(this.subset, "</pdfaid:part>\n <pdfaid:conformance>").concat(this.subset_conformance, "</pdfaid:conformance>\n </rdf:Description>\n ");
6239 },
6240
6241 _addPdfaMetadata() {
6242 this.appendXML(this._getPdfaid());
6243 }
6244
6245};
6246
6247var PDFUA = {
6248 initPDFUA() {
6249 this.subset = 1;
6250 },
6251
6252 endSubset() {
6253 this._addPdfuaMetadata();
6254 },
6255
6256 _addPdfuaMetadata() {
6257 this.appendXML(this._getPdfuaid());
6258 },
6259
6260 _getPdfuaid() {
6261 return "\n <rdf:Description xmlns:pdfuaid=\"http://www.aiim.org/pdfua/ns/id/\" rdf:about=\"\">\n <pdfuaid:part>".concat(this.subset, "</pdfuaid:part>\n </rdf:Description>\n ");
6262 }
6263
6264};
6265
6266var SubsetMixin = {
6267 _importSubset(subset) {
6268 Object.assign(this, subset);
6269 },
6270
6271 initSubset(options) {
6272 switch (options.subset) {
6273 case 'PDF/A-1':
6274 case 'PDF/A-1a':
6275 case 'PDF/A-1b':
6276 case 'PDF/A-2':
6277 case 'PDF/A-2a':
6278 case 'PDF/A-2b':
6279 case 'PDF/A-3':
6280 case 'PDF/A-3a':
6281 case 'PDF/A-3b':
6282 this._importSubset(PDFA);
6283
6284 this.initPDFA(options.subset);
6285 break;
6286
6287 case 'PDF/UA':
6288 this._importSubset(PDFUA);
6289
6290 this.initPDFUA();
6291 break;
6292 }
6293 }
6294
6295};
6296
6297class PDFMetadata {
6298 constructor() {
6299 this._metadata = "\n <?xpacket begin=\"\uFEFF\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n <x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n ";
6300 }
6301
6302 _closeTags() {
6303 this._metadata = this._metadata.concat("\n </rdf:RDF>\n </x:xmpmeta>\n <?xpacket end=\"w\"?>\n ");
6304 }
6305
6306 append(xml) {
6307 var newline = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
6308 this._metadata = this._metadata.concat(xml);
6309 if (newline) this._metadata = this._metadata.concat('\n');
6310 }
6311
6312 getXML() {
6313 return this._metadata;
6314 }
6315
6316 getLength() {
6317 return this._metadata.length;
6318 }
6319
6320 end() {
6321 this._closeTags();
6322
6323 this._metadata = this._metadata.trim();
6324 }
6325
6326}
6327
6328var MetadataMixin = {
6329 initMetadata() {
6330 this.metadata = new PDFMetadata();
6331 },
6332
6333 appendXML(xml) {
6334 var newline = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
6335 this.metadata.append(xml, newline);
6336 },
6337
6338 _addInfo() {
6339 this.appendXML("\n <rdf:Description rdf:about=\"\" xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n <xmp:CreateDate>".concat(this.info.CreationDate.toISOString().split('.')[0] + "Z", "</xmp:CreateDate>\n <xmp:CreatorTool>").concat(this.info.Creator, "</xmp:CreatorTool>\n </rdf:Description>\n "));
6340
6341 if (this.info.Title || this.info.Author || this.info.Subject) {
6342 this.appendXML("\n <rdf:Description rdf:about=\"\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n ");
6343
6344 if (this.info.Title) {
6345 this.appendXML("\n <dc:title>\n <rdf:Alt>\n <rdf:li xml:lang=\"x-default\">".concat(this.info.Title, "</rdf:li>\n </rdf:Alt>\n </dc:title>\n "));
6346 }
6347
6348 if (this.info.Author) {
6349 this.appendXML("\n <dc:creator>\n <rdf:Seq>\n <rdf:li>".concat(this.info.Author, "</rdf:li>\n </rdf:Seq>\n </dc:creator>\n "));
6350 }
6351
6352 if (this.info.Subject) {
6353 this.appendXML("\n <dc:description>\n <rdf:Alt>\n <rdf:li xml:lang=\"x-default\">".concat(this.info.Subject, "</rdf:li>\n </rdf:Alt>\n </dc:description>\n "));
6354 }
6355
6356 this.appendXML("\n </rdf:Description>\n ");
6357 }
6358
6359 this.appendXML("\n <rdf:Description rdf:about=\"\" xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n <pdf:Producer>".concat(this.info.Creator, "</pdf:Producer>"), false);
6360
6361 if (this.info.Keywords) {
6362 this.appendXML("\n <pdf:Keywords>".concat(this.info.Keywords, "</pdf:Keywords>"), false);
6363 }
6364
6365 this.appendXML("\n </rdf:Description>\n ");
6366 },
6367
6368 endMetadata() {
6369 this._addInfo();
6370
6371 this.metadata.end();
6372 /*
6373 Metadata was introduced in PDF 1.4, so adding it to 1.3
6374 will likely only take up more space.
6375 */
6376
6377 if (this.version != 1.3) {
6378 this.metadataRef = this.ref({
6379 length: this.metadata.getLength(),
6380 Type: 'Metadata',
6381 Subtype: 'XML'
6382 });
6383 this.metadataRef.compress = false;
6384 this.metadataRef.write(Buffer.from(this.metadata.getXML(), 'utf-8'));
6385 this.metadataRef.end();
6386 this._root.data.Metadata = this.metadataRef;
6387 }
6388 }
6389
6390};
6391
6392/*
6393PDFDocument - represents an entire PDF document
6394By Devon Govett
6395*/
6396
6397class PDFDocument extends stream.Readable {
6398 constructor() {
6399 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
6400 super(options);
6401 this.options = options; // PDF version
6402
6403 switch (options.pdfVersion) {
6404 case '1.4':
6405 this.version = 1.4;
6406 break;
6407
6408 case '1.5':
6409 this.version = 1.5;
6410 break;
6411
6412 case '1.6':
6413 this.version = 1.6;
6414 break;
6415
6416 case '1.7':
6417 case '1.7ext3':
6418 this.version = 1.7;
6419 break;
6420
6421 default:
6422 this.version = 1.3;
6423 break;
6424 } // Whether streams should be compressed
6425
6426
6427 this.compress = this.options.compress != null ? this.options.compress : true;
6428 this._pageBuffer = [];
6429 this._pageBufferStart = 0; // The PDF object store
6430
6431 this._offsets = [];
6432 this._waiting = 0;
6433 this._ended = false;
6434 this._offset = 0;
6435 var Pages = this.ref({
6436 Type: 'Pages',
6437 Count: 0,
6438 Kids: []
6439 });
6440 var Names = this.ref({
6441 Dests: new PDFNameTree()
6442 });
6443 this._root = this.ref({
6444 Type: 'Catalog',
6445 Pages,
6446 Names
6447 });
6448
6449 if (this.options.lang) {
6450 this._root.data.Lang = new String(this.options.lang);
6451 } // The current page
6452
6453
6454 this.page = null; // Initialize mixins
6455
6456 this.initMetadata();
6457 this.initColor();
6458 this.initVector();
6459 this.initFonts(options.font);
6460 this.initText();
6461 this.initImages();
6462 this.initOutline();
6463 this.initMarkings(options);
6464 this.initSubset(options); // Initialize the metadata
6465
6466 this.info = {
6467 Producer: 'PDFKit',
6468 Creator: 'PDFKit',
6469 CreationDate: new Date()
6470 };
6471
6472 if (this.options.info) {
6473 for (var key in this.options.info) {
6474 var val = this.options.info[key];
6475 this.info[key] = val;
6476 }
6477 }
6478
6479 if (this.options.displayTitle) {
6480 this._root.data.ViewerPreferences = this.ref({
6481 DisplayDocTitle: true
6482 });
6483 } // Generate file ID
6484
6485
6486 this._id = PDFSecurity.generateFileID(this.info); // Initialize security settings
6487
6488 this._security = PDFSecurity.create(this, options); // Write the header
6489 // PDF version
6490
6491 this._write("%PDF-".concat(this.version)); // 4 binary chars, as recommended by the spec
6492
6493
6494 this._write('%\xFF\xFF\xFF\xFF'); // Add the first page
6495
6496
6497 if (this.options.autoFirstPage !== false) {
6498 this.addPage();
6499 }
6500 }
6501
6502 addPage(options) {
6503 if (options == null) {
6504 ({
6505 options
6506 } = this);
6507 } // end the current page if needed
6508
6509
6510 if (!this.options.bufferPages) {
6511 this.flushPages();
6512 } // create a page object
6513
6514
6515 this.page = new PDFPage(this, options);
6516
6517 this._pageBuffer.push(this.page); // add the page to the object store
6518
6519
6520 var pages = this._root.data.Pages.data;
6521 pages.Kids.push(this.page.dictionary);
6522 pages.Count++; // reset x and y coordinates
6523
6524 this.x = this.page.margins.left;
6525 this.y = this.page.margins.top; // flip PDF coordinate system so that the origin is in
6526 // the top left rather than the bottom left
6527
6528 this._ctm = [1, 0, 0, 1, 0, 0];
6529 this.transform(1, 0, 0, -1, 0, this.page.height);
6530 this.emit('pageAdded');
6531 return this;
6532 }
6533
6534 continueOnNewPage(options) {
6535 var pageMarkings = this.endPageMarkings(this.page);
6536 this.addPage(options);
6537 this.initPageMarkings(pageMarkings);
6538 return this;
6539 }
6540
6541 bufferedPageRange() {
6542 return {
6543 start: this._pageBufferStart,
6544 count: this._pageBuffer.length
6545 };
6546 }
6547
6548 switchToPage(n) {
6549 var page;
6550
6551 if (!(page = this._pageBuffer[n - this._pageBufferStart])) {
6552 throw new Error("switchToPage(".concat(n, ") out of bounds, current buffer covers pages ").concat(this._pageBufferStart, " to ").concat(this._pageBufferStart + this._pageBuffer.length - 1));
6553 }
6554
6555 return this.page = page;
6556 }
6557
6558 flushPages() {
6559 // this local variable exists so we're future-proof against
6560 // reentrant calls to flushPages.
6561 var pages = this._pageBuffer;
6562 this._pageBuffer = [];
6563 this._pageBufferStart += pages.length;
6564
6565 for (var page of pages) {
6566 this.endPageMarkings(page);
6567 page.end();
6568 }
6569 }
6570
6571 addNamedDestination(name) {
6572 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
6573 args[_key - 1] = arguments[_key];
6574 }
6575
6576 if (args.length === 0) {
6577 args = ['XYZ', null, null, null];
6578 }
6579
6580 if (args[0] === 'XYZ' && args[2] !== null) {
6581 args[2] = this.page.height - args[2];
6582 }
6583
6584 args.unshift(this.page.dictionary);
6585
6586 this._root.data.Names.data.Dests.add(name, args);
6587 }
6588
6589 addNamedEmbeddedFile(name, ref) {
6590 if (!this._root.data.Names.data.EmbeddedFiles) {
6591 // disabling /Limits for this tree fixes attachments not showing in Adobe Reader
6592 this._root.data.Names.data.EmbeddedFiles = new PDFNameTree({
6593 limits: false
6594 });
6595 } // add filespec to EmbeddedFiles
6596
6597
6598 this._root.data.Names.data.EmbeddedFiles.add(name, ref);
6599 }
6600
6601 addNamedJavaScript(name, js) {
6602 if (!this._root.data.Names.data.JavaScript) {
6603 this._root.data.Names.data.JavaScript = new PDFNameTree();
6604 }
6605
6606 var data = {
6607 JS: new String(js),
6608 S: 'JavaScript'
6609 };
6610
6611 this._root.data.Names.data.JavaScript.add(name, data);
6612 }
6613
6614 ref(data) {
6615 var ref = new PDFReference(this, this._offsets.length + 1, data);
6616
6617 this._offsets.push(null); // placeholder for this object's offset once it is finalized
6618
6619
6620 this._waiting++;
6621 return ref;
6622 }
6623
6624 _read() {} // do nothing, but this method is required by node
6625
6626
6627 _write(data) {
6628 if (!Buffer.isBuffer(data)) {
6629 data = Buffer.from(data + '\n', 'binary');
6630 }
6631
6632 this.push(data);
6633 return this._offset += data.length;
6634 }
6635
6636 addContent(data) {
6637 this.page.write(data);
6638 return this;
6639 }
6640
6641 _refEnd(ref) {
6642 this._offsets[ref.id - 1] = ref.offset;
6643
6644 if (--this._waiting === 0 && this._ended) {
6645 this._finalize();
6646
6647 return this._ended = false;
6648 }
6649 }
6650
6651 write(filename, fn) {
6652 // print a deprecation warning with a stacktrace
6653 var err = new Error("PDFDocument#write is deprecated, and will be removed in a future version of PDFKit. Please pipe the document into a Node stream.");
6654 console.warn(err.stack);
6655 this.pipe(fs.createWriteStream(filename));
6656 this.end();
6657 return this.once('end', fn);
6658 }
6659
6660 end() {
6661 this.flushPages();
6662 this._info = this.ref();
6663
6664 for (var key in this.info) {
6665 var val = this.info[key];
6666
6667 if (typeof val === 'string') {
6668 val = new String(val);
6669 }
6670
6671 var entry = this.ref(val);
6672 entry.end();
6673 this._info.data[key] = entry;
6674 }
6675
6676 this._info.end();
6677
6678 for (var name in this._fontFamilies) {
6679 var font = this._fontFamilies[name];
6680 font.finalize();
6681 }
6682
6683 this.endOutline();
6684 this.endMarkings();
6685
6686 if (this.subset) {
6687 this.endSubset();
6688 }
6689
6690 this.endMetadata();
6691
6692 this._root.end();
6693
6694 this._root.data.Pages.end();
6695
6696 this._root.data.Names.end();
6697
6698 this.endAcroForm();
6699
6700 if (this._root.data.ViewerPreferences) {
6701 this._root.data.ViewerPreferences.end();
6702 }
6703
6704 if (this._security) {
6705 this._security.end();
6706 }
6707
6708 if (this._waiting === 0) {
6709 return this._finalize();
6710 } else {
6711 return this._ended = true;
6712 }
6713 }
6714
6715 _finalize() {
6716 // generate xref
6717 var xRefOffset = this._offset;
6718
6719 this._write('xref');
6720
6721 this._write("0 ".concat(this._offsets.length + 1));
6722
6723 this._write('0000000000 65535 f ');
6724
6725 for (var offset of this._offsets) {
6726 offset = "0000000000".concat(offset).slice(-10);
6727
6728 this._write(offset + ' 00000 n ');
6729 } // trailer
6730
6731
6732 var trailer = {
6733 Size: this._offsets.length + 1,
6734 Root: this._root,
6735 Info: this._info,
6736 ID: [this._id, this._id]
6737 };
6738
6739 if (this._security) {
6740 trailer.Encrypt = this._security.dictionary;
6741 }
6742
6743 this._write('trailer');
6744
6745 this._write(PDFObject.convert(trailer));
6746
6747 this._write('startxref');
6748
6749 this._write("".concat(xRefOffset));
6750
6751 this._write('%%EOF'); // end the stream
6752
6753
6754 return this.push(null);
6755 }
6756
6757 toString() {
6758 return '[object PDFDocument]';
6759 }
6760
6761}
6762
6763var mixin = methods => {
6764 Object.assign(PDFDocument.prototype, methods);
6765};
6766
6767mixin(MetadataMixin);
6768mixin(ColorMixin);
6769mixin(VectorMixin);
6770mixin(FontsMixin);
6771mixin(TextMixin);
6772mixin(ImagesMixin);
6773mixin(AnnotationsMixin);
6774mixin(OutlineMixin);
6775mixin(MarkingsMixin);
6776mixin(AcroFormMixin);
6777mixin(AttachmentsMixin);
6778mixin(SubsetMixin);
6779PDFDocument.LineWrapper = LineWrapper;
6780
6781export default PDFDocument;
6782//# sourceMappingURL=pdfkit.esnext.js.map