pdfkit 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -1
- package/README.md +11 -8
- package/js/pdfkit.es5.js +1613 -979
- package/js/pdfkit.es5.js.map +1 -1
- package/js/pdfkit.esnext.js +1676 -1153
- package/js/pdfkit.esnext.js.map +1 -1
- package/js/pdfkit.js +1123 -500
- package/js/pdfkit.js.map +1 -1
- package/js/pdfkit.standalone.js +68105 -0
- package/js/virtual-fs.js +20 -17
- package/package.json +16 -13
- package/.babelrc +0 -8
- package/.github/ISSUE_TEMPLATE/bug-report.md +0 -21
- package/.github/ISSUE_TEMPLATE/feature-request.md +0 -11
- package/.github/ISSUE_TEMPLATE/question.md +0 -20
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -35
- package/.prettierrc +0 -3
- package/CONTRIBUTING.md +0 -83
- package/js/font/data/Courier-Bold.afm +0 -342
- package/js/font/data/Courier-BoldOblique.afm +0 -342
- package/js/font/data/Courier-Oblique.afm +0 -342
- package/js/font/data/Courier.afm +0 -342
- package/js/font/data/Helvetica-Bold.afm +0 -2827
- package/js/font/data/Helvetica-BoldOblique.afm +0 -2827
- package/js/font/data/Helvetica-Oblique.afm +0 -3051
- package/js/font/data/Helvetica.afm +0 -3051
- package/js/font/data/Symbol.afm +0 -213
- package/js/font/data/Times-Bold.afm +0 -2588
- package/js/font/data/Times-BoldItalic.afm +0 -2384
- package/js/font/data/Times-Italic.afm +0 -2667
- package/js/font/data/Times-Roman.afm +0 -2419
- package/js/font/data/ZapfDingbats.afm +0 -225
package/js/pdfkit.js
CHANGED
|
@@ -2,24 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
|
4
4
|
|
|
5
|
+
var stream = _interopDefault(require('stream'));
|
|
6
|
+
var fs = require('fs');
|
|
5
7
|
var zlib = _interopDefault(require('zlib'));
|
|
6
8
|
var CryptoJS = _interopDefault(require('crypto-js'));
|
|
7
|
-
var saslprep = _interopDefault(require('saslprep'));
|
|
8
|
-
var fs = require('fs');
|
|
9
9
|
var fontkit = _interopDefault(require('fontkit'));
|
|
10
10
|
var events = require('events');
|
|
11
11
|
var LineBreaker = _interopDefault(require('linebreak'));
|
|
12
12
|
var PNG = _interopDefault(require('png-js'));
|
|
13
|
-
var stream = _interopDefault(require('stream'));
|
|
14
13
|
|
|
15
14
|
/*
|
|
16
15
|
PDFAbstractReference - abstract class for PDF reference
|
|
17
16
|
*/
|
|
18
|
-
|
|
19
17
|
class PDFAbstractReference {
|
|
20
18
|
toString() {
|
|
21
19
|
throw new Error('Must be implemented by subclasses');
|
|
22
20
|
}
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
PDFNameTree - represents a name tree object
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
class PDFNameTree {
|
|
29
|
+
constructor() {
|
|
30
|
+
this._items = {};
|
|
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
|
+
const sortedKeys = Object.keys(this._items).sort((a, b) => a.localeCompare(b));
|
|
44
|
+
const out = ['<<'];
|
|
45
|
+
|
|
46
|
+
if (sortedKeys.length > 1) {
|
|
47
|
+
const first = sortedKeys[0],
|
|
48
|
+
last = sortedKeys[sortedKeys.length - 1];
|
|
49
|
+
out.push(` /Limits ${PDFObject.convert([new String(first), new String(last)])}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
out.push(' /Names [');
|
|
53
|
+
|
|
54
|
+
for (let key of sortedKeys) {
|
|
55
|
+
out.push(` ${PDFObject.convert(new String(key))} ${PDFObject.convert(this._items[key])}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
out.push(']');
|
|
59
|
+
out.push('>>');
|
|
60
|
+
return out.join('\n');
|
|
61
|
+
}
|
|
62
|
+
|
|
23
63
|
}
|
|
24
64
|
|
|
25
65
|
/*
|
|
@@ -39,11 +79,11 @@ const escapable = {
|
|
|
39
79
|
'\\': '\\\\',
|
|
40
80
|
'(': '\\(',
|
|
41
81
|
')': '\\)'
|
|
42
|
-
};
|
|
82
|
+
}; // Convert little endian UTF-16 to big endian
|
|
43
83
|
|
|
44
|
-
// Convert little endian UTF-16 to big endian
|
|
45
84
|
const swapBytes = function (buff) {
|
|
46
85
|
const l = buff.length;
|
|
86
|
+
|
|
47
87
|
if (l & 0x01) {
|
|
48
88
|
throw new Error('Buffer length must be even');
|
|
49
89
|
} else {
|
|
@@ -61,53 +101,48 @@ class PDFObject {
|
|
|
61
101
|
static convert(object, encryptFn = null) {
|
|
62
102
|
// String literals are converted to the PDF name type
|
|
63
103
|
if (typeof object === 'string') {
|
|
64
|
-
return `/${object}`;
|
|
65
|
-
|
|
66
|
-
// String objects are converted to PDF strings (UTF-16)
|
|
104
|
+
return `/${object}`; // String objects are converted to PDF strings (UTF-16)
|
|
67
105
|
} else if (object instanceof String) {
|
|
68
|
-
let string = object;
|
|
69
|
-
|
|
106
|
+
let string = object; // Detect if this is a unicode string
|
|
107
|
+
|
|
70
108
|
let isUnicode = false;
|
|
109
|
+
|
|
71
110
|
for (let i = 0, end = string.length; i < end; i++) {
|
|
72
111
|
if (string.charCodeAt(i) > 0x7f) {
|
|
73
112
|
isUnicode = true;
|
|
74
113
|
break;
|
|
75
114
|
}
|
|
76
|
-
}
|
|
115
|
+
} // If so, encode it as big endian UTF-16
|
|
116
|
+
|
|
77
117
|
|
|
78
|
-
// If so, encode it as big endian UTF-16
|
|
79
118
|
let stringBuffer;
|
|
119
|
+
|
|
80
120
|
if (isUnicode) {
|
|
81
121
|
stringBuffer = swapBytes(Buffer.from(`\ufeff${string}`, 'utf16le'));
|
|
82
122
|
} else {
|
|
83
123
|
stringBuffer = Buffer.from(string.valueOf(), 'ascii');
|
|
84
|
-
}
|
|
124
|
+
} // Encrypt the string when necessary
|
|
125
|
+
|
|
85
126
|
|
|
86
|
-
// Encrypt the string when necessary
|
|
87
127
|
if (encryptFn) {
|
|
88
128
|
string = encryptFn(stringBuffer).toString('binary');
|
|
89
129
|
} else {
|
|
90
130
|
string = stringBuffer.toString('binary');
|
|
91
|
-
}
|
|
131
|
+
} // Escape characters as required by the spec
|
|
92
132
|
|
|
93
|
-
// Escape characters as required by the spec
|
|
94
|
-
string = string.replace(escapableRe, c => escapable[c]);
|
|
95
133
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// Buffers are converted to PDF hex strings
|
|
134
|
+
string = string.replace(escapableRe, c => escapable[c]);
|
|
135
|
+
return `(${string})`; // Buffers are converted to PDF hex strings
|
|
99
136
|
} else if (Buffer.isBuffer(object)) {
|
|
100
137
|
return `<${object.toString('hex')}>`;
|
|
101
|
-
} else if (object instanceof PDFAbstractReference) {
|
|
138
|
+
} else if (object instanceof PDFAbstractReference || object instanceof PDFNameTree) {
|
|
102
139
|
return object.toString();
|
|
103
140
|
} else if (object instanceof Date) {
|
|
104
|
-
let string = `D:${pad(object.getUTCFullYear(), 4)}` + pad(object.getUTCMonth() + 1, 2) + pad(object.getUTCDate(), 2) + pad(object.getUTCHours(), 2) + pad(object.getUTCMinutes(), 2) + pad(object.getUTCSeconds(), 2) + 'Z';
|
|
141
|
+
let string = `D:${pad(object.getUTCFullYear(), 4)}` + pad(object.getUTCMonth() + 1, 2) + pad(object.getUTCDate(), 2) + pad(object.getUTCHours(), 2) + pad(object.getUTCMinutes(), 2) + pad(object.getUTCSeconds(), 2) + 'Z'; // Encrypt the string when necessary
|
|
105
142
|
|
|
106
|
-
// Encrypt the string when necessary
|
|
107
143
|
if (encryptFn) {
|
|
108
|
-
string = encryptFn(new Buffer(string, 'ascii')).toString('binary');
|
|
144
|
+
string = encryptFn(new Buffer(string, 'ascii')).toString('binary'); // Escape characters as required by the spec
|
|
109
145
|
|
|
110
|
-
// Escape characters as required by the spec
|
|
111
146
|
string = string.replace(escapableRe, c => escapable[c]);
|
|
112
147
|
}
|
|
113
148
|
|
|
@@ -117,6 +152,7 @@ class PDFObject {
|
|
|
117
152
|
return `[${items}]`;
|
|
118
153
|
} else if ({}.toString.call(object) === '[object Object]') {
|
|
119
154
|
const out = ['<<'];
|
|
155
|
+
|
|
120
156
|
for (let key in object) {
|
|
121
157
|
const val = object[key];
|
|
122
158
|
out.push(`/${key} ${PDFObject.convert(val, encryptFn)}`);
|
|
@@ -138,6 +174,7 @@ class PDFObject {
|
|
|
138
174
|
|
|
139
175
|
throw new Error(`unsupported number: ${n}`);
|
|
140
176
|
}
|
|
177
|
+
|
|
141
178
|
}
|
|
142
179
|
|
|
143
180
|
/*
|
|
@@ -146,13 +183,10 @@ By Devon Govett
|
|
|
146
183
|
*/
|
|
147
184
|
|
|
148
185
|
class PDFReference extends PDFAbstractReference {
|
|
149
|
-
constructor(document, id, data) {
|
|
186
|
+
constructor(document, id, data = {}) {
|
|
150
187
|
super();
|
|
151
188
|
this.document = document;
|
|
152
189
|
this.id = id;
|
|
153
|
-
if (data == null) {
|
|
154
|
-
data = {};
|
|
155
|
-
}
|
|
156
190
|
this.data = data;
|
|
157
191
|
this.gen = 0;
|
|
158
192
|
this.compress = this.document.compress && !this.data.Filter;
|
|
@@ -166,11 +200,14 @@ class PDFReference extends PDFAbstractReference {
|
|
|
166
200
|
}
|
|
167
201
|
|
|
168
202
|
this.uncompressedLength += chunk.length;
|
|
203
|
+
|
|
169
204
|
if (this.data.Length == null) {
|
|
170
205
|
this.data.Length = 0;
|
|
171
206
|
}
|
|
207
|
+
|
|
172
208
|
this.buffer.push(chunk);
|
|
173
209
|
this.data.Length += chunk.length;
|
|
210
|
+
|
|
174
211
|
if (this.compress) {
|
|
175
212
|
return this.data.Filter = 'FlateDecode';
|
|
176
213
|
}
|
|
@@ -180,16 +217,17 @@ class PDFReference extends PDFAbstractReference {
|
|
|
180
217
|
if (chunk) {
|
|
181
218
|
this.write(chunk);
|
|
182
219
|
}
|
|
220
|
+
|
|
183
221
|
return this.finalize();
|
|
184
222
|
}
|
|
185
223
|
|
|
186
224
|
finalize() {
|
|
187
225
|
this.offset = this.document._offset;
|
|
188
|
-
|
|
189
226
|
const encryptFn = this.document._security ? this.document._security.getEncryptFn(this.id, this.gen) : null;
|
|
190
227
|
|
|
191
228
|
if (this.buffer.length) {
|
|
192
229
|
this.buffer = Buffer.concat(this.buffer);
|
|
230
|
+
|
|
193
231
|
if (this.compress) {
|
|
194
232
|
this.buffer = zlib.deflateSync(this.buffer);
|
|
195
233
|
}
|
|
@@ -202,36 +240,40 @@ class PDFReference extends PDFAbstractReference {
|
|
|
202
240
|
}
|
|
203
241
|
|
|
204
242
|
this.document._write(`${this.id} ${this.gen} obj`);
|
|
243
|
+
|
|
205
244
|
this.document._write(PDFObject.convert(this.data, encryptFn));
|
|
206
245
|
|
|
207
246
|
if (this.buffer.length) {
|
|
208
247
|
this.document._write('stream');
|
|
248
|
+
|
|
209
249
|
this.document._write(this.buffer);
|
|
210
250
|
|
|
211
251
|
this.buffer = []; // free up memory
|
|
252
|
+
|
|
212
253
|
this.document._write('\nendstream');
|
|
213
254
|
}
|
|
214
255
|
|
|
215
256
|
this.document._write('endobj');
|
|
257
|
+
|
|
216
258
|
this.document._refEnd(this);
|
|
217
259
|
}
|
|
260
|
+
|
|
218
261
|
toString() {
|
|
219
262
|
return `${this.id} ${this.gen} R`;
|
|
220
263
|
}
|
|
264
|
+
|
|
221
265
|
}
|
|
222
266
|
|
|
223
267
|
/*
|
|
224
268
|
PDFPage - represents a single page in the PDF document
|
|
225
269
|
By Devon Govett
|
|
226
270
|
*/
|
|
227
|
-
|
|
228
271
|
const DEFAULT_MARGINS = {
|
|
229
272
|
top: 72,
|
|
230
273
|
left: 72,
|
|
231
274
|
bottom: 72,
|
|
232
275
|
right: 72
|
|
233
276
|
};
|
|
234
|
-
|
|
235
277
|
const SIZES = {
|
|
236
278
|
'4A0': [4767.87, 6740.79],
|
|
237
279
|
'2A0': [3370.39, 4767.87],
|
|
@@ -286,41 +328,32 @@ const SIZES = {
|
|
|
286
328
|
};
|
|
287
329
|
|
|
288
330
|
class PDFPage {
|
|
289
|
-
constructor(document, options) {
|
|
331
|
+
constructor(document, options = {}) {
|
|
290
332
|
this.document = document;
|
|
291
|
-
if (options == null) {
|
|
292
|
-
options = {};
|
|
293
|
-
}
|
|
294
333
|
this.size = options.size || 'letter';
|
|
295
|
-
this.layout = options.layout || 'portrait';
|
|
334
|
+
this.layout = options.layout || 'portrait'; // process margins
|
|
296
335
|
|
|
297
|
-
// process margins
|
|
298
336
|
if (typeof options.margin === 'number') {
|
|
299
337
|
this.margins = {
|
|
300
338
|
top: options.margin,
|
|
301
339
|
left: options.margin,
|
|
302
340
|
bottom: options.margin,
|
|
303
341
|
right: options.margin
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
// default to 1 inch margins
|
|
342
|
+
}; // default to 1 inch margins
|
|
307
343
|
} else {
|
|
308
344
|
this.margins = options.margins || DEFAULT_MARGINS;
|
|
309
|
-
}
|
|
345
|
+
} // calculate page dimensions
|
|
346
|
+
|
|
310
347
|
|
|
311
|
-
// calculate page dimensions
|
|
312
348
|
const dimensions = Array.isArray(this.size) ? this.size : SIZES[this.size.toUpperCase()];
|
|
313
349
|
this.width = dimensions[this.layout === 'portrait' ? 0 : 1];
|
|
314
350
|
this.height = dimensions[this.layout === 'portrait' ? 1 : 0];
|
|
351
|
+
this.content = this.document.ref(); // Initialize the Font, XObject, and ExtGState dictionaries
|
|
315
352
|
|
|
316
|
-
this.content = this.document.ref();
|
|
317
|
-
|
|
318
|
-
// Initialize the Font, XObject, and ExtGState dictionaries
|
|
319
353
|
this.resources = this.document.ref({
|
|
320
354
|
ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI']
|
|
321
|
-
});
|
|
355
|
+
}); // The page dictionary
|
|
322
356
|
|
|
323
|
-
// The page dictionary
|
|
324
357
|
this.dictionary = this.document.ref({
|
|
325
358
|
Type: 'Page',
|
|
326
359
|
Parent: this.document._root.data.Pages,
|
|
@@ -328,9 +361,9 @@ class PDFPage {
|
|
|
328
361
|
Contents: this.content,
|
|
329
362
|
Resources: this.resources
|
|
330
363
|
});
|
|
331
|
-
}
|
|
364
|
+
} // Lazily create these dictionaries
|
|
365
|
+
|
|
332
366
|
|
|
333
|
-
// Lazily create these dictionaries
|
|
334
367
|
get fonts() {
|
|
335
368
|
const data = this.resources.data;
|
|
336
369
|
return data.Font != null ? data.Font : data.Font = {};
|
|
@@ -369,6 +402,433 @@ class PDFPage {
|
|
|
369
402
|
this.resources.end();
|
|
370
403
|
return this.content.end();
|
|
371
404
|
}
|
|
405
|
+
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Check if value is in a range group.
|
|
410
|
+
* @param {number} value
|
|
411
|
+
* @param {number[]} rangeGroup
|
|
412
|
+
* @returns {boolean}
|
|
413
|
+
*/
|
|
414
|
+
function inRange(value, rangeGroup) {
|
|
415
|
+
if (value < rangeGroup[0]) return false;
|
|
416
|
+
let startRange = 0;
|
|
417
|
+
let endRange = rangeGroup.length / 2;
|
|
418
|
+
|
|
419
|
+
while (startRange <= endRange) {
|
|
420
|
+
const middleRange = Math.floor((startRange + endRange) / 2); // actual array index
|
|
421
|
+
|
|
422
|
+
const arrayIndex = middleRange * 2; // Check if value is in range pointed by actual index
|
|
423
|
+
|
|
424
|
+
if (value >= rangeGroup[arrayIndex] && value <= rangeGroup[arrayIndex + 1]) {
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (value > rangeGroup[arrayIndex + 1]) {
|
|
429
|
+
// Search Right Side Of Array
|
|
430
|
+
startRange = middleRange + 1;
|
|
431
|
+
} else {
|
|
432
|
+
// Search Left Side Of Array
|
|
433
|
+
endRange = middleRange - 1;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/* eslint-disable prettier/prettier */
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* A.1 Unassigned code points in Unicode 3.2
|
|
444
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-A.1
|
|
445
|
+
*/
|
|
446
|
+
|
|
447
|
+
const unassigned_code_points = [0x0221, 0x0221, 0x0234, 0x024f, 0x02ae, 0x02af, 0x02ef, 0x02ff, 0x0350, 0x035f, 0x0370, 0x0373, 0x0376, 0x0379, 0x037b, 0x037d, 0x037f, 0x0383, 0x038b, 0x038b, 0x038d, 0x038d, 0x03a2, 0x03a2, 0x03cf, 0x03cf, 0x03f7, 0x03ff, 0x0487, 0x0487, 0x04cf, 0x04cf, 0x04f6, 0x04f7, 0x04fa, 0x04ff, 0x0510, 0x0530, 0x0557, 0x0558, 0x0560, 0x0560, 0x0588, 0x0588, 0x058b, 0x0590, 0x05a2, 0x05a2, 0x05ba, 0x05ba, 0x05c5, 0x05cf, 0x05eb, 0x05ef, 0x05f5, 0x060b, 0x060d, 0x061a, 0x061c, 0x061e, 0x0620, 0x0620, 0x063b, 0x063f, 0x0656, 0x065f, 0x06ee, 0x06ef, 0x06ff, 0x06ff, 0x070e, 0x070e, 0x072d, 0x072f, 0x074b, 0x077f, 0x07b2, 0x0900, 0x0904, 0x0904, 0x093a, 0x093b, 0x094e, 0x094f, 0x0955, 0x0957, 0x0971, 0x0980, 0x0984, 0x0984, 0x098d, 0x098e, 0x0991, 0x0992, 0x09a9, 0x09a9, 0x09b1, 0x09b1, 0x09b3, 0x09b5, 0x09ba, 0x09bb, 0x09bd, 0x09bd, 0x09c5, 0x09c6, 0x09c9, 0x09ca, 0x09ce, 0x09d6, 0x09d8, 0x09db, 0x09de, 0x09de, 0x09e4, 0x09e5, 0x09fb, 0x0a01, 0x0a03, 0x0a04, 0x0a0b, 0x0a0e, 0x0a11, 0x0a12, 0x0a29, 0x0a29, 0x0a31, 0x0a31, 0x0a34, 0x0a34, 0x0a37, 0x0a37, 0x0a3a, 0x0a3b, 0x0a3d, 0x0a3d, 0x0a43, 0x0a46, 0x0a49, 0x0a4a, 0x0a4e, 0x0a58, 0x0a5d, 0x0a5d, 0x0a5f, 0x0a65, 0x0a75, 0x0a80, 0x0a84, 0x0a84, 0x0a8c, 0x0a8c, 0x0a8e, 0x0a8e, 0x0a92, 0x0a92, 0x0aa9, 0x0aa9, 0x0ab1, 0x0ab1, 0x0ab4, 0x0ab4, 0x0aba, 0x0abb, 0x0ac6, 0x0ac6, 0x0aca, 0x0aca, 0x0ace, 0x0acf, 0x0ad1, 0x0adf, 0x0ae1, 0x0ae5, 0x0af0, 0x0b00, 0x0b04, 0x0b04, 0x0b0d, 0x0b0e, 0x0b11, 0x0b12, 0x0b29, 0x0b29, 0x0b31, 0x0b31, 0x0b34, 0x0b35, 0x0b3a, 0x0b3b, 0x0b44, 0x0b46, 0x0b49, 0x0b4a, 0x0b4e, 0x0b55, 0x0b58, 0x0b5b, 0x0b5e, 0x0b5e, 0x0b62, 0x0b65, 0x0b71, 0x0b81, 0x0b84, 0x0b84, 0x0b8b, 0x0b8d, 0x0b91, 0x0b91, 0x0b96, 0x0b98, 0x0b9b, 0x0b9b, 0x0b9d, 0x0b9d, 0x0ba0, 0x0ba2, 0x0ba5, 0x0ba7, 0x0bab, 0x0bad, 0x0bb6, 0x0bb6, 0x0bba, 0x0bbd, 0x0bc3, 0x0bc5, 0x0bc9, 0x0bc9, 0x0bce, 0x0bd6, 0x0bd8, 0x0be6, 0x0bf3, 0x0c00, 0x0c04, 0x0c04, 0x0c0d, 0x0c0d, 0x0c11, 0x0c11, 0x0c29, 0x0c29, 0x0c34, 0x0c34, 0x0c3a, 0x0c3d, 0x0c45, 0x0c45, 0x0c49, 0x0c49, 0x0c4e, 0x0c54, 0x0c57, 0x0c5f, 0x0c62, 0x0c65, 0x0c70, 0x0c81, 0x0c84, 0x0c84, 0x0c8d, 0x0c8d, 0x0c91, 0x0c91, 0x0ca9, 0x0ca9, 0x0cb4, 0x0cb4, 0x0cba, 0x0cbd, 0x0cc5, 0x0cc5, 0x0cc9, 0x0cc9, 0x0cce, 0x0cd4, 0x0cd7, 0x0cdd, 0x0cdf, 0x0cdf, 0x0ce2, 0x0ce5, 0x0cf0, 0x0d01, 0x0d04, 0x0d04, 0x0d0d, 0x0d0d, 0x0d11, 0x0d11, 0x0d29, 0x0d29, 0x0d3a, 0x0d3d, 0x0d44, 0x0d45, 0x0d49, 0x0d49, 0x0d4e, 0x0d56, 0x0d58, 0x0d5f, 0x0d62, 0x0d65, 0x0d70, 0x0d81, 0x0d84, 0x0d84, 0x0d97, 0x0d99, 0x0db2, 0x0db2, 0x0dbc, 0x0dbc, 0x0dbe, 0x0dbf, 0x0dc7, 0x0dc9, 0x0dcb, 0x0dce, 0x0dd5, 0x0dd5, 0x0dd7, 0x0dd7, 0x0de0, 0x0df1, 0x0df5, 0x0e00, 0x0e3b, 0x0e3e, 0x0e5c, 0x0e80, 0x0e83, 0x0e83, 0x0e85, 0x0e86, 0x0e89, 0x0e89, 0x0e8b, 0x0e8c, 0x0e8e, 0x0e93, 0x0e98, 0x0e98, 0x0ea0, 0x0ea0, 0x0ea4, 0x0ea4, 0x0ea6, 0x0ea6, 0x0ea8, 0x0ea9, 0x0eac, 0x0eac, 0x0eba, 0x0eba, 0x0ebe, 0x0ebf, 0x0ec5, 0x0ec5, 0x0ec7, 0x0ec7, 0x0ece, 0x0ecf, 0x0eda, 0x0edb, 0x0ede, 0x0eff, 0x0f48, 0x0f48, 0x0f6b, 0x0f70, 0x0f8c, 0x0f8f, 0x0f98, 0x0f98, 0x0fbd, 0x0fbd, 0x0fcd, 0x0fce, 0x0fd0, 0x0fff, 0x1022, 0x1022, 0x1028, 0x1028, 0x102b, 0x102b, 0x1033, 0x1035, 0x103a, 0x103f, 0x105a, 0x109f, 0x10c6, 0x10cf, 0x10f9, 0x10fa, 0x10fc, 0x10ff, 0x115a, 0x115e, 0x11a3, 0x11a7, 0x11fa, 0x11ff, 0x1207, 0x1207, 0x1247, 0x1247, 0x1249, 0x1249, 0x124e, 0x124f, 0x1257, 0x1257, 0x1259, 0x1259, 0x125e, 0x125f, 0x1287, 0x1287, 0x1289, 0x1289, 0x128e, 0x128f, 0x12af, 0x12af, 0x12b1, 0x12b1, 0x12b6, 0x12b7, 0x12bf, 0x12bf, 0x12c1, 0x12c1, 0x12c6, 0x12c7, 0x12cf, 0x12cf, 0x12d7, 0x12d7, 0x12ef, 0x12ef, 0x130f, 0x130f, 0x1311, 0x1311, 0x1316, 0x1317, 0x131f, 0x131f, 0x1347, 0x1347, 0x135b, 0x1360, 0x137d, 0x139f, 0x13f5, 0x1400, 0x1677, 0x167f, 0x169d, 0x169f, 0x16f1, 0x16ff, 0x170d, 0x170d, 0x1715, 0x171f, 0x1737, 0x173f, 0x1754, 0x175f, 0x176d, 0x176d, 0x1771, 0x1771, 0x1774, 0x177f, 0x17dd, 0x17df, 0x17ea, 0x17ff, 0x180f, 0x180f, 0x181a, 0x181f, 0x1878, 0x187f, 0x18aa, 0x1dff, 0x1e9c, 0x1e9f, 0x1efa, 0x1eff, 0x1f16, 0x1f17, 0x1f1e, 0x1f1f, 0x1f46, 0x1f47, 0x1f4e, 0x1f4f, 0x1f58, 0x1f58, 0x1f5a, 0x1f5a, 0x1f5c, 0x1f5c, 0x1f5e, 0x1f5e, 0x1f7e, 0x1f7f, 0x1fb5, 0x1fb5, 0x1fc5, 0x1fc5, 0x1fd4, 0x1fd5, 0x1fdc, 0x1fdc, 0x1ff0, 0x1ff1, 0x1ff5, 0x1ff5, 0x1fff, 0x1fff, 0x2053, 0x2056, 0x2058, 0x205e, 0x2064, 0x2069, 0x2072, 0x2073, 0x208f, 0x209f, 0x20b2, 0x20cf, 0x20eb, 0x20ff, 0x213b, 0x213c, 0x214c, 0x2152, 0x2184, 0x218f, 0x23cf, 0x23ff, 0x2427, 0x243f, 0x244b, 0x245f, 0x24ff, 0x24ff, 0x2614, 0x2615, 0x2618, 0x2618, 0x267e, 0x267f, 0x268a, 0x2700, 0x2705, 0x2705, 0x270a, 0x270b, 0x2728, 0x2728, 0x274c, 0x274c, 0x274e, 0x274e, 0x2753, 0x2755, 0x2757, 0x2757, 0x275f, 0x2760, 0x2795, 0x2797, 0x27b0, 0x27b0, 0x27bf, 0x27cf, 0x27ec, 0x27ef, 0x2b00, 0x2e7f, 0x2e9a, 0x2e9a, 0x2ef4, 0x2eff, 0x2fd6, 0x2fef, 0x2ffc, 0x2fff, 0x3040, 0x3040, 0x3097, 0x3098, 0x3100, 0x3104, 0x312d, 0x3130, 0x318f, 0x318f, 0x31b8, 0x31ef, 0x321d, 0x321f, 0x3244, 0x3250, 0x327c, 0x327e, 0x32cc, 0x32cf, 0x32ff, 0x32ff, 0x3377, 0x337a, 0x33de, 0x33df, 0x33ff, 0x33ff, 0x4db6, 0x4dff, 0x9fa6, 0x9fff, 0xa48d, 0xa48f, 0xa4c7, 0xabff, 0xd7a4, 0xd7ff, 0xfa2e, 0xfa2f, 0xfa6b, 0xfaff, 0xfb07, 0xfb12, 0xfb18, 0xfb1c, 0xfb37, 0xfb37, 0xfb3d, 0xfb3d, 0xfb3f, 0xfb3f, 0xfb42, 0xfb42, 0xfb45, 0xfb45, 0xfbb2, 0xfbd2, 0xfd40, 0xfd4f, 0xfd90, 0xfd91, 0xfdc8, 0xfdcf, 0xfdfd, 0xfdff, 0xfe10, 0xfe1f, 0xfe24, 0xfe2f, 0xfe47, 0xfe48, 0xfe53, 0xfe53, 0xfe67, 0xfe67, 0xfe6c, 0xfe6f, 0xfe75, 0xfe75, 0xfefd, 0xfefe, 0xff00, 0xff00, 0xffbf, 0xffc1, 0xffc8, 0xffc9, 0xffd0, 0xffd1, 0xffd8, 0xffd9, 0xffdd, 0xffdf, 0xffe7, 0xffe7, 0xffef, 0xfff8, 0x10000, 0x102ff, 0x1031f, 0x1031f, 0x10324, 0x1032f, 0x1034b, 0x103ff, 0x10426, 0x10427, 0x1044e, 0x1cfff, 0x1d0f6, 0x1d0ff, 0x1d127, 0x1d129, 0x1d1de, 0x1d3ff, 0x1d455, 0x1d455, 0x1d49d, 0x1d49d, 0x1d4a0, 0x1d4a1, 0x1d4a3, 0x1d4a4, 0x1d4a7, 0x1d4a8, 0x1d4ad, 0x1d4ad, 0x1d4ba, 0x1d4ba, 0x1d4bc, 0x1d4bc, 0x1d4c1, 0x1d4c1, 0x1d4c4, 0x1d4c4, 0x1d506, 0x1d506, 0x1d50b, 0x1d50c, 0x1d515, 0x1d515, 0x1d51d, 0x1d51d, 0x1d53a, 0x1d53a, 0x1d53f, 0x1d53f, 0x1d545, 0x1d545, 0x1d547, 0x1d549, 0x1d551, 0x1d551, 0x1d6a4, 0x1d6a7, 0x1d7ca, 0x1d7cd, 0x1d800, 0x1fffd, 0x2a6d7, 0x2f7ff, 0x2fa1e, 0x2fffd, 0x30000, 0x3fffd, 0x40000, 0x4fffd, 0x50000, 0x5fffd, 0x60000, 0x6fffd, 0x70000, 0x7fffd, 0x80000, 0x8fffd, 0x90000, 0x9fffd, 0xa0000, 0xafffd, 0xb0000, 0xbfffd, 0xc0000, 0xcfffd, 0xd0000, 0xdfffd, 0xe0000, 0xe0000, 0xe0002, 0xe001f, 0xe0080, 0xefffd];
|
|
448
|
+
/* eslint-enable */
|
|
449
|
+
|
|
450
|
+
const isUnassignedCodePoint = character => inRange(character, unassigned_code_points);
|
|
451
|
+
/* eslint-disable prettier/prettier */
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* B.1 Commonly mapped to nothing
|
|
455
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-B.1
|
|
456
|
+
*/
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
const commonly_mapped_to_nothing = [0x00ad, 0x00ad, 0x034f, 0x034f, 0x1806, 0x1806, 0x180b, 0x180b, 0x180c, 0x180c, 0x180d, 0x180d, 0x200b, 0x200b, 0x200c, 0x200c, 0x200d, 0x200d, 0x2060, 0x2060, 0xfe00, 0xfe00, 0xfe01, 0xfe01, 0xfe02, 0xfe02, 0xfe03, 0xfe03, 0xfe04, 0xfe04, 0xfe05, 0xfe05, 0xfe06, 0xfe06, 0xfe07, 0xfe07, 0xfe08, 0xfe08, 0xfe09, 0xfe09, 0xfe0a, 0xfe0a, 0xfe0b, 0xfe0b, 0xfe0c, 0xfe0c, 0xfe0d, 0xfe0d, 0xfe0e, 0xfe0e, 0xfe0f, 0xfe0f, 0xfeff, 0xfeff];
|
|
460
|
+
/* eslint-enable */
|
|
461
|
+
|
|
462
|
+
const isCommonlyMappedToNothing = character => inRange(character, commonly_mapped_to_nothing);
|
|
463
|
+
/* eslint-disable prettier/prettier */
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* C.1.2 Non-ASCII space characters
|
|
467
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-C.1.2
|
|
468
|
+
*/
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
const non_ASCII_space_characters = [0x00a0, 0x00a0
|
|
472
|
+
/* NO-BREAK SPACE */
|
|
473
|
+
, 0x1680, 0x1680
|
|
474
|
+
/* OGHAM SPACE MARK */
|
|
475
|
+
, 0x2000, 0x2000
|
|
476
|
+
/* EN QUAD */
|
|
477
|
+
, 0x2001, 0x2001
|
|
478
|
+
/* EM QUAD */
|
|
479
|
+
, 0x2002, 0x2002
|
|
480
|
+
/* EN SPACE */
|
|
481
|
+
, 0x2003, 0x2003
|
|
482
|
+
/* EM SPACE */
|
|
483
|
+
, 0x2004, 0x2004
|
|
484
|
+
/* THREE-PER-EM SPACE */
|
|
485
|
+
, 0x2005, 0x2005
|
|
486
|
+
/* FOUR-PER-EM SPACE */
|
|
487
|
+
, 0x2006, 0x2006
|
|
488
|
+
/* SIX-PER-EM SPACE */
|
|
489
|
+
, 0x2007, 0x2007
|
|
490
|
+
/* FIGURE SPACE */
|
|
491
|
+
, 0x2008, 0x2008
|
|
492
|
+
/* PUNCTUATION SPACE */
|
|
493
|
+
, 0x2009, 0x2009
|
|
494
|
+
/* THIN SPACE */
|
|
495
|
+
, 0x200a, 0x200a
|
|
496
|
+
/* HAIR SPACE */
|
|
497
|
+
, 0x200b, 0x200b
|
|
498
|
+
/* ZERO WIDTH SPACE */
|
|
499
|
+
, 0x202f, 0x202f
|
|
500
|
+
/* NARROW NO-BREAK SPACE */
|
|
501
|
+
, 0x205f, 0x205f
|
|
502
|
+
/* MEDIUM MATHEMATICAL SPACE */
|
|
503
|
+
, 0x3000, 0x3000
|
|
504
|
+
/* IDEOGRAPHIC SPACE */
|
|
505
|
+
];
|
|
506
|
+
/* eslint-enable */
|
|
507
|
+
|
|
508
|
+
const isNonASCIISpaceCharacter = character => inRange(character, non_ASCII_space_characters);
|
|
509
|
+
/* eslint-disable prettier/prettier */
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
const non_ASCII_controls_characters = [
|
|
513
|
+
/**
|
|
514
|
+
* C.2.2 Non-ASCII control characters
|
|
515
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-C.2.2
|
|
516
|
+
*/
|
|
517
|
+
0x0080, 0x009f
|
|
518
|
+
/* [CONTROL CHARACTERS] */
|
|
519
|
+
, 0x06dd, 0x06dd
|
|
520
|
+
/* ARABIC END OF AYAH */
|
|
521
|
+
, 0x070f, 0x070f
|
|
522
|
+
/* SYRIAC ABBREVIATION MARK */
|
|
523
|
+
, 0x180e, 0x180e
|
|
524
|
+
/* MONGOLIAN VOWEL SEPARATOR */
|
|
525
|
+
, 0x200c, 0x200c
|
|
526
|
+
/* ZERO WIDTH NON-JOINER */
|
|
527
|
+
, 0x200d, 0x200d
|
|
528
|
+
/* ZERO WIDTH JOINER */
|
|
529
|
+
, 0x2028, 0x2028
|
|
530
|
+
/* LINE SEPARATOR */
|
|
531
|
+
, 0x2029, 0x2029
|
|
532
|
+
/* PARAGRAPH SEPARATOR */
|
|
533
|
+
, 0x2060, 0x2060
|
|
534
|
+
/* WORD JOINER */
|
|
535
|
+
, 0x2061, 0x2061
|
|
536
|
+
/* FUNCTION APPLICATION */
|
|
537
|
+
, 0x2062, 0x2062
|
|
538
|
+
/* INVISIBLE TIMES */
|
|
539
|
+
, 0x2063, 0x2063
|
|
540
|
+
/* INVISIBLE SEPARATOR */
|
|
541
|
+
, 0x206a, 0x206f
|
|
542
|
+
/* [CONTROL CHARACTERS] */
|
|
543
|
+
, 0xfeff, 0xfeff
|
|
544
|
+
/* ZERO WIDTH NO-BREAK SPACE */
|
|
545
|
+
, 0xfff9, 0xfffc
|
|
546
|
+
/* [CONTROL CHARACTERS] */
|
|
547
|
+
, 0x1d173, 0x1d17a
|
|
548
|
+
/* [MUSICAL CONTROL CHARACTERS] */
|
|
549
|
+
];
|
|
550
|
+
const non_character_codepoints = [
|
|
551
|
+
/**
|
|
552
|
+
* C.4 Non-character code points
|
|
553
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-C.4
|
|
554
|
+
*/
|
|
555
|
+
0xfdd0, 0xfdef
|
|
556
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
557
|
+
, 0xfffe, 0xffff
|
|
558
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
559
|
+
, 0x1fffe, 0x1ffff
|
|
560
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
561
|
+
, 0x2fffe, 0x2ffff
|
|
562
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
563
|
+
, 0x3fffe, 0x3ffff
|
|
564
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
565
|
+
, 0x4fffe, 0x4ffff
|
|
566
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
567
|
+
, 0x5fffe, 0x5ffff
|
|
568
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
569
|
+
, 0x6fffe, 0x6ffff
|
|
570
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
571
|
+
, 0x7fffe, 0x7ffff
|
|
572
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
573
|
+
, 0x8fffe, 0x8ffff
|
|
574
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
575
|
+
, 0x9fffe, 0x9ffff
|
|
576
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
577
|
+
, 0xafffe, 0xaffff
|
|
578
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
579
|
+
, 0xbfffe, 0xbffff
|
|
580
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
581
|
+
, 0xcfffe, 0xcffff
|
|
582
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
583
|
+
, 0xdfffe, 0xdffff
|
|
584
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
585
|
+
, 0xefffe, 0xeffff
|
|
586
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
587
|
+
, 0x10fffe, 0x10ffff
|
|
588
|
+
/* [NONCHARACTER CODE POINTS] */
|
|
589
|
+
];
|
|
590
|
+
/**
|
|
591
|
+
* 2.3. Prohibited Output
|
|
592
|
+
*/
|
|
593
|
+
|
|
594
|
+
const prohibited_characters = [
|
|
595
|
+
/**
|
|
596
|
+
* C.2.1 ASCII control characters
|
|
597
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-C.2.1
|
|
598
|
+
*/
|
|
599
|
+
0, 0x001f
|
|
600
|
+
/* [CONTROL CHARACTERS] */
|
|
601
|
+
, 0x007f, 0x007f
|
|
602
|
+
/* DELETE */
|
|
603
|
+
,
|
|
604
|
+
/**
|
|
605
|
+
* C.8 Change display properties or are deprecated
|
|
606
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-C.8
|
|
607
|
+
*/
|
|
608
|
+
0x0340, 0x0340
|
|
609
|
+
/* COMBINING GRAVE TONE MARK */
|
|
610
|
+
, 0x0341, 0x0341
|
|
611
|
+
/* COMBINING ACUTE TONE MARK */
|
|
612
|
+
, 0x200e, 0x200e
|
|
613
|
+
/* LEFT-TO-RIGHT MARK */
|
|
614
|
+
, 0x200f, 0x200f
|
|
615
|
+
/* RIGHT-TO-LEFT MARK */
|
|
616
|
+
, 0x202a, 0x202a
|
|
617
|
+
/* LEFT-TO-RIGHT EMBEDDING */
|
|
618
|
+
, 0x202b, 0x202b
|
|
619
|
+
/* RIGHT-TO-LEFT EMBEDDING */
|
|
620
|
+
, 0x202c, 0x202c
|
|
621
|
+
/* POP DIRECTIONAL FORMATTING */
|
|
622
|
+
, 0x202d, 0x202d
|
|
623
|
+
/* LEFT-TO-RIGHT OVERRIDE */
|
|
624
|
+
, 0x202e, 0x202e
|
|
625
|
+
/* RIGHT-TO-LEFT OVERRIDE */
|
|
626
|
+
, 0x206a, 0x206a
|
|
627
|
+
/* INHIBIT SYMMETRIC SWAPPING */
|
|
628
|
+
, 0x206b, 0x206b
|
|
629
|
+
/* ACTIVATE SYMMETRIC SWAPPING */
|
|
630
|
+
, 0x206c, 0x206c
|
|
631
|
+
/* INHIBIT ARABIC FORM SHAPING */
|
|
632
|
+
, 0x206d, 0x206d
|
|
633
|
+
/* ACTIVATE ARABIC FORM SHAPING */
|
|
634
|
+
, 0x206e, 0x206e
|
|
635
|
+
/* NATIONAL DIGIT SHAPES */
|
|
636
|
+
, 0x206f, 0x206f
|
|
637
|
+
/* NOMINAL DIGIT SHAPES */
|
|
638
|
+
,
|
|
639
|
+
/**
|
|
640
|
+
* C.7 Inappropriate for canonical representation
|
|
641
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-C.7
|
|
642
|
+
*/
|
|
643
|
+
0x2ff0, 0x2ffb
|
|
644
|
+
/* [IDEOGRAPHIC DESCRIPTION CHARACTERS] */
|
|
645
|
+
,
|
|
646
|
+
/**
|
|
647
|
+
* C.5 Surrogate codes
|
|
648
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-C.5
|
|
649
|
+
*/
|
|
650
|
+
0xd800, 0xdfff,
|
|
651
|
+
/**
|
|
652
|
+
* C.3 Private use
|
|
653
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-C.3
|
|
654
|
+
*/
|
|
655
|
+
0xe000, 0xf8ff
|
|
656
|
+
/* [PRIVATE USE, PLANE 0] */
|
|
657
|
+
,
|
|
658
|
+
/**
|
|
659
|
+
* C.6 Inappropriate for plain text
|
|
660
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-C.6
|
|
661
|
+
*/
|
|
662
|
+
0xfff9, 0xfff9
|
|
663
|
+
/* INTERLINEAR ANNOTATION ANCHOR */
|
|
664
|
+
, 0xfffa, 0xfffa
|
|
665
|
+
/* INTERLINEAR ANNOTATION SEPARATOR */
|
|
666
|
+
, 0xfffb, 0xfffb
|
|
667
|
+
/* INTERLINEAR ANNOTATION TERMINATOR */
|
|
668
|
+
, 0xfffc, 0xfffc
|
|
669
|
+
/* OBJECT REPLACEMENT CHARACTER */
|
|
670
|
+
, 0xfffd, 0xfffd
|
|
671
|
+
/* REPLACEMENT CHARACTER */
|
|
672
|
+
,
|
|
673
|
+
/**
|
|
674
|
+
* C.9 Tagging characters
|
|
675
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-C.9
|
|
676
|
+
*/
|
|
677
|
+
0xe0001, 0xe0001
|
|
678
|
+
/* LANGUAGE TAG */
|
|
679
|
+
, 0xe0020, 0xe007f
|
|
680
|
+
/* [TAGGING CHARACTERS] */
|
|
681
|
+
,
|
|
682
|
+
/**
|
|
683
|
+
* C.3 Private use
|
|
684
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-C.3
|
|
685
|
+
*/
|
|
686
|
+
0xf0000, 0xffffd
|
|
687
|
+
/* [PRIVATE USE, PLANE 15] */
|
|
688
|
+
, 0x100000, 0x10fffd
|
|
689
|
+
/* [PRIVATE USE, PLANE 16] */
|
|
690
|
+
];
|
|
691
|
+
/* eslint-enable */
|
|
692
|
+
|
|
693
|
+
const isProhibitedCharacter = character => inRange(character, non_ASCII_space_characters) || inRange(character, prohibited_characters) || inRange(character, non_ASCII_controls_characters) || inRange(character, non_character_codepoints);
|
|
694
|
+
/* eslint-disable prettier/prettier */
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* D.1 Characters with bidirectional property "R" or "AL"
|
|
698
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-D.1
|
|
699
|
+
*/
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
const bidirectional_r_al = [0x05be, 0x05be, 0x05c0, 0x05c0, 0x05c3, 0x05c3, 0x05d0, 0x05ea, 0x05f0, 0x05f4, 0x061b, 0x061b, 0x061f, 0x061f, 0x0621, 0x063a, 0x0640, 0x064a, 0x066d, 0x066f, 0x0671, 0x06d5, 0x06dd, 0x06dd, 0x06e5, 0x06e6, 0x06fa, 0x06fe, 0x0700, 0x070d, 0x0710, 0x0710, 0x0712, 0x072c, 0x0780, 0x07a5, 0x07b1, 0x07b1, 0x200f, 0x200f, 0xfb1d, 0xfb1d, 0xfb1f, 0xfb28, 0xfb2a, 0xfb36, 0xfb38, 0xfb3c, 0xfb3e, 0xfb3e, 0xfb40, 0xfb41, 0xfb43, 0xfb44, 0xfb46, 0xfbb1, 0xfbd3, 0xfd3d, 0xfd50, 0xfd8f, 0xfd92, 0xfdc7, 0xfdf0, 0xfdfc, 0xfe70, 0xfe74, 0xfe76, 0xfefc];
|
|
703
|
+
/* eslint-enable */
|
|
704
|
+
|
|
705
|
+
const isBidirectionalRAL = character => inRange(character, bidirectional_r_al);
|
|
706
|
+
/* eslint-disable prettier/prettier */
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* D.2 Characters with bidirectional property "L"
|
|
710
|
+
* @link https://tools.ietf.org/html/rfc3454#appendix-D.2
|
|
711
|
+
*/
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
const bidirectional_l = [0x0041, 0x005a, 0x0061, 0x007a, 0x00aa, 0x00aa, 0x00b5, 0x00b5, 0x00ba, 0x00ba, 0x00c0, 0x00d6, 0x00d8, 0x00f6, 0x00f8, 0x0220, 0x0222, 0x0233, 0x0250, 0x02ad, 0x02b0, 0x02b8, 0x02bb, 0x02c1, 0x02d0, 0x02d1, 0x02e0, 0x02e4, 0x02ee, 0x02ee, 0x037a, 0x037a, 0x0386, 0x0386, 0x0388, 0x038a, 0x038c, 0x038c, 0x038e, 0x03a1, 0x03a3, 0x03ce, 0x03d0, 0x03f5, 0x0400, 0x0482, 0x048a, 0x04ce, 0x04d0, 0x04f5, 0x04f8, 0x04f9, 0x0500, 0x050f, 0x0531, 0x0556, 0x0559, 0x055f, 0x0561, 0x0587, 0x0589, 0x0589, 0x0903, 0x0903, 0x0905, 0x0939, 0x093d, 0x0940, 0x0949, 0x094c, 0x0950, 0x0950, 0x0958, 0x0961, 0x0964, 0x0970, 0x0982, 0x0983, 0x0985, 0x098c, 0x098f, 0x0990, 0x0993, 0x09a8, 0x09aa, 0x09b0, 0x09b2, 0x09b2, 0x09b6, 0x09b9, 0x09be, 0x09c0, 0x09c7, 0x09c8, 0x09cb, 0x09cc, 0x09d7, 0x09d7, 0x09dc, 0x09dd, 0x09df, 0x09e1, 0x09e6, 0x09f1, 0x09f4, 0x09fa, 0x0a05, 0x0a0a, 0x0a0f, 0x0a10, 0x0a13, 0x0a28, 0x0a2a, 0x0a30, 0x0a32, 0x0a33, 0x0a35, 0x0a36, 0x0a38, 0x0a39, 0x0a3e, 0x0a40, 0x0a59, 0x0a5c, 0x0a5e, 0x0a5e, 0x0a66, 0x0a6f, 0x0a72, 0x0a74, 0x0a83, 0x0a83, 0x0a85, 0x0a8b, 0x0a8d, 0x0a8d, 0x0a8f, 0x0a91, 0x0a93, 0x0aa8, 0x0aaa, 0x0ab0, 0x0ab2, 0x0ab3, 0x0ab5, 0x0ab9, 0x0abd, 0x0ac0, 0x0ac9, 0x0ac9, 0x0acb, 0x0acc, 0x0ad0, 0x0ad0, 0x0ae0, 0x0ae0, 0x0ae6, 0x0aef, 0x0b02, 0x0b03, 0x0b05, 0x0b0c, 0x0b0f, 0x0b10, 0x0b13, 0x0b28, 0x0b2a, 0x0b30, 0x0b32, 0x0b33, 0x0b36, 0x0b39, 0x0b3d, 0x0b3e, 0x0b40, 0x0b40, 0x0b47, 0x0b48, 0x0b4b, 0x0b4c, 0x0b57, 0x0b57, 0x0b5c, 0x0b5d, 0x0b5f, 0x0b61, 0x0b66, 0x0b70, 0x0b83, 0x0b83, 0x0b85, 0x0b8a, 0x0b8e, 0x0b90, 0x0b92, 0x0b95, 0x0b99, 0x0b9a, 0x0b9c, 0x0b9c, 0x0b9e, 0x0b9f, 0x0ba3, 0x0ba4, 0x0ba8, 0x0baa, 0x0bae, 0x0bb5, 0x0bb7, 0x0bb9, 0x0bbe, 0x0bbf, 0x0bc1, 0x0bc2, 0x0bc6, 0x0bc8, 0x0bca, 0x0bcc, 0x0bd7, 0x0bd7, 0x0be7, 0x0bf2, 0x0c01, 0x0c03, 0x0c05, 0x0c0c, 0x0c0e, 0x0c10, 0x0c12, 0x0c28, 0x0c2a, 0x0c33, 0x0c35, 0x0c39, 0x0c41, 0x0c44, 0x0c60, 0x0c61, 0x0c66, 0x0c6f, 0x0c82, 0x0c83, 0x0c85, 0x0c8c, 0x0c8e, 0x0c90, 0x0c92, 0x0ca8, 0x0caa, 0x0cb3, 0x0cb5, 0x0cb9, 0x0cbe, 0x0cbe, 0x0cc0, 0x0cc4, 0x0cc7, 0x0cc8, 0x0cca, 0x0ccb, 0x0cd5, 0x0cd6, 0x0cde, 0x0cde, 0x0ce0, 0x0ce1, 0x0ce6, 0x0cef, 0x0d02, 0x0d03, 0x0d05, 0x0d0c, 0x0d0e, 0x0d10, 0x0d12, 0x0d28, 0x0d2a, 0x0d39, 0x0d3e, 0x0d40, 0x0d46, 0x0d48, 0x0d4a, 0x0d4c, 0x0d57, 0x0d57, 0x0d60, 0x0d61, 0x0d66, 0x0d6f, 0x0d82, 0x0d83, 0x0d85, 0x0d96, 0x0d9a, 0x0db1, 0x0db3, 0x0dbb, 0x0dbd, 0x0dbd, 0x0dc0, 0x0dc6, 0x0dcf, 0x0dd1, 0x0dd8, 0x0ddf, 0x0df2, 0x0df4, 0x0e01, 0x0e30, 0x0e32, 0x0e33, 0x0e40, 0x0e46, 0x0e4f, 0x0e5b, 0x0e81, 0x0e82, 0x0e84, 0x0e84, 0x0e87, 0x0e88, 0x0e8a, 0x0e8a, 0x0e8d, 0x0e8d, 0x0e94, 0x0e97, 0x0e99, 0x0e9f, 0x0ea1, 0x0ea3, 0x0ea5, 0x0ea5, 0x0ea7, 0x0ea7, 0x0eaa, 0x0eab, 0x0ead, 0x0eb0, 0x0eb2, 0x0eb3, 0x0ebd, 0x0ebd, 0x0ec0, 0x0ec4, 0x0ec6, 0x0ec6, 0x0ed0, 0x0ed9, 0x0edc, 0x0edd, 0x0f00, 0x0f17, 0x0f1a, 0x0f34, 0x0f36, 0x0f36, 0x0f38, 0x0f38, 0x0f3e, 0x0f47, 0x0f49, 0x0f6a, 0x0f7f, 0x0f7f, 0x0f85, 0x0f85, 0x0f88, 0x0f8b, 0x0fbe, 0x0fc5, 0x0fc7, 0x0fcc, 0x0fcf, 0x0fcf, 0x1000, 0x1021, 0x1023, 0x1027, 0x1029, 0x102a, 0x102c, 0x102c, 0x1031, 0x1031, 0x1038, 0x1038, 0x1040, 0x1057, 0x10a0, 0x10c5, 0x10d0, 0x10f8, 0x10fb, 0x10fb, 0x1100, 0x1159, 0x115f, 0x11a2, 0x11a8, 0x11f9, 0x1200, 0x1206, 0x1208, 0x1246, 0x1248, 0x1248, 0x124a, 0x124d, 0x1250, 0x1256, 0x1258, 0x1258, 0x125a, 0x125d, 0x1260, 0x1286, 0x1288, 0x1288, 0x128a, 0x128d, 0x1290, 0x12ae, 0x12b0, 0x12b0, 0x12b2, 0x12b5, 0x12b8, 0x12be, 0x12c0, 0x12c0, 0x12c2, 0x12c5, 0x12c8, 0x12ce, 0x12d0, 0x12d6, 0x12d8, 0x12ee, 0x12f0, 0x130e, 0x1310, 0x1310, 0x1312, 0x1315, 0x1318, 0x131e, 0x1320, 0x1346, 0x1348, 0x135a, 0x1361, 0x137c, 0x13a0, 0x13f4, 0x1401, 0x1676, 0x1681, 0x169a, 0x16a0, 0x16f0, 0x1700, 0x170c, 0x170e, 0x1711, 0x1720, 0x1731, 0x1735, 0x1736, 0x1740, 0x1751, 0x1760, 0x176c, 0x176e, 0x1770, 0x1780, 0x17b6, 0x17be, 0x17c5, 0x17c7, 0x17c8, 0x17d4, 0x17da, 0x17dc, 0x17dc, 0x17e0, 0x17e9, 0x1810, 0x1819, 0x1820, 0x1877, 0x1880, 0x18a8, 0x1e00, 0x1e9b, 0x1ea0, 0x1ef9, 0x1f00, 0x1f15, 0x1f18, 0x1f1d, 0x1f20, 0x1f45, 0x1f48, 0x1f4d, 0x1f50, 0x1f57, 0x1f59, 0x1f59, 0x1f5b, 0x1f5b, 0x1f5d, 0x1f5d, 0x1f5f, 0x1f7d, 0x1f80, 0x1fb4, 0x1fb6, 0x1fbc, 0x1fbe, 0x1fbe, 0x1fc2, 0x1fc4, 0x1fc6, 0x1fcc, 0x1fd0, 0x1fd3, 0x1fd6, 0x1fdb, 0x1fe0, 0x1fec, 0x1ff2, 0x1ff4, 0x1ff6, 0x1ffc, 0x200e, 0x200e, 0x2071, 0x2071, 0x207f, 0x207f, 0x2102, 0x2102, 0x2107, 0x2107, 0x210a, 0x2113, 0x2115, 0x2115, 0x2119, 0x211d, 0x2124, 0x2124, 0x2126, 0x2126, 0x2128, 0x2128, 0x212a, 0x212d, 0x212f, 0x2131, 0x2133, 0x2139, 0x213d, 0x213f, 0x2145, 0x2149, 0x2160, 0x2183, 0x2336, 0x237a, 0x2395, 0x2395, 0x249c, 0x24e9, 0x3005, 0x3007, 0x3021, 0x3029, 0x3031, 0x3035, 0x3038, 0x303c, 0x3041, 0x3096, 0x309d, 0x309f, 0x30a1, 0x30fa, 0x30fc, 0x30ff, 0x3105, 0x312c, 0x3131, 0x318e, 0x3190, 0x31b7, 0x31f0, 0x321c, 0x3220, 0x3243, 0x3260, 0x327b, 0x327f, 0x32b0, 0x32c0, 0x32cb, 0x32d0, 0x32fe, 0x3300, 0x3376, 0x337b, 0x33dd, 0x33e0, 0x33fe, 0x3400, 0x4db5, 0x4e00, 0x9fa5, 0xa000, 0xa48c, 0xac00, 0xd7a3, 0xd800, 0xfa2d, 0xfa30, 0xfa6a, 0xfb00, 0xfb06, 0xfb13, 0xfb17, 0xff21, 0xff3a, 0xff41, 0xff5a, 0xff66, 0xffbe, 0xffc2, 0xffc7, 0xffca, 0xffcf, 0xffd2, 0xffd7, 0xffda, 0xffdc, 0x10300, 0x1031e, 0x10320, 0x10323, 0x10330, 0x1034a, 0x10400, 0x10425, 0x10428, 0x1044d, 0x1d000, 0x1d0f5, 0x1d100, 0x1d126, 0x1d12a, 0x1d166, 0x1d16a, 0x1d172, 0x1d183, 0x1d184, 0x1d18c, 0x1d1a9, 0x1d1ae, 0x1d1dd, 0x1d400, 0x1d454, 0x1d456, 0x1d49c, 0x1d49e, 0x1d49f, 0x1d4a2, 0x1d4a2, 0x1d4a5, 0x1d4a6, 0x1d4a9, 0x1d4ac, 0x1d4ae, 0x1d4b9, 0x1d4bb, 0x1d4bb, 0x1d4bd, 0x1d4c0, 0x1d4c2, 0x1d4c3, 0x1d4c5, 0x1d505, 0x1d507, 0x1d50a, 0x1d50d, 0x1d514, 0x1d516, 0x1d51c, 0x1d51e, 0x1d539, 0x1d53b, 0x1d53e, 0x1d540, 0x1d544, 0x1d546, 0x1d546, 0x1d54a, 0x1d550, 0x1d552, 0x1d6a3, 0x1d6a8, 0x1d7c9, 0x20000, 0x2a6d6, 0x2f800, 0x2fa1d, 0xf0000, 0xffffd, 0x100000, 0x10fffd];
|
|
715
|
+
/* eslint-enable */
|
|
716
|
+
|
|
717
|
+
const isBidirectionalL = character => inRange(character, bidirectional_l);
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* non-ASCII space characters [StringPrep, C.1.2] that can be
|
|
721
|
+
* mapped to SPACE (U+0020)
|
|
722
|
+
*/
|
|
723
|
+
|
|
724
|
+
const mapping2space = isNonASCIISpaceCharacter;
|
|
725
|
+
/**
|
|
726
|
+
* the "commonly mapped to nothing" characters [StringPrep, B.1]
|
|
727
|
+
* that can be mapped to nothing.
|
|
728
|
+
*/
|
|
729
|
+
|
|
730
|
+
const mapping2nothing = isCommonlyMappedToNothing; // utils
|
|
731
|
+
|
|
732
|
+
const getCodePoint = character => character.codePointAt(0);
|
|
733
|
+
|
|
734
|
+
const first = x => x[0];
|
|
735
|
+
|
|
736
|
+
const last = x => x[x.length - 1];
|
|
737
|
+
/**
|
|
738
|
+
* Convert provided string into an array of Unicode Code Points.
|
|
739
|
+
* Based on https://stackoverflow.com/a/21409165/1556249
|
|
740
|
+
* and https://www.npmjs.com/package/code-point-at.
|
|
741
|
+
* @param {string} input
|
|
742
|
+
* @returns {number[]}
|
|
743
|
+
*/
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
function toCodePoints(input) {
|
|
747
|
+
const codepoints = [];
|
|
748
|
+
const size = input.length;
|
|
749
|
+
|
|
750
|
+
for (let i = 0; i < size; i += 1) {
|
|
751
|
+
const before = input.charCodeAt(i);
|
|
752
|
+
|
|
753
|
+
if (before >= 0xd800 && before <= 0xdbff && size > i + 1) {
|
|
754
|
+
const next = input.charCodeAt(i + 1);
|
|
755
|
+
|
|
756
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
757
|
+
codepoints.push((before - 0xd800) * 0x400 + next - 0xdc00 + 0x10000);
|
|
758
|
+
i += 1;
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
codepoints.push(before);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
return codepoints;
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* SASLprep.
|
|
770
|
+
* @param {string} input
|
|
771
|
+
* @param {Object} opts
|
|
772
|
+
* @param {boolean} opts.allowUnassigned
|
|
773
|
+
* @returns {string}
|
|
774
|
+
*/
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
function saslprep(input, opts = {}) {
|
|
778
|
+
if (typeof input !== 'string') {
|
|
779
|
+
throw new TypeError('Expected string.');
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
if (input.length === 0) {
|
|
783
|
+
return '';
|
|
784
|
+
} // 1. Map
|
|
785
|
+
|
|
786
|
+
|
|
787
|
+
const mapped_input = toCodePoints(input) // 1.1 mapping to space
|
|
788
|
+
.map(character => mapping2space(character) ? 0x20 : character) // 1.2 mapping to nothing
|
|
789
|
+
.filter(character => !mapping2nothing(character)); // 2. Normalize
|
|
790
|
+
|
|
791
|
+
const normalized_input = String.fromCodePoint.apply(null, mapped_input).normalize('NFKC');
|
|
792
|
+
const normalized_map = toCodePoints(normalized_input); // 3. Prohibit
|
|
793
|
+
|
|
794
|
+
const hasProhibited = normalized_map.some(isProhibitedCharacter);
|
|
795
|
+
|
|
796
|
+
if (hasProhibited) {
|
|
797
|
+
throw new Error('Prohibited character, see https://tools.ietf.org/html/rfc4013#section-2.3');
|
|
798
|
+
} // Unassigned Code Points
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
if (opts.allowUnassigned !== true) {
|
|
802
|
+
const hasUnassigned = normalized_map.some(isUnassignedCodePoint);
|
|
803
|
+
|
|
804
|
+
if (hasUnassigned) {
|
|
805
|
+
throw new Error('Unassigned code point, see https://tools.ietf.org/html/rfc4013#section-2.5');
|
|
806
|
+
}
|
|
807
|
+
} // 4. check bidi
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
const hasBidiRAL = normalized_map.some(isBidirectionalRAL);
|
|
811
|
+
const hasBidiL = normalized_map.some(isBidirectionalL); // 4.1 If a string contains any RandALCat character, the string MUST NOT
|
|
812
|
+
// contain any LCat character.
|
|
813
|
+
|
|
814
|
+
if (hasBidiRAL && hasBidiL) {
|
|
815
|
+
throw new Error('String must not contain RandALCat and LCat at the same time,' + ' see https://tools.ietf.org/html/rfc3454#section-6');
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* 4.2 If a string contains any RandALCat character, a RandALCat
|
|
819
|
+
* character MUST be the first character of the string, and a
|
|
820
|
+
* RandALCat character MUST be the last character of the string.
|
|
821
|
+
*/
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
const isFirstBidiRAL = isBidirectionalRAL(getCodePoint(first(normalized_input)));
|
|
825
|
+
const isLastBidiRAL = isBidirectionalRAL(getCodePoint(last(normalized_input)));
|
|
826
|
+
|
|
827
|
+
if (hasBidiRAL && !(isFirstBidiRAL && isLastBidiRAL)) {
|
|
828
|
+
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');
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
return normalized_input;
|
|
372
832
|
}
|
|
373
833
|
|
|
374
834
|
/*
|
|
@@ -384,6 +844,7 @@ class PDFSecurity {
|
|
|
384
844
|
if (!info.hasOwnProperty(key)) {
|
|
385
845
|
continue;
|
|
386
846
|
}
|
|
847
|
+
|
|
387
848
|
infoStr += `${key}: ${info[key].toString()}\n`;
|
|
388
849
|
}
|
|
389
850
|
|
|
@@ -398,6 +859,7 @@ class PDFSecurity {
|
|
|
398
859
|
if (!options.ownerPassword && !options.userPassword) {
|
|
399
860
|
return null;
|
|
400
861
|
}
|
|
862
|
+
|
|
401
863
|
return new PDFSecurity(document, options);
|
|
402
864
|
}
|
|
403
865
|
|
|
@@ -407,6 +869,7 @@ class PDFSecurity {
|
|
|
407
869
|
}
|
|
408
870
|
|
|
409
871
|
this.document = document;
|
|
872
|
+
|
|
410
873
|
this._setupEncryption(options);
|
|
411
874
|
}
|
|
412
875
|
|
|
@@ -416,13 +879,16 @@ class PDFSecurity {
|
|
|
416
879
|
case '1.5':
|
|
417
880
|
this.version = 2;
|
|
418
881
|
break;
|
|
882
|
+
|
|
419
883
|
case '1.6':
|
|
420
884
|
case '1.7':
|
|
421
885
|
this.version = 4;
|
|
422
886
|
break;
|
|
887
|
+
|
|
423
888
|
case '1.7ext3':
|
|
424
889
|
this.version = 5;
|
|
425
890
|
break;
|
|
891
|
+
|
|
426
892
|
default:
|
|
427
893
|
this.version = 1;
|
|
428
894
|
break;
|
|
@@ -437,9 +903,12 @@ class PDFSecurity {
|
|
|
437
903
|
case 2:
|
|
438
904
|
case 4:
|
|
439
905
|
this._setupEncryptionV1V2V4(this.version, encDict, options);
|
|
906
|
+
|
|
440
907
|
break;
|
|
908
|
+
|
|
441
909
|
case 5:
|
|
442
910
|
this._setupEncryptionV5(encDict, options);
|
|
911
|
+
|
|
443
912
|
break;
|
|
444
913
|
}
|
|
445
914
|
|
|
@@ -448,17 +917,20 @@ class PDFSecurity {
|
|
|
448
917
|
|
|
449
918
|
_setupEncryptionV1V2V4(v, encDict, options) {
|
|
450
919
|
let r, permissions;
|
|
920
|
+
|
|
451
921
|
switch (v) {
|
|
452
922
|
case 1:
|
|
453
923
|
r = 2;
|
|
454
924
|
this.keyBits = 40;
|
|
455
925
|
permissions = getPermissionsR2(options.permissions);
|
|
456
926
|
break;
|
|
927
|
+
|
|
457
928
|
case 2:
|
|
458
929
|
r = 3;
|
|
459
930
|
this.keyBits = 128;
|
|
460
931
|
permissions = getPermissionsR3(options.permissions);
|
|
461
932
|
break;
|
|
933
|
+
|
|
462
934
|
case 4:
|
|
463
935
|
r = 4;
|
|
464
936
|
this.keyBits = 128;
|
|
@@ -468,10 +940,10 @@ class PDFSecurity {
|
|
|
468
940
|
|
|
469
941
|
const paddedUserPassword = processPasswordR2R3R4(options.userPassword);
|
|
470
942
|
const paddedOwnerPassword = options.ownerPassword ? processPasswordR2R3R4(options.ownerPassword) : paddedUserPassword;
|
|
471
|
-
|
|
472
943
|
const ownerPasswordEntry = getOwnerPasswordR2R3R4(r, this.keyBits, paddedUserPassword, paddedOwnerPassword);
|
|
473
944
|
this.encryptionKey = getEncryptionKeyR2R3R4(r, this.keyBits, this.document._id, paddedUserPassword, ownerPasswordEntry, permissions);
|
|
474
945
|
let userPasswordEntry;
|
|
946
|
+
|
|
475
947
|
if (r === 2) {
|
|
476
948
|
userPasswordEntry = getUserPasswordR2(this.encryptionKey);
|
|
477
949
|
} else {
|
|
@@ -479,9 +951,11 @@ class PDFSecurity {
|
|
|
479
951
|
}
|
|
480
952
|
|
|
481
953
|
encDict.V = v;
|
|
954
|
+
|
|
482
955
|
if (v >= 2) {
|
|
483
956
|
encDict.Length = this.keyBits;
|
|
484
957
|
}
|
|
958
|
+
|
|
485
959
|
if (v === 4) {
|
|
486
960
|
encDict.CF = {
|
|
487
961
|
StdCF: {
|
|
@@ -493,6 +967,7 @@ class PDFSecurity {
|
|
|
493
967
|
encDict.StmF = 'StdCF';
|
|
494
968
|
encDict.StrF = 'StdCF';
|
|
495
969
|
}
|
|
970
|
+
|
|
496
971
|
encDict.R = r;
|
|
497
972
|
encDict.O = wordArrayToBuffer(ownerPasswordEntry);
|
|
498
973
|
encDict.U = wordArrayToBuffer(userPasswordEntry);
|
|
@@ -502,10 +977,8 @@ class PDFSecurity {
|
|
|
502
977
|
_setupEncryptionV5(encDict, options) {
|
|
503
978
|
this.keyBits = 256;
|
|
504
979
|
const permissions = getPermissionsR3(options);
|
|
505
|
-
|
|
506
980
|
const processedUserPassword = processPasswordR5(options.userPassword);
|
|
507
981
|
const processedOwnerPassword = options.ownerPassword ? processPasswordR5(options.ownerPassword) : processedUserPassword;
|
|
508
|
-
|
|
509
982
|
this.encryptionKey = getEncryptionKeyR5(PDFSecurity.generateRandomWordArray);
|
|
510
983
|
const userPasswordEntry = getUserPasswordR5(processedUserPassword, PDFSecurity.generateRandomWordArray);
|
|
511
984
|
const userKeySalt = CryptoJS.lib.WordArray.create(userPasswordEntry.words.slice(10, 12), 8);
|
|
@@ -514,7 +987,6 @@ class PDFSecurity {
|
|
|
514
987
|
const ownerKeySalt = CryptoJS.lib.WordArray.create(ownerPasswordEntry.words.slice(10, 12), 8);
|
|
515
988
|
const ownerEncryptionKeyEntry = getOwnerEncryptionKeyR5(processedOwnerPassword, ownerKeySalt, userPasswordEntry, this.encryptionKey);
|
|
516
989
|
const permsEntry = getEncryptedPermissionsR5(permissions, this.encryptionKey, PDFSecurity.generateRandomWordArray);
|
|
517
|
-
|
|
518
990
|
encDict.V = 5;
|
|
519
991
|
encDict.Length = this.keyBits;
|
|
520
992
|
encDict.CF = {
|
|
@@ -537,6 +1009,7 @@ class PDFSecurity {
|
|
|
537
1009
|
|
|
538
1010
|
getEncryptFn(obj, gen) {
|
|
539
1011
|
let digest;
|
|
1012
|
+
|
|
540
1013
|
if (this.version < 5) {
|
|
541
1014
|
digest = this.encryptionKey.clone().concat(CryptoJS.lib.WordArray.create([(obj & 0xff) << 24 | (obj & 0xff00) << 8 | obj >> 8 & 0xff00 | gen & 0xff, (gen & 0xff00) << 16], 5));
|
|
542
1015
|
}
|
|
@@ -548,6 +1021,7 @@ class PDFSecurity {
|
|
|
548
1021
|
}
|
|
549
1022
|
|
|
550
1023
|
let key;
|
|
1024
|
+
|
|
551
1025
|
if (this.version === 4) {
|
|
552
1026
|
key = CryptoJS.MD5(digest.concat(CryptoJS.lib.WordArray.create([0x73416c54], 4)));
|
|
553
1027
|
} else {
|
|
@@ -560,58 +1034,72 @@ class PDFSecurity {
|
|
|
560
1034
|
padding: CryptoJS.pad.Pkcs7,
|
|
561
1035
|
iv
|
|
562
1036
|
};
|
|
563
|
-
|
|
564
1037
|
return buffer => wordArrayToBuffer(iv.clone().concat(CryptoJS.AES.encrypt(CryptoJS.lib.WordArray.create(buffer), key, options).ciphertext));
|
|
565
1038
|
}
|
|
566
1039
|
|
|
567
1040
|
end() {
|
|
568
1041
|
this.dictionary.end();
|
|
569
1042
|
}
|
|
1043
|
+
|
|
570
1044
|
}
|
|
571
1045
|
|
|
572
1046
|
function getPermissionsR2(permissionObject = {}) {
|
|
573
1047
|
let permissions = 0xffffffc0 >> 0;
|
|
1048
|
+
|
|
574
1049
|
if (permissionObject.printing) {
|
|
575
1050
|
permissions |= 0b000000000100;
|
|
576
1051
|
}
|
|
1052
|
+
|
|
577
1053
|
if (permissionObject.modifying) {
|
|
578
1054
|
permissions |= 0b000000001000;
|
|
579
1055
|
}
|
|
1056
|
+
|
|
580
1057
|
if (permissionObject.copying) {
|
|
581
1058
|
permissions |= 0b000000010000;
|
|
582
1059
|
}
|
|
1060
|
+
|
|
583
1061
|
if (permissionObject.annotating) {
|
|
584
1062
|
permissions |= 0b000000100000;
|
|
585
1063
|
}
|
|
1064
|
+
|
|
586
1065
|
return permissions;
|
|
587
1066
|
}
|
|
588
1067
|
|
|
589
1068
|
function getPermissionsR3(permissionObject = {}) {
|
|
590
1069
|
let permissions = 0xfffff0c0 >> 0;
|
|
1070
|
+
|
|
591
1071
|
if (permissionObject.printing === 'lowResolution') {
|
|
592
1072
|
permissions |= 0b000000000100;
|
|
593
1073
|
}
|
|
1074
|
+
|
|
594
1075
|
if (permissionObject.printing === 'highResolution') {
|
|
595
1076
|
permissions |= 0b100000000100;
|
|
596
1077
|
}
|
|
1078
|
+
|
|
597
1079
|
if (permissionObject.modifying) {
|
|
598
1080
|
permissions |= 0b000000001000;
|
|
599
1081
|
}
|
|
1082
|
+
|
|
600
1083
|
if (permissionObject.copying) {
|
|
601
1084
|
permissions |= 0b000000010000;
|
|
602
1085
|
}
|
|
1086
|
+
|
|
603
1087
|
if (permissionObject.annotating) {
|
|
604
1088
|
permissions |= 0b000000100000;
|
|
605
1089
|
}
|
|
1090
|
+
|
|
606
1091
|
if (permissionObject.fillingForms) {
|
|
607
1092
|
permissions |= 0b000100000000;
|
|
608
1093
|
}
|
|
1094
|
+
|
|
609
1095
|
if (permissionObject.contentAccessibility) {
|
|
610
1096
|
permissions |= 0b001000000000;
|
|
611
1097
|
}
|
|
1098
|
+
|
|
612
1099
|
if (permissionObject.documentAssembly) {
|
|
613
1100
|
permissions |= 0b010000000000;
|
|
614
1101
|
}
|
|
1102
|
+
|
|
615
1103
|
return permissions;
|
|
616
1104
|
}
|
|
617
1105
|
|
|
@@ -622,19 +1110,24 @@ function getUserPasswordR2(encryptionKey) {
|
|
|
622
1110
|
function getUserPasswordR3R4(documentId, encryptionKey) {
|
|
623
1111
|
const key = encryptionKey.clone();
|
|
624
1112
|
let cipher = CryptoJS.MD5(processPasswordR2R3R4().concat(CryptoJS.lib.WordArray.create(documentId)));
|
|
1113
|
+
|
|
625
1114
|
for (let i = 0; i < 20; i++) {
|
|
626
1115
|
const xorRound = Math.ceil(key.sigBytes / 4);
|
|
1116
|
+
|
|
627
1117
|
for (let j = 0; j < xorRound; j++) {
|
|
628
1118
|
key.words[j] = encryptionKey.words[j] ^ (i | i << 8 | i << 16 | i << 24);
|
|
629
1119
|
}
|
|
1120
|
+
|
|
630
1121
|
cipher = CryptoJS.RC4.encrypt(cipher, key).ciphertext;
|
|
631
1122
|
}
|
|
1123
|
+
|
|
632
1124
|
return cipher.concat(CryptoJS.lib.WordArray.create(null, 16));
|
|
633
1125
|
}
|
|
634
1126
|
|
|
635
1127
|
function getOwnerPasswordR2R3R4(r, keyBits, paddedUserPassword, paddedOwnerPassword) {
|
|
636
1128
|
let digest = paddedOwnerPassword;
|
|
637
1129
|
let round = r >= 3 ? 51 : 1;
|
|
1130
|
+
|
|
638
1131
|
for (let i = 0; i < round; i++) {
|
|
639
1132
|
digest = CryptoJS.MD5(digest);
|
|
640
1133
|
}
|
|
@@ -643,23 +1136,29 @@ function getOwnerPasswordR2R3R4(r, keyBits, paddedUserPassword, paddedOwnerPassw
|
|
|
643
1136
|
key.sigBytes = keyBits / 8;
|
|
644
1137
|
let cipher = paddedUserPassword;
|
|
645
1138
|
round = r >= 3 ? 20 : 1;
|
|
1139
|
+
|
|
646
1140
|
for (let i = 0; i < round; i++) {
|
|
647
1141
|
const xorRound = Math.ceil(key.sigBytes / 4);
|
|
1142
|
+
|
|
648
1143
|
for (let j = 0; j < xorRound; j++) {
|
|
649
1144
|
key.words[j] = digest.words[j] ^ (i | i << 8 | i << 16 | i << 24);
|
|
650
1145
|
}
|
|
1146
|
+
|
|
651
1147
|
cipher = CryptoJS.RC4.encrypt(cipher, key).ciphertext;
|
|
652
1148
|
}
|
|
1149
|
+
|
|
653
1150
|
return cipher;
|
|
654
1151
|
}
|
|
655
1152
|
|
|
656
1153
|
function getEncryptionKeyR2R3R4(r, keyBits, documentId, paddedUserPassword, ownerPasswordEntry, permissions) {
|
|
657
1154
|
let key = paddedUserPassword.clone().concat(ownerPasswordEntry).concat(CryptoJS.lib.WordArray.create([lsbFirstWord(permissions)], 4)).concat(CryptoJS.lib.WordArray.create(documentId));
|
|
658
1155
|
const round = r >= 3 ? 51 : 1;
|
|
1156
|
+
|
|
659
1157
|
for (let i = 0; i < round; i++) {
|
|
660
1158
|
key = CryptoJS.MD5(key);
|
|
661
1159
|
key.sigBytes = keyBits / 8;
|
|
662
1160
|
}
|
|
1161
|
+
|
|
663
1162
|
return key;
|
|
664
1163
|
}
|
|
665
1164
|
|
|
@@ -712,18 +1211,23 @@ function processPasswordR2R3R4(password = '') {
|
|
|
712
1211
|
const out = new Buffer(32);
|
|
713
1212
|
const length = password.length;
|
|
714
1213
|
let index = 0;
|
|
1214
|
+
|
|
715
1215
|
while (index < length && index < 32) {
|
|
716
1216
|
const code = password.charCodeAt(index);
|
|
1217
|
+
|
|
717
1218
|
if (code > 0xff) {
|
|
718
1219
|
throw new Error('Password contains one or more invalid characters.');
|
|
719
1220
|
}
|
|
1221
|
+
|
|
720
1222
|
out[index] = code;
|
|
721
1223
|
index++;
|
|
722
1224
|
}
|
|
1225
|
+
|
|
723
1226
|
while (index < 32) {
|
|
724
1227
|
out[index] = PASSWORD_PADDING[index - length];
|
|
725
1228
|
index++;
|
|
726
1229
|
}
|
|
1230
|
+
|
|
727
1231
|
return CryptoJS.lib.WordArray.create(out);
|
|
728
1232
|
}
|
|
729
1233
|
|
|
@@ -745,15 +1249,19 @@ function lsbFirstWord(data) {
|
|
|
745
1249
|
|
|
746
1250
|
function wordArrayToBuffer(wordArray) {
|
|
747
1251
|
const byteArray = [];
|
|
1252
|
+
|
|
748
1253
|
for (let i = 0; i < wordArray.sigBytes; i++) {
|
|
749
1254
|
byteArray.push(wordArray.words[Math.floor(i / 4)] >> 8 * (3 - i % 4) & 0xff);
|
|
750
1255
|
}
|
|
1256
|
+
|
|
751
1257
|
return Buffer.from(byteArray);
|
|
752
1258
|
}
|
|
753
1259
|
|
|
754
1260
|
const PASSWORD_PADDING = [0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a];
|
|
755
1261
|
|
|
756
|
-
const {
|
|
1262
|
+
const {
|
|
1263
|
+
number
|
|
1264
|
+
} = PDFObject;
|
|
757
1265
|
|
|
758
1266
|
class PDFGradient {
|
|
759
1267
|
constructor(doc) {
|
|
@@ -767,6 +1275,7 @@ class PDFGradient {
|
|
|
767
1275
|
if (opacity == null) {
|
|
768
1276
|
opacity = 1;
|
|
769
1277
|
}
|
|
1278
|
+
|
|
770
1279
|
color = this.doc._normalizeColor(color);
|
|
771
1280
|
|
|
772
1281
|
if (this.stops.length === 0) {
|
|
@@ -795,14 +1304,16 @@ class PDFGradient {
|
|
|
795
1304
|
|
|
796
1305
|
embed(m) {
|
|
797
1306
|
let fn;
|
|
1307
|
+
|
|
798
1308
|
if (this.stops.length === 0) {
|
|
799
1309
|
return;
|
|
800
1310
|
}
|
|
1311
|
+
|
|
801
1312
|
this.embedded = true;
|
|
802
|
-
this.matrix = m;
|
|
1313
|
+
this.matrix = m; // if the last stop comes before 100%, add a copy at 100%
|
|
803
1314
|
|
|
804
|
-
// if the last stop comes before 100%, add a copy at 100%
|
|
805
1315
|
const last = this.stops[this.stops.length - 1];
|
|
1316
|
+
|
|
806
1317
|
if (last[0] < 1) {
|
|
807
1318
|
this.stops.push([1, last[1], last[2]]);
|
|
808
1319
|
}
|
|
@@ -813,6 +1324,7 @@ class PDFGradient {
|
|
|
813
1324
|
|
|
814
1325
|
for (let i = 0, stopsLength = this.stops.length - 1; i < stopsLength; i++) {
|
|
815
1326
|
encode.push(0, 1);
|
|
1327
|
+
|
|
816
1328
|
if (i + 2 !== stopsLength) {
|
|
817
1329
|
bounds.push(this.stops[i + 1][0]);
|
|
818
1330
|
}
|
|
@@ -824,38 +1336,34 @@ class PDFGradient {
|
|
|
824
1336
|
C1: this.stops[i + 1][1],
|
|
825
1337
|
N: 1
|
|
826
1338
|
});
|
|
827
|
-
|
|
828
1339
|
stops.push(fn);
|
|
829
1340
|
fn.end();
|
|
830
|
-
}
|
|
1341
|
+
} // if there are only two stops, we don't need a stitching function
|
|
1342
|
+
|
|
831
1343
|
|
|
832
|
-
// if there are only two stops, we don't need a stitching function
|
|
833
1344
|
if (stops.length === 1) {
|
|
834
1345
|
fn = stops[0];
|
|
835
1346
|
} else {
|
|
836
1347
|
fn = this.doc.ref({
|
|
837
|
-
FunctionType: 3,
|
|
1348
|
+
FunctionType: 3,
|
|
1349
|
+
// stitching function
|
|
838
1350
|
Domain: [0, 1],
|
|
839
1351
|
Functions: stops,
|
|
840
1352
|
Bounds: bounds,
|
|
841
1353
|
Encode: encode
|
|
842
1354
|
});
|
|
843
|
-
|
|
844
1355
|
fn.end();
|
|
845
1356
|
}
|
|
846
1357
|
|
|
847
1358
|
this.id = `Sh${++this.doc._gradCount}`;
|
|
848
|
-
|
|
849
1359
|
const shader = this.shader(fn);
|
|
850
1360
|
shader.end();
|
|
851
|
-
|
|
852
1361
|
const pattern = this.doc.ref({
|
|
853
1362
|
Type: 'Pattern',
|
|
854
1363
|
PatternType: 2,
|
|
855
1364
|
Shading: shader,
|
|
856
1365
|
Matrix: this.matrix.map(v => number(v))
|
|
857
1366
|
});
|
|
858
|
-
|
|
859
1367
|
pattern.end();
|
|
860
1368
|
|
|
861
1369
|
if (this.stops.some(stop => stop[2] < 1)) {
|
|
@@ -867,9 +1375,7 @@ class PDFGradient {
|
|
|
867
1375
|
}
|
|
868
1376
|
|
|
869
1377
|
grad = grad.embed(this.matrix);
|
|
870
|
-
|
|
871
1378
|
const pageBBox = [0, 0, this.doc.page.width, this.doc.page.height];
|
|
872
|
-
|
|
873
1379
|
const form = this.doc.ref({
|
|
874
1380
|
Type: 'XObject',
|
|
875
1381
|
Subtype: 'Form',
|
|
@@ -887,10 +1393,8 @@ class PDFGradient {
|
|
|
887
1393
|
}
|
|
888
1394
|
}
|
|
889
1395
|
});
|
|
890
|
-
|
|
891
1396
|
form.write('/Pattern cs /Sh1 scn');
|
|
892
1397
|
form.end(`${pageBBox.join(' ')} re f`);
|
|
893
|
-
|
|
894
1398
|
const gstate = this.doc.ref({
|
|
895
1399
|
Type: 'ExtGState',
|
|
896
1400
|
SMask: {
|
|
@@ -899,9 +1403,7 @@ class PDFGradient {
|
|
|
899
1403
|
G: form
|
|
900
1404
|
}
|
|
901
1405
|
});
|
|
902
|
-
|
|
903
1406
|
gstate.end();
|
|
904
|
-
|
|
905
1407
|
const opacityPattern = this.doc.ref({
|
|
906
1408
|
Type: 'Pattern',
|
|
907
1409
|
PatternType: 1,
|
|
@@ -920,10 +1422,8 @@ class PDFGradient {
|
|
|
920
1422
|
}
|
|
921
1423
|
}
|
|
922
1424
|
});
|
|
923
|
-
|
|
924
1425
|
opacityPattern.write('/Gs1 gs /Pattern cs /Sh1 scn');
|
|
925
1426
|
opacityPattern.end(`${pageBBox.join(' ')} re f`);
|
|
926
|
-
|
|
927
1427
|
this.doc.page.patterns[this.id] = opacityPattern;
|
|
928
1428
|
} else {
|
|
929
1429
|
this.doc.page.patterns[this.id] = pattern;
|
|
@@ -941,8 +1441,10 @@ class PDFGradient {
|
|
|
941
1441
|
if (!this.embedded || m.join(' ') !== this.matrix.join(' ')) {
|
|
942
1442
|
this.embed(m);
|
|
943
1443
|
}
|
|
1444
|
+
|
|
944
1445
|
return this.doc.addContent(`/${this.id} ${op}`);
|
|
945
1446
|
}
|
|
1447
|
+
|
|
946
1448
|
}
|
|
947
1449
|
|
|
948
1450
|
class PDFLinearGradient extends PDFGradient {
|
|
@@ -967,6 +1469,7 @@ class PDFLinearGradient extends PDFGradient {
|
|
|
967
1469
|
opacityGradient() {
|
|
968
1470
|
return new PDFLinearGradient(this.doc, this.x1, this.y1, this.x2, this.y2);
|
|
969
1471
|
}
|
|
1472
|
+
|
|
970
1473
|
}
|
|
971
1474
|
|
|
972
1475
|
class PDFRadialGradient extends PDFGradient {
|
|
@@ -994,12 +1497,20 @@ class PDFRadialGradient extends PDFGradient {
|
|
|
994
1497
|
opacityGradient() {
|
|
995
1498
|
return new PDFRadialGradient(this.doc, this.x1, this.y1, this.r1, this.x2, this.y2, this.r2);
|
|
996
1499
|
}
|
|
997
|
-
}
|
|
998
1500
|
|
|
999
|
-
|
|
1501
|
+
}
|
|
1000
1502
|
|
|
1001
|
-
|
|
1503
|
+
var Gradient = {
|
|
1504
|
+
PDFGradient,
|
|
1505
|
+
PDFLinearGradient,
|
|
1506
|
+
PDFRadialGradient
|
|
1507
|
+
};
|
|
1002
1508
|
|
|
1509
|
+
const {
|
|
1510
|
+
PDFGradient: PDFGradient$1,
|
|
1511
|
+
PDFLinearGradient: PDFLinearGradient$1,
|
|
1512
|
+
PDFRadialGradient: PDFRadialGradient$1
|
|
1513
|
+
} = Gradient;
|
|
1003
1514
|
var ColorMixin = {
|
|
1004
1515
|
initColor() {
|
|
1005
1516
|
// The opacity dictionaries
|
|
@@ -1018,6 +1529,7 @@ var ColorMixin = {
|
|
|
1018
1529
|
if (color.length === 4) {
|
|
1019
1530
|
color = color.replace(/#([0-9A-F])([0-9A-F])([0-9A-F])/i, '#$1$1$2$2$3$3');
|
|
1020
1531
|
}
|
|
1532
|
+
|
|
1021
1533
|
const hex = parseInt(color.slice(1), 16);
|
|
1022
1534
|
color = [hex >> 16, hex >> 8 & 0xff, hex & 0xff];
|
|
1023
1535
|
} else if (namedColors[color]) {
|
|
@@ -1028,11 +1540,11 @@ var ColorMixin = {
|
|
|
1028
1540
|
if (Array.isArray(color)) {
|
|
1029
1541
|
// RGB
|
|
1030
1542
|
if (color.length === 3) {
|
|
1031
|
-
color = color.map(part => part / 255);
|
|
1032
|
-
// CMYK
|
|
1543
|
+
color = color.map(part => part / 255); // CMYK
|
|
1033
1544
|
} else if (color.length === 4) {
|
|
1034
1545
|
color = color.map(part => part / 100);
|
|
1035
1546
|
}
|
|
1547
|
+
|
|
1036
1548
|
return color;
|
|
1037
1549
|
}
|
|
1038
1550
|
|
|
@@ -1041,6 +1553,7 @@ var ColorMixin = {
|
|
|
1041
1553
|
|
|
1042
1554
|
_setColor(color, stroke) {
|
|
1043
1555
|
color = this._normalizeColor(color);
|
|
1556
|
+
|
|
1044
1557
|
if (!color) {
|
|
1045
1558
|
return false;
|
|
1046
1559
|
}
|
|
@@ -1049,9 +1562,11 @@ var ColorMixin = {
|
|
|
1049
1562
|
|
|
1050
1563
|
if (color instanceof PDFGradient$1) {
|
|
1051
1564
|
this._setColorSpace('Pattern', stroke);
|
|
1565
|
+
|
|
1052
1566
|
color.apply(op);
|
|
1053
1567
|
} else {
|
|
1054
1568
|
const space = color.length === 4 ? 'DeviceCMYK' : 'DeviceRGB';
|
|
1569
|
+
|
|
1055
1570
|
this._setColorSpace(space, stroke);
|
|
1056
1571
|
|
|
1057
1572
|
color = color.join(' ');
|
|
@@ -1068,41 +1583,48 @@ var ColorMixin = {
|
|
|
1068
1583
|
|
|
1069
1584
|
fillColor(color, opacity) {
|
|
1070
1585
|
const set = this._setColor(color, false);
|
|
1586
|
+
|
|
1071
1587
|
if (set) {
|
|
1072
1588
|
this.fillOpacity(opacity);
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
// save this for text wrapper, which needs to reset
|
|
1589
|
+
} // save this for text wrapper, which needs to reset
|
|
1076
1590
|
// the fill color on new pages
|
|
1591
|
+
|
|
1592
|
+
|
|
1077
1593
|
this._fillColor = [color, opacity];
|
|
1078
1594
|
return this;
|
|
1079
1595
|
},
|
|
1080
1596
|
|
|
1081
1597
|
strokeColor(color, opacity) {
|
|
1082
1598
|
const set = this._setColor(color, true);
|
|
1599
|
+
|
|
1083
1600
|
if (set) {
|
|
1084
1601
|
this.strokeOpacity(opacity);
|
|
1085
1602
|
}
|
|
1603
|
+
|
|
1086
1604
|
return this;
|
|
1087
1605
|
},
|
|
1088
1606
|
|
|
1089
1607
|
opacity(opacity) {
|
|
1090
1608
|
this._doOpacity(opacity, opacity);
|
|
1609
|
+
|
|
1091
1610
|
return this;
|
|
1092
1611
|
},
|
|
1093
1612
|
|
|
1094
1613
|
fillOpacity(opacity) {
|
|
1095
1614
|
this._doOpacity(opacity, null);
|
|
1615
|
+
|
|
1096
1616
|
return this;
|
|
1097
1617
|
},
|
|
1098
1618
|
|
|
1099
1619
|
strokeOpacity(opacity) {
|
|
1100
1620
|
this._doOpacity(null, opacity);
|
|
1621
|
+
|
|
1101
1622
|
return this;
|
|
1102
1623
|
},
|
|
1103
1624
|
|
|
1104
1625
|
_doOpacity(fillOpacity, strokeOpacity) {
|
|
1105
1626
|
let dictionary, name;
|
|
1627
|
+
|
|
1106
1628
|
if (fillOpacity == null && strokeOpacity == null) {
|
|
1107
1629
|
return;
|
|
1108
1630
|
}
|
|
@@ -1110,19 +1632,24 @@ var ColorMixin = {
|
|
|
1110
1632
|
if (fillOpacity != null) {
|
|
1111
1633
|
fillOpacity = Math.max(0, Math.min(1, fillOpacity));
|
|
1112
1634
|
}
|
|
1635
|
+
|
|
1113
1636
|
if (strokeOpacity != null) {
|
|
1114
1637
|
strokeOpacity = Math.max(0, Math.min(1, strokeOpacity));
|
|
1115
1638
|
}
|
|
1639
|
+
|
|
1116
1640
|
const key = `${fillOpacity}_${strokeOpacity}`;
|
|
1117
1641
|
|
|
1118
1642
|
if (this._opacityRegistry[key]) {
|
|
1119
1643
|
[dictionary, name] = this._opacityRegistry[key];
|
|
1120
1644
|
} else {
|
|
1121
|
-
dictionary = {
|
|
1645
|
+
dictionary = {
|
|
1646
|
+
Type: 'ExtGState'
|
|
1647
|
+
};
|
|
1122
1648
|
|
|
1123
1649
|
if (fillOpacity != null) {
|
|
1124
1650
|
dictionary.ca = fillOpacity;
|
|
1125
1651
|
}
|
|
1652
|
+
|
|
1126
1653
|
if (strokeOpacity != null) {
|
|
1127
1654
|
dictionary.CA = strokeOpacity;
|
|
1128
1655
|
}
|
|
@@ -1145,8 +1672,8 @@ var ColorMixin = {
|
|
|
1145
1672
|
radialGradient(x1, y1, r1, x2, y2, r2) {
|
|
1146
1673
|
return new PDFRadialGradient$1(this, x1, y1, r1, x2, y2, r2);
|
|
1147
1674
|
}
|
|
1148
|
-
};
|
|
1149
1675
|
|
|
1676
|
+
};
|
|
1150
1677
|
var namedColors = {
|
|
1151
1678
|
aliceblue: [240, 248, 255],
|
|
1152
1679
|
antiquewhite: [250, 235, 215],
|
|
@@ -1298,9 +1825,7 @@ var namedColors = {
|
|
|
1298
1825
|
};
|
|
1299
1826
|
|
|
1300
1827
|
let cx, cy, px, py, sx, sy;
|
|
1301
|
-
|
|
1302
1828
|
cx = cy = px = py = sx = sy = 0;
|
|
1303
|
-
|
|
1304
1829
|
const parameters = {
|
|
1305
1830
|
A: 7,
|
|
1306
1831
|
a: 7,
|
|
@@ -1335,13 +1860,17 @@ const parse = function (path) {
|
|
|
1335
1860
|
for (let c of path) {
|
|
1336
1861
|
if (parameters[c] != null) {
|
|
1337
1862
|
params = parameters[c];
|
|
1863
|
+
|
|
1338
1864
|
if (cmd) {
|
|
1339
1865
|
// save existing command
|
|
1340
1866
|
if (curArg.length > 0) {
|
|
1341
1867
|
args[args.length] = +curArg;
|
|
1342
1868
|
}
|
|
1343
|
-
ret[ret.length] = { cmd, args };
|
|
1344
1869
|
|
|
1870
|
+
ret[ret.length] = {
|
|
1871
|
+
cmd,
|
|
1872
|
+
args
|
|
1873
|
+
};
|
|
1345
1874
|
args = [];
|
|
1346
1875
|
curArg = '';
|
|
1347
1876
|
foundDecimal = false;
|
|
@@ -1355,13 +1884,16 @@ const parse = function (path) {
|
|
|
1355
1884
|
|
|
1356
1885
|
if (args.length === params) {
|
|
1357
1886
|
// handle reused commands
|
|
1358
|
-
ret[ret.length] = {
|
|
1359
|
-
|
|
1887
|
+
ret[ret.length] = {
|
|
1888
|
+
cmd,
|
|
1889
|
+
args
|
|
1890
|
+
};
|
|
1891
|
+
args = [+curArg]; // handle assumed commands
|
|
1360
1892
|
|
|
1361
|
-
// handle assumed commands
|
|
1362
1893
|
if (cmd === 'M') {
|
|
1363
1894
|
cmd = 'L';
|
|
1364
1895
|
}
|
|
1896
|
+
|
|
1365
1897
|
if (cmd === 'm') {
|
|
1366
1898
|
cmd = 'l';
|
|
1367
1899
|
}
|
|
@@ -1369,29 +1901,32 @@ const parse = function (path) {
|
|
|
1369
1901
|
args[args.length] = +curArg;
|
|
1370
1902
|
}
|
|
1371
1903
|
|
|
1372
|
-
foundDecimal = c === '.';
|
|
1904
|
+
foundDecimal = c === '.'; // fix for negative numbers or repeated decimals with no delimeter between commands
|
|
1373
1905
|
|
|
1374
|
-
// fix for negative numbers or repeated decimals with no delimeter between commands
|
|
1375
1906
|
curArg = ['-', '.'].includes(c) ? c : '';
|
|
1376
1907
|
} else {
|
|
1377
1908
|
curArg += c;
|
|
1909
|
+
|
|
1378
1910
|
if (c === '.') {
|
|
1379
1911
|
foundDecimal = true;
|
|
1380
1912
|
}
|
|
1381
1913
|
}
|
|
1382
|
-
}
|
|
1914
|
+
} // add the last command
|
|
1915
|
+
|
|
1383
1916
|
|
|
1384
|
-
// add the last command
|
|
1385
1917
|
if (curArg.length > 0) {
|
|
1386
1918
|
if (args.length === params) {
|
|
1387
1919
|
// handle reused commands
|
|
1388
|
-
ret[ret.length] = {
|
|
1389
|
-
|
|
1920
|
+
ret[ret.length] = {
|
|
1921
|
+
cmd,
|
|
1922
|
+
args
|
|
1923
|
+
};
|
|
1924
|
+
args = [+curArg]; // handle assumed commands
|
|
1390
1925
|
|
|
1391
|
-
// handle assumed commands
|
|
1392
1926
|
if (cmd === 'M') {
|
|
1393
1927
|
cmd = 'L';
|
|
1394
1928
|
}
|
|
1929
|
+
|
|
1395
1930
|
if (cmd === 'm') {
|
|
1396
1931
|
cmd = 'l';
|
|
1397
1932
|
}
|
|
@@ -1400,18 +1935,20 @@ const parse = function (path) {
|
|
|
1400
1935
|
}
|
|
1401
1936
|
}
|
|
1402
1937
|
|
|
1403
|
-
ret[ret.length] = {
|
|
1404
|
-
|
|
1938
|
+
ret[ret.length] = {
|
|
1939
|
+
cmd,
|
|
1940
|
+
args
|
|
1941
|
+
};
|
|
1405
1942
|
return ret;
|
|
1406
1943
|
};
|
|
1407
1944
|
|
|
1408
1945
|
const apply = function (commands, doc) {
|
|
1409
1946
|
// current point, control point, and subpath starting point
|
|
1410
|
-
cx = cy = px = py = sx = sy = 0;
|
|
1947
|
+
cx = cy = px = py = sx = sy = 0; // run the commands
|
|
1411
1948
|
|
|
1412
|
-
// run the commands
|
|
1413
1949
|
for (let i = 0; i < commands.length; i++) {
|
|
1414
1950
|
const c = commands[i];
|
|
1951
|
+
|
|
1415
1952
|
if (typeof runners[c.cmd] === 'function') {
|
|
1416
1953
|
runners[c.cmd](doc, c.args);
|
|
1417
1954
|
}
|
|
@@ -1588,6 +2125,7 @@ const runners = {
|
|
|
1588
2125
|
cx = sx;
|
|
1589
2126
|
return cy = sy;
|
|
1590
2127
|
}
|
|
2128
|
+
|
|
1591
2129
|
};
|
|
1592
2130
|
|
|
1593
2131
|
const solveArc = function (doc, x, y, coords) {
|
|
@@ -1598,9 +2136,9 @@ const solveArc = function (doc, x, y, coords) {
|
|
|
1598
2136
|
const bez = segmentToBezier(...(seg || []));
|
|
1599
2137
|
doc.bezierCurveTo(...(bez || []));
|
|
1600
2138
|
}
|
|
1601
|
-
};
|
|
2139
|
+
}; // from Inkscape svgtopdf, thanks!
|
|
2140
|
+
|
|
1602
2141
|
|
|
1603
|
-
// from Inkscape svgtopdf, thanks!
|
|
1604
2142
|
const arcToSegments = function (x, y, rx, ry, large, sweep, rotateX, ox, oy) {
|
|
1605
2143
|
const th = rotateX * (Math.PI / 180);
|
|
1606
2144
|
const sin_th = Math.sin(th);
|
|
@@ -1610,6 +2148,7 @@ const arcToSegments = function (x, y, rx, ry, large, sweep, rotateX, ox, oy) {
|
|
|
1610
2148
|
px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
|
|
1611
2149
|
py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
|
|
1612
2150
|
let pl = px * px / (rx * rx) + py * py / (ry * ry);
|
|
2151
|
+
|
|
1613
2152
|
if (pl > 1) {
|
|
1614
2153
|
pl = Math.sqrt(pl);
|
|
1615
2154
|
rx *= pl;
|
|
@@ -1624,24 +2163,25 @@ const arcToSegments = function (x, y, rx, ry, large, sweep, rotateX, ox, oy) {
|
|
|
1624
2163
|
const y0 = a10 * ox + a11 * oy;
|
|
1625
2164
|
const x1 = a00 * x + a01 * y;
|
|
1626
2165
|
const y1 = a10 * x + a11 * y;
|
|
1627
|
-
|
|
1628
2166
|
const d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
|
|
1629
2167
|
let sfactor_sq = 1 / d - 0.25;
|
|
2168
|
+
|
|
1630
2169
|
if (sfactor_sq < 0) {
|
|
1631
2170
|
sfactor_sq = 0;
|
|
1632
2171
|
}
|
|
2172
|
+
|
|
1633
2173
|
let sfactor = Math.sqrt(sfactor_sq);
|
|
2174
|
+
|
|
1634
2175
|
if (sweep === large) {
|
|
1635
2176
|
sfactor = -sfactor;
|
|
1636
2177
|
}
|
|
1637
2178
|
|
|
1638
2179
|
const xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
|
|
1639
2180
|
const yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
|
|
1640
|
-
|
|
1641
2181
|
const th0 = Math.atan2(y0 - yc, x0 - xc);
|
|
1642
2182
|
const th1 = Math.atan2(y1 - yc, x1 - xc);
|
|
1643
|
-
|
|
1644
2183
|
let th_arc = th1 - th0;
|
|
2184
|
+
|
|
1645
2185
|
if (th_arc < 0 && sweep === 1) {
|
|
1646
2186
|
th_arc += 2 * Math.PI;
|
|
1647
2187
|
} else if (th_arc > 0 && sweep === 0) {
|
|
@@ -1665,7 +2205,6 @@ const segmentToBezier = function (cx, cy, th0, th1, rx, ry, sin_th, cos_th) {
|
|
|
1665
2205
|
const a01 = -sin_th * ry;
|
|
1666
2206
|
const a10 = sin_th * rx;
|
|
1667
2207
|
const a11 = cos_th * ry;
|
|
1668
|
-
|
|
1669
2208
|
const th_half = 0.5 * (th1 - th0);
|
|
1670
2209
|
const t = 8 / 3 * Math.sin(th_half * 0.5) * Math.sin(th_half * 0.5) / Math.sin(th_half);
|
|
1671
2210
|
const x1 = cx + Math.cos(th0) - t * Math.sin(th0);
|
|
@@ -1674,7 +2213,6 @@ const segmentToBezier = function (cx, cy, th0, th1, rx, ry, sin_th, cos_th) {
|
|
|
1674
2213
|
const y3 = cy + Math.sin(th1);
|
|
1675
2214
|
const x2 = x3 + t * Math.sin(th1);
|
|
1676
2215
|
const y2 = y3 - t * Math.cos(th1);
|
|
1677
|
-
|
|
1678
2216
|
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];
|
|
1679
2217
|
};
|
|
1680
2218
|
|
|
@@ -1683,22 +2221,26 @@ class SVGPath {
|
|
|
1683
2221
|
const commands = parse(path);
|
|
1684
2222
|
apply(commands, doc);
|
|
1685
2223
|
}
|
|
1686
|
-
}
|
|
1687
2224
|
|
|
1688
|
-
|
|
2225
|
+
}
|
|
1689
2226
|
|
|
1690
|
-
|
|
2227
|
+
const {
|
|
2228
|
+
number: number$1
|
|
2229
|
+
} = PDFObject; // This constant is used to approximate a symmetrical arc using a cubic
|
|
1691
2230
|
// Bezier curve.
|
|
2231
|
+
|
|
1692
2232
|
const KAPPA = 4.0 * ((Math.sqrt(2) - 1.0) / 3.0);
|
|
1693
2233
|
var VectorMixin = {
|
|
1694
2234
|
initVector() {
|
|
1695
2235
|
this._ctm = [1, 0, 0, 1, 0, 0]; // current transformation matrix
|
|
2236
|
+
|
|
1696
2237
|
return this._ctmStack = [];
|
|
1697
2238
|
},
|
|
1698
2239
|
|
|
1699
2240
|
save() {
|
|
1700
|
-
this._ctmStack.push(this._ctm.slice());
|
|
1701
|
-
|
|
2241
|
+
this._ctmStack.push(this._ctm.slice()); // TODO: save/restore colorspace and styles so not setting it unnessesarily all the time?
|
|
2242
|
+
|
|
2243
|
+
|
|
1702
2244
|
return this.addContent('q');
|
|
1703
2245
|
},
|
|
1704
2246
|
|
|
@@ -1725,6 +2267,7 @@ var VectorMixin = {
|
|
|
1725
2267
|
if (typeof c === 'string') {
|
|
1726
2268
|
c = this._CAP_STYLES[c.toUpperCase()];
|
|
1727
2269
|
}
|
|
2270
|
+
|
|
1728
2271
|
return this.addContent(`${c} J`);
|
|
1729
2272
|
},
|
|
1730
2273
|
|
|
@@ -1738,6 +2281,7 @@ var VectorMixin = {
|
|
|
1738
2281
|
if (typeof j === 'string') {
|
|
1739
2282
|
j = this._JOIN_STYLES[j.toUpperCase()];
|
|
1740
2283
|
}
|
|
2284
|
+
|
|
1741
2285
|
return this.addContent(`${j} j`);
|
|
1742
2286
|
},
|
|
1743
2287
|
|
|
@@ -1745,23 +2289,21 @@ var VectorMixin = {
|
|
|
1745
2289
|
return this.addContent(`${number$1(m)} M`);
|
|
1746
2290
|
},
|
|
1747
2291
|
|
|
1748
|
-
dash(length, options) {
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
if (length == null) {
|
|
1754
|
-
return this;
|
|
2292
|
+
dash(length, options = {}) {
|
|
2293
|
+
const originalLength = length;
|
|
2294
|
+
|
|
2295
|
+
if (!Array.isArray(length)) {
|
|
2296
|
+
length = [length, options.space || length];
|
|
1755
2297
|
}
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
const space = options.space != null ? options.space : length;
|
|
1762
|
-
phase = options.phase || 0;
|
|
1763
|
-
return this.addContent(`[${number$1(length)} ${number$1(space)}] ${number$1(phase)} d`);
|
|
2298
|
+
|
|
2299
|
+
const valid = length.every(x => Number.isFinite(x) && x > 0);
|
|
2300
|
+
|
|
2301
|
+
if (!valid) {
|
|
2302
|
+
throw new Error(`dash(${JSON.stringify(originalLength)}, ${JSON.stringify(options)}) invalid, lengths must be numeric and greater than zero`);
|
|
1764
2303
|
}
|
|
2304
|
+
|
|
2305
|
+
length = length.map(number$1).join(' ');
|
|
2306
|
+
return this.addContent(`[${length}] ${number$1(options.phase || 0)} d`);
|
|
1765
2307
|
},
|
|
1766
2308
|
|
|
1767
2309
|
undash() {
|
|
@@ -1792,11 +2334,10 @@ var VectorMixin = {
|
|
|
1792
2334
|
if (r == null) {
|
|
1793
2335
|
r = 0;
|
|
1794
2336
|
}
|
|
1795
|
-
r = Math.min(r, 0.5 * w, 0.5 * h);
|
|
1796
2337
|
|
|
1797
|
-
// amount to inset control points from corners (see `ellipse`)
|
|
1798
|
-
const c = r * (1.0 - KAPPA);
|
|
2338
|
+
r = Math.min(r, 0.5 * w, 0.5 * h); // amount to inset control points from corners (see `ellipse`)
|
|
1799
2339
|
|
|
2340
|
+
const c = r * (1.0 - KAPPA);
|
|
1800
2341
|
this.moveTo(x + r, y);
|
|
1801
2342
|
this.lineTo(x + w - r, y);
|
|
1802
2343
|
this.bezierCurveTo(x + w - c, y, x + w, y + c, x + w, y + r);
|
|
@@ -1814,6 +2355,7 @@ var VectorMixin = {
|
|
|
1814
2355
|
if (r2 == null) {
|
|
1815
2356
|
r2 = r1;
|
|
1816
2357
|
}
|
|
2358
|
+
|
|
1817
2359
|
x -= r1;
|
|
1818
2360
|
y -= r2;
|
|
1819
2361
|
const ox = r1 * KAPPA;
|
|
@@ -1822,7 +2364,6 @@ var VectorMixin = {
|
|
|
1822
2364
|
const ye = y + r2 * 2;
|
|
1823
2365
|
const xm = x + r1;
|
|
1824
2366
|
const ym = y + r2;
|
|
1825
|
-
|
|
1826
2367
|
this.moveTo(x, ym);
|
|
1827
2368
|
this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
|
|
1828
2369
|
this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
|
|
@@ -1839,9 +2380,9 @@ var VectorMixin = {
|
|
|
1839
2380
|
if (anticlockwise == null) {
|
|
1840
2381
|
anticlockwise = false;
|
|
1841
2382
|
}
|
|
2383
|
+
|
|
1842
2384
|
const TWO_PI = 2.0 * Math.PI;
|
|
1843
2385
|
const HALF_PI = 0.5 * Math.PI;
|
|
1844
|
-
|
|
1845
2386
|
let deltaAng = endAngle - startAngle;
|
|
1846
2387
|
|
|
1847
2388
|
if (Math.abs(deltaAng) > TWO_PI) {
|
|
@@ -1856,40 +2397,32 @@ var VectorMixin = {
|
|
|
1856
2397
|
const numSegs = Math.ceil(Math.abs(deltaAng) / HALF_PI);
|
|
1857
2398
|
const segAng = deltaAng / numSegs;
|
|
1858
2399
|
const handleLen = segAng / HALF_PI * KAPPA * radius;
|
|
1859
|
-
let curAng = startAngle;
|
|
2400
|
+
let curAng = startAngle; // component distances between anchor point and control point
|
|
1860
2401
|
|
|
1861
|
-
// component distances between anchor point and control point
|
|
1862
2402
|
let deltaCx = -Math.sin(curAng) * handleLen;
|
|
1863
|
-
let deltaCy = Math.cos(curAng) * handleLen;
|
|
2403
|
+
let deltaCy = Math.cos(curAng) * handleLen; // anchor point
|
|
1864
2404
|
|
|
1865
|
-
// anchor point
|
|
1866
2405
|
let ax = x + Math.cos(curAng) * radius;
|
|
1867
|
-
let ay = y + Math.sin(curAng) * radius;
|
|
2406
|
+
let ay = y + Math.sin(curAng) * radius; // calculate and render segments
|
|
1868
2407
|
|
|
1869
|
-
// calculate and render segments
|
|
1870
2408
|
this.moveTo(ax, ay);
|
|
1871
2409
|
|
|
1872
2410
|
for (let segIdx = 0; segIdx < numSegs; segIdx++) {
|
|
1873
2411
|
// starting control point
|
|
1874
2412
|
const cp1x = ax + deltaCx;
|
|
1875
|
-
const cp1y = ay + deltaCy;
|
|
2413
|
+
const cp1y = ay + deltaCy; // step angle
|
|
1876
2414
|
|
|
1877
|
-
//
|
|
1878
|
-
curAng += segAng;
|
|
2415
|
+
curAng += segAng; // next anchor point
|
|
1879
2416
|
|
|
1880
|
-
// next anchor point
|
|
1881
2417
|
ax = x + Math.cos(curAng) * radius;
|
|
1882
|
-
ay = y + Math.sin(curAng) * radius;
|
|
2418
|
+
ay = y + Math.sin(curAng) * radius; // next control point delta
|
|
1883
2419
|
|
|
1884
|
-
// next control point delta
|
|
1885
2420
|
deltaCx = -Math.sin(curAng) * handleLen;
|
|
1886
|
-
deltaCy = Math.cos(curAng) * handleLen;
|
|
2421
|
+
deltaCy = Math.cos(curAng) * handleLen; // ending control point
|
|
1887
2422
|
|
|
1888
|
-
// ending control point
|
|
1889
2423
|
const cp2x = ax - deltaCx;
|
|
1890
|
-
const cp2y = ay - deltaCy;
|
|
2424
|
+
const cp2y = ay - deltaCy; // render segment
|
|
1891
2425
|
|
|
1892
|
-
// render segment
|
|
1893
2426
|
this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, ax, ay);
|
|
1894
2427
|
}
|
|
1895
2428
|
|
|
@@ -1898,9 +2431,11 @@ var VectorMixin = {
|
|
|
1898
2431
|
|
|
1899
2432
|
polygon(...points) {
|
|
1900
2433
|
this.moveTo(...(points.shift() || []));
|
|
2434
|
+
|
|
1901
2435
|
for (let point of points) {
|
|
1902
2436
|
this.lineTo(...(point || []));
|
|
1903
2437
|
}
|
|
2438
|
+
|
|
1904
2439
|
return this.closePath();
|
|
1905
2440
|
},
|
|
1906
2441
|
|
|
@@ -1926,6 +2461,7 @@ var VectorMixin = {
|
|
|
1926
2461
|
if (color) {
|
|
1927
2462
|
this.fillColor(color);
|
|
1928
2463
|
}
|
|
2464
|
+
|
|
1929
2465
|
return this.addContent(`f${this._windingRule(rule)}`);
|
|
1930
2466
|
},
|
|
1931
2467
|
|
|
@@ -1933,6 +2469,7 @@ var VectorMixin = {
|
|
|
1933
2469
|
if (color) {
|
|
1934
2470
|
this.strokeColor(color);
|
|
1935
2471
|
}
|
|
2472
|
+
|
|
1936
2473
|
return this.addContent('S');
|
|
1937
2474
|
},
|
|
1938
2475
|
|
|
@@ -1940,7 +2477,9 @@ var VectorMixin = {
|
|
|
1940
2477
|
if (strokeColor == null) {
|
|
1941
2478
|
strokeColor = fillColor;
|
|
1942
2479
|
}
|
|
2480
|
+
|
|
1943
2481
|
const isFillRule = /(even-?odd)|(non-?zero)/;
|
|
2482
|
+
|
|
1944
2483
|
if (isFillRule.test(fillColor)) {
|
|
1945
2484
|
rule = fillColor;
|
|
1946
2485
|
fillColor = null;
|
|
@@ -1973,7 +2512,6 @@ var VectorMixin = {
|
|
|
1973
2512
|
m[3] = m1 * m21 + m3 * m22;
|
|
1974
2513
|
m[4] = m0 * dx + m2 * dy + m4;
|
|
1975
2514
|
m[5] = m1 * dx + m3 * dy + m5;
|
|
1976
|
-
|
|
1977
2515
|
const values = [m11, m12, m21, m22, dx, dy].map(v => number$1(v)).join(' ');
|
|
1978
2516
|
return this.addContent(`${values} cm`);
|
|
1979
2517
|
},
|
|
@@ -1982,11 +2520,8 @@ var VectorMixin = {
|
|
|
1982
2520
|
return this.transform(1, 0, 0, 1, x, y);
|
|
1983
2521
|
},
|
|
1984
2522
|
|
|
1985
|
-
rotate(angle, options) {
|
|
2523
|
+
rotate(angle, options = {}) {
|
|
1986
2524
|
let y;
|
|
1987
|
-
if (options == null) {
|
|
1988
|
-
options = {};
|
|
1989
|
-
}
|
|
1990
2525
|
const rad = angle * Math.PI / 180;
|
|
1991
2526
|
const cos = Math.cos(rad);
|
|
1992
2527
|
const sin = Math.sin(rad);
|
|
@@ -2003,20 +2538,20 @@ var VectorMixin = {
|
|
|
2003
2538
|
return this.transform(cos, sin, -sin, cos, x, y);
|
|
2004
2539
|
},
|
|
2005
2540
|
|
|
2006
|
-
scale(xFactor, yFactor, options) {
|
|
2541
|
+
scale(xFactor, yFactor, options = {}) {
|
|
2007
2542
|
let y;
|
|
2543
|
+
|
|
2008
2544
|
if (yFactor == null) {
|
|
2009
2545
|
yFactor = xFactor;
|
|
2010
2546
|
}
|
|
2011
|
-
|
|
2012
|
-
options = {};
|
|
2013
|
-
}
|
|
2547
|
+
|
|
2014
2548
|
if (typeof yFactor === 'object') {
|
|
2015
2549
|
options = yFactor;
|
|
2016
2550
|
yFactor = xFactor;
|
|
2017
2551
|
}
|
|
2018
2552
|
|
|
2019
2553
|
let x = y = 0;
|
|
2554
|
+
|
|
2020
2555
|
if (options.origin != null) {
|
|
2021
2556
|
[x, y] = options.origin;
|
|
2022
2557
|
x -= xFactor * x;
|
|
@@ -2025,6 +2560,7 @@ var VectorMixin = {
|
|
|
2025
2560
|
|
|
2026
2561
|
return this.transform(xFactor, 0, 0, yFactor, x, y);
|
|
2027
2562
|
}
|
|
2563
|
+
|
|
2028
2564
|
};
|
|
2029
2565
|
|
|
2030
2566
|
const WIN_ANSI_MAP = {
|
|
@@ -2056,7 +2592,6 @@ const WIN_ANSI_MAP = {
|
|
|
2056
2592
|
381: 142,
|
|
2057
2593
|
382: 158
|
|
2058
2594
|
};
|
|
2059
|
-
|
|
2060
2595
|
const characters = `\
|
|
2061
2596
|
.notdef .notdef .notdef .notdef
|
|
2062
2597
|
.notdef .notdef .notdef .notdef
|
|
@@ -2142,10 +2677,10 @@ class AFMFont {
|
|
|
2142
2677
|
this.glyphWidths = {};
|
|
2143
2678
|
this.boundingBoxes = {};
|
|
2144
2679
|
this.kernPairs = {};
|
|
2680
|
+
this.parse(); // todo: remove charWidths since appears to not be used
|
|
2145
2681
|
|
|
2146
|
-
this.parse();
|
|
2147
|
-
// todo: remove charWidths since appears to not be used
|
|
2148
2682
|
this.charWidths = new Array(256);
|
|
2683
|
+
|
|
2149
2684
|
for (let char = 0; char <= 255; char++) {
|
|
2150
2685
|
this.charWidths[char] = this.glyphWidths[characters[char]];
|
|
2151
2686
|
}
|
|
@@ -2160,9 +2695,11 @@ class AFMFont {
|
|
|
2160
2695
|
|
|
2161
2696
|
parse() {
|
|
2162
2697
|
let section = '';
|
|
2698
|
+
|
|
2163
2699
|
for (let line of this.contents.split('\n')) {
|
|
2164
2700
|
var match;
|
|
2165
2701
|
var a;
|
|
2702
|
+
|
|
2166
2703
|
if (match = line.match(/^Start(\w+)/)) {
|
|
2167
2704
|
section = match[1];
|
|
2168
2705
|
continue;
|
|
@@ -2181,25 +2718,30 @@ class AFMFont {
|
|
|
2181
2718
|
if (!Array.isArray(a)) {
|
|
2182
2719
|
a = this.attributes[key] = [a];
|
|
2183
2720
|
}
|
|
2721
|
+
|
|
2184
2722
|
a.push(value);
|
|
2185
2723
|
} else {
|
|
2186
2724
|
this.attributes[key] = value;
|
|
2187
2725
|
}
|
|
2726
|
+
|
|
2188
2727
|
break;
|
|
2189
2728
|
|
|
2190
2729
|
case 'CharMetrics':
|
|
2191
2730
|
if (!/^CH?\s/.test(line)) {
|
|
2192
2731
|
continue;
|
|
2193
2732
|
}
|
|
2733
|
+
|
|
2194
2734
|
var name = line.match(/\bN\s+(\.?\w+)\s*;/)[1];
|
|
2195
2735
|
this.glyphWidths[name] = +line.match(/\bWX\s+(\d+)\s*;/)[1];
|
|
2196
2736
|
break;
|
|
2197
2737
|
|
|
2198
2738
|
case 'KernPairs':
|
|
2199
2739
|
match = line.match(/^KPX\s+(\.?\w+)\s+(\.?\w+)\s+(-?\d+)/);
|
|
2740
|
+
|
|
2200
2741
|
if (match) {
|
|
2201
2742
|
this.kernPairs[match[1] + '\0' + match[2]] = parseInt(match[3]);
|
|
2202
2743
|
}
|
|
2744
|
+
|
|
2203
2745
|
break;
|
|
2204
2746
|
}
|
|
2205
2747
|
}
|
|
@@ -2207,6 +2749,7 @@ class AFMFont {
|
|
|
2207
2749
|
|
|
2208
2750
|
encodeText(text) {
|
|
2209
2751
|
const res = [];
|
|
2752
|
+
|
|
2210
2753
|
for (let i = 0, len = text.length; i < len; i++) {
|
|
2211
2754
|
let char = text.charCodeAt(i);
|
|
2212
2755
|
char = WIN_ANSI_MAP[char] || char;
|
|
@@ -2250,6 +2793,7 @@ class AFMFont {
|
|
|
2250
2793
|
|
|
2251
2794
|
return advances;
|
|
2252
2795
|
}
|
|
2796
|
+
|
|
2253
2797
|
}
|
|
2254
2798
|
|
|
2255
2799
|
class PDFFont {
|
|
@@ -2284,55 +2828,70 @@ class PDFFont {
|
|
|
2284
2828
|
if (includeGap == null) {
|
|
2285
2829
|
includeGap = false;
|
|
2286
2830
|
}
|
|
2831
|
+
|
|
2287
2832
|
const gap = includeGap ? this.lineGap : 0;
|
|
2288
2833
|
return (this.ascender + gap - this.descender) / 1000 * size;
|
|
2289
2834
|
}
|
|
2835
|
+
|
|
2290
2836
|
}
|
|
2291
2837
|
|
|
2292
|
-
// This insanity is so bundlers can inline the font files
|
|
2293
2838
|
const STANDARD_FONTS = {
|
|
2294
2839
|
Courier() {
|
|
2295
2840
|
return fs.readFileSync(__dirname + '/data/Courier.afm', 'utf8');
|
|
2296
2841
|
},
|
|
2842
|
+
|
|
2297
2843
|
'Courier-Bold'() {
|
|
2298
2844
|
return fs.readFileSync(__dirname + '/data/Courier-Bold.afm', 'utf8');
|
|
2299
2845
|
},
|
|
2846
|
+
|
|
2300
2847
|
'Courier-Oblique'() {
|
|
2301
2848
|
return fs.readFileSync(__dirname + '/data/Courier-Oblique.afm', 'utf8');
|
|
2302
2849
|
},
|
|
2850
|
+
|
|
2303
2851
|
'Courier-BoldOblique'() {
|
|
2304
2852
|
return fs.readFileSync(__dirname + '/data/Courier-BoldOblique.afm', 'utf8');
|
|
2305
2853
|
},
|
|
2854
|
+
|
|
2306
2855
|
Helvetica() {
|
|
2307
2856
|
return fs.readFileSync(__dirname + '/data/Helvetica.afm', 'utf8');
|
|
2308
2857
|
},
|
|
2858
|
+
|
|
2309
2859
|
'Helvetica-Bold'() {
|
|
2310
2860
|
return fs.readFileSync(__dirname + '/data/Helvetica-Bold.afm', 'utf8');
|
|
2311
2861
|
},
|
|
2862
|
+
|
|
2312
2863
|
'Helvetica-Oblique'() {
|
|
2313
2864
|
return fs.readFileSync(__dirname + '/data/Helvetica-Oblique.afm', 'utf8');
|
|
2314
2865
|
},
|
|
2866
|
+
|
|
2315
2867
|
'Helvetica-BoldOblique'() {
|
|
2316
2868
|
return fs.readFileSync(__dirname + '/data/Helvetica-BoldOblique.afm', 'utf8');
|
|
2317
2869
|
},
|
|
2870
|
+
|
|
2318
2871
|
'Times-Roman'() {
|
|
2319
2872
|
return fs.readFileSync(__dirname + '/data/Times-Roman.afm', 'utf8');
|
|
2320
2873
|
},
|
|
2874
|
+
|
|
2321
2875
|
'Times-Bold'() {
|
|
2322
2876
|
return fs.readFileSync(__dirname + '/data/Times-Bold.afm', 'utf8');
|
|
2323
2877
|
},
|
|
2878
|
+
|
|
2324
2879
|
'Times-Italic'() {
|
|
2325
2880
|
return fs.readFileSync(__dirname + '/data/Times-Italic.afm', 'utf8');
|
|
2326
2881
|
},
|
|
2882
|
+
|
|
2327
2883
|
'Times-BoldItalic'() {
|
|
2328
2884
|
return fs.readFileSync(__dirname + '/data/Times-BoldItalic.afm', 'utf8');
|
|
2329
2885
|
},
|
|
2886
|
+
|
|
2330
2887
|
Symbol() {
|
|
2331
2888
|
return fs.readFileSync(__dirname + '/data/Symbol.afm', 'utf8');
|
|
2332
2889
|
},
|
|
2890
|
+
|
|
2333
2891
|
ZapfDingbats() {
|
|
2334
2892
|
return fs.readFileSync(__dirname + '/data/ZapfDingbats.afm', 'utf8');
|
|
2335
2893
|
}
|
|
2894
|
+
|
|
2336
2895
|
};
|
|
2337
2896
|
|
|
2338
2897
|
class StandardFont extends PDFFont {
|
|
@@ -2359,7 +2918,6 @@ class StandardFont extends PDFFont {
|
|
|
2359
2918
|
Subtype: 'Type1',
|
|
2360
2919
|
Encoding: 'WinAnsiEncoding'
|
|
2361
2920
|
};
|
|
2362
|
-
|
|
2363
2921
|
return this.dictionary.end();
|
|
2364
2922
|
}
|
|
2365
2923
|
|
|
@@ -2368,6 +2926,7 @@ class StandardFont extends PDFFont {
|
|
|
2368
2926
|
const glyphs = this.font.glyphsForString(`${text}`);
|
|
2369
2927
|
const advances = this.font.advancesForGlyphs(glyphs);
|
|
2370
2928
|
const positions = [];
|
|
2929
|
+
|
|
2371
2930
|
for (let i = 0; i < glyphs.length; i++) {
|
|
2372
2931
|
const glyph = glyphs[i];
|
|
2373
2932
|
positions.push({
|
|
@@ -2385,8 +2944,8 @@ class StandardFont extends PDFFont {
|
|
|
2385
2944
|
widthOfString(string, size) {
|
|
2386
2945
|
const glyphs = this.font.glyphsForString(`${string}`);
|
|
2387
2946
|
const advances = this.font.advancesForGlyphs(glyphs);
|
|
2388
|
-
|
|
2389
2947
|
let width = 0;
|
|
2948
|
+
|
|
2390
2949
|
for (let advance of advances) {
|
|
2391
2950
|
width += advance;
|
|
2392
2951
|
}
|
|
@@ -2398,6 +2957,7 @@ class StandardFont extends PDFFont {
|
|
|
2398
2957
|
static isStandardFont(name) {
|
|
2399
2958
|
return name in STANDARD_FONTS;
|
|
2400
2959
|
}
|
|
2960
|
+
|
|
2401
2961
|
}
|
|
2402
2962
|
|
|
2403
2963
|
const toHex = function (num) {
|
|
@@ -2413,7 +2973,6 @@ class EmbeddedFont extends PDFFont {
|
|
|
2413
2973
|
this.subset = this.font.createSubset();
|
|
2414
2974
|
this.unicode = [[0]];
|
|
2415
2975
|
this.widths = [this.font.getGlyph(0).advanceWidth];
|
|
2416
|
-
|
|
2417
2976
|
this.name = this.font.postscriptName;
|
|
2418
2977
|
this.scale = 1000 / this.font.unitsPerEm;
|
|
2419
2978
|
this.ascender = this.font.ascent * this.scale;
|
|
@@ -2429,11 +2988,11 @@ class EmbeddedFont extends PDFFont {
|
|
|
2429
2988
|
}
|
|
2430
2989
|
|
|
2431
2990
|
layoutRun(text, features) {
|
|
2432
|
-
const run = this.font.layout(text, features);
|
|
2991
|
+
const run = this.font.layout(text, features); // Normalize position values
|
|
2433
2992
|
|
|
2434
|
-
// Normalize position values
|
|
2435
2993
|
for (let i = 0; i < run.positions.length; i++) {
|
|
2436
2994
|
const position = run.positions[i];
|
|
2995
|
+
|
|
2437
2996
|
for (let key in position) {
|
|
2438
2997
|
position[key] *= this.scale;
|
|
2439
2998
|
}
|
|
@@ -2448,7 +3007,9 @@ class EmbeddedFont extends PDFFont {
|
|
|
2448
3007
|
if (!this.layoutCache) {
|
|
2449
3008
|
return this.layoutRun(text);
|
|
2450
3009
|
}
|
|
3010
|
+
|
|
2451
3011
|
let cached;
|
|
3012
|
+
|
|
2452
3013
|
if (cached = this.layoutCache[text]) {
|
|
2453
3014
|
return cached;
|
|
2454
3015
|
}
|
|
@@ -2466,16 +3027,18 @@ class EmbeddedFont extends PDFFont {
|
|
|
2466
3027
|
|
|
2467
3028
|
let glyphs = onlyWidth ? null : [];
|
|
2468
3029
|
let positions = onlyWidth ? null : [];
|
|
2469
|
-
let advanceWidth = 0;
|
|
2470
|
-
|
|
2471
|
-
// Split the string by words to increase cache efficiency.
|
|
3030
|
+
let advanceWidth = 0; // Split the string by words to increase cache efficiency.
|
|
2472
3031
|
// For this purpose, spaces and tabs are a good enough delimeter.
|
|
3032
|
+
|
|
2473
3033
|
let last = 0;
|
|
2474
3034
|
let index = 0;
|
|
3035
|
+
|
|
2475
3036
|
while (index <= text.length) {
|
|
2476
3037
|
var needle;
|
|
3038
|
+
|
|
2477
3039
|
if (index === text.length && last < index || (needle = text.charAt(index), [' ', '\t'].includes(needle))) {
|
|
2478
3040
|
const run = this.layoutCached(text.slice(last, ++index));
|
|
3041
|
+
|
|
2479
3042
|
if (!onlyWidth) {
|
|
2480
3043
|
glyphs = glyphs.concat(run.glyphs);
|
|
2481
3044
|
positions = positions.concat(run.positions);
|
|
@@ -2488,13 +3051,20 @@ class EmbeddedFont extends PDFFont {
|
|
|
2488
3051
|
}
|
|
2489
3052
|
}
|
|
2490
3053
|
|
|
2491
|
-
return {
|
|
3054
|
+
return {
|
|
3055
|
+
glyphs,
|
|
3056
|
+
positions,
|
|
3057
|
+
advanceWidth
|
|
3058
|
+
};
|
|
2492
3059
|
}
|
|
2493
3060
|
|
|
2494
3061
|
encode(text, features) {
|
|
2495
|
-
const {
|
|
2496
|
-
|
|
3062
|
+
const {
|
|
3063
|
+
glyphs,
|
|
3064
|
+
positions
|
|
3065
|
+
} = this.layout(text, features);
|
|
2497
3066
|
const res = [];
|
|
3067
|
+
|
|
2498
3068
|
for (let i = 0; i < glyphs.length; i++) {
|
|
2499
3069
|
const glyph = glyphs[i];
|
|
2500
3070
|
const gid = this.subset.includeGlyph(glyph.id);
|
|
@@ -2503,6 +3073,7 @@ class EmbeddedFont extends PDFFont {
|
|
|
2503
3073
|
if (this.widths[gid] == null) {
|
|
2504
3074
|
this.widths[gid] = glyph.advanceWidth * this.scale;
|
|
2505
3075
|
}
|
|
3076
|
+
|
|
2506
3077
|
if (this.unicode[gid] == null) {
|
|
2507
3078
|
this.unicode[gid] = glyph.codePoints;
|
|
2508
3079
|
}
|
|
@@ -2526,28 +3097,33 @@ class EmbeddedFont extends PDFFont {
|
|
|
2526
3097
|
}
|
|
2527
3098
|
|
|
2528
3099
|
this.subset.encodeStream().on('data', data => fontFile.write(data)).on('end', () => fontFile.end());
|
|
2529
|
-
|
|
2530
3100
|
const familyClass = ((this.font['OS/2'] != null ? this.font['OS/2'].sFamilyClass : undefined) || 0) >> 8;
|
|
2531
3101
|
let flags = 0;
|
|
3102
|
+
|
|
2532
3103
|
if (this.font.post.isFixedPitch) {
|
|
2533
3104
|
flags |= 1 << 0;
|
|
2534
3105
|
}
|
|
3106
|
+
|
|
2535
3107
|
if (1 <= familyClass && familyClass <= 7) {
|
|
2536
3108
|
flags |= 1 << 1;
|
|
2537
3109
|
}
|
|
3110
|
+
|
|
2538
3111
|
flags |= 1 << 2; // assume the font uses non-latin characters
|
|
3112
|
+
|
|
2539
3113
|
if (familyClass === 10) {
|
|
2540
3114
|
flags |= 1 << 3;
|
|
2541
3115
|
}
|
|
3116
|
+
|
|
2542
3117
|
if (this.font.head.macStyle.italic) {
|
|
2543
3118
|
flags |= 1 << 6;
|
|
2544
|
-
}
|
|
3119
|
+
} // generate a tag (6 uppercase letters. 16 is the char code offset from '1' to 'A'. 74 will map to 'Z')
|
|
3120
|
+
|
|
2545
3121
|
|
|
2546
|
-
// generate a tag (6 uppercase letters. 16 is the char code offset from '1' to 'A'. 74 will map to 'Z')
|
|
2547
3122
|
const tag = [1, 2, 3, 4, 5, 6].map(i => String.fromCharCode((this.id.charCodeAt(i) || 74) + 16)).join('');
|
|
2548
3123
|
const name = tag + '+' + this.font.postscriptName;
|
|
2549
|
-
|
|
2550
|
-
|
|
3124
|
+
const {
|
|
3125
|
+
bbox
|
|
3126
|
+
} = this.font;
|
|
2551
3127
|
const descriptor = this.document.ref({
|
|
2552
3128
|
Type: 'FontDescriptor',
|
|
2553
3129
|
FontName: name,
|
|
@@ -2568,7 +3144,6 @@ class EmbeddedFont extends PDFFont {
|
|
|
2568
3144
|
}
|
|
2569
3145
|
|
|
2570
3146
|
descriptor.end();
|
|
2571
|
-
|
|
2572
3147
|
const descendantFont = this.document.ref({
|
|
2573
3148
|
Type: 'Font',
|
|
2574
3149
|
Subtype: isCFF ? 'CIDFontType0' : 'CIDFontType2',
|
|
@@ -2581,9 +3156,7 @@ class EmbeddedFont extends PDFFont {
|
|
|
2581
3156
|
FontDescriptor: descriptor,
|
|
2582
3157
|
W: [0, this.widths]
|
|
2583
3158
|
});
|
|
2584
|
-
|
|
2585
3159
|
descendantFont.end();
|
|
2586
|
-
|
|
2587
3160
|
this.dictionary.data = {
|
|
2588
3161
|
Type: 'Font',
|
|
2589
3162
|
Subtype: 'Type0',
|
|
@@ -2592,21 +3165,19 @@ class EmbeddedFont extends PDFFont {
|
|
|
2592
3165
|
DescendantFonts: [descendantFont],
|
|
2593
3166
|
ToUnicode: this.toUnicodeCmap()
|
|
2594
3167
|
};
|
|
2595
|
-
|
|
2596
3168
|
return this.dictionary.end();
|
|
2597
|
-
}
|
|
2598
|
-
|
|
2599
|
-
// Maps the glyph ids encoded in the PDF back to unicode strings
|
|
3169
|
+
} // Maps the glyph ids encoded in the PDF back to unicode strings
|
|
2600
3170
|
// Because of ligature substitutions and the like, there may be one or more
|
|
2601
3171
|
// unicode characters represented by each glyph.
|
|
3172
|
+
|
|
3173
|
+
|
|
2602
3174
|
toUnicodeCmap() {
|
|
2603
3175
|
const cmap = this.document.ref();
|
|
2604
|
-
|
|
2605
3176
|
const entries = [];
|
|
3177
|
+
|
|
2606
3178
|
for (let codePoints of this.unicode) {
|
|
2607
|
-
const encoded = [];
|
|
3179
|
+
const encoded = []; // encode codePoints to utf16
|
|
2608
3180
|
|
|
2609
|
-
// encode codePoints to utf16
|
|
2610
3181
|
for (let value of codePoints) {
|
|
2611
3182
|
if (value > 0xffff) {
|
|
2612
3183
|
value -= 0x10000;
|
|
@@ -2642,14 +3213,15 @@ CMapName currentdict /CMap defineresource pop
|
|
|
2642
3213
|
end
|
|
2643
3214
|
end\
|
|
2644
3215
|
`);
|
|
2645
|
-
|
|
2646
3216
|
return cmap;
|
|
2647
3217
|
}
|
|
3218
|
+
|
|
2648
3219
|
}
|
|
2649
3220
|
|
|
2650
3221
|
class PDFFontFactory {
|
|
2651
3222
|
static open(document, src, family, id) {
|
|
2652
3223
|
let font;
|
|
3224
|
+
|
|
2653
3225
|
if (typeof src === 'string') {
|
|
2654
3226
|
if (StandardFont.isStandardFont(src)) {
|
|
2655
3227
|
return new StandardFont(document, src, id);
|
|
@@ -2657,6 +3229,7 @@ class PDFFontFactory {
|
|
|
2657
3229
|
|
|
2658
3230
|
src = fs.readFileSync(src);
|
|
2659
3231
|
}
|
|
3232
|
+
|
|
2660
3233
|
if (Buffer.isBuffer(src)) {
|
|
2661
3234
|
font = fontkit.create(src, family);
|
|
2662
3235
|
} else if (src instanceof Uint8Array) {
|
|
@@ -2671,21 +3244,19 @@ class PDFFontFactory {
|
|
|
2671
3244
|
|
|
2672
3245
|
return new EmbeddedFont(document, font, id);
|
|
2673
3246
|
}
|
|
3247
|
+
|
|
2674
3248
|
}
|
|
2675
3249
|
|
|
2676
3250
|
var FontsMixin = {
|
|
2677
3251
|
initFonts(defaultFont = 'Helvetica') {
|
|
2678
3252
|
// Lookup table for embedded fonts
|
|
2679
3253
|
this._fontFamilies = {};
|
|
2680
|
-
this._fontCount = 0;
|
|
3254
|
+
this._fontCount = 0; // Font state
|
|
2681
3255
|
|
|
2682
|
-
// Font state
|
|
2683
3256
|
this._fontSize = 12;
|
|
2684
3257
|
this._font = null;
|
|
3258
|
+
this._registeredFonts = {}; // Set the default font
|
|
2685
3259
|
|
|
2686
|
-
this._registeredFonts = {};
|
|
2687
|
-
|
|
2688
|
-
// Set the default font
|
|
2689
3260
|
if (defaultFont) {
|
|
2690
3261
|
this.font(defaultFont);
|
|
2691
3262
|
}
|
|
@@ -2693,17 +3264,22 @@ var FontsMixin = {
|
|
|
2693
3264
|
|
|
2694
3265
|
font(src, family, size) {
|
|
2695
3266
|
let cacheKey, font;
|
|
3267
|
+
|
|
2696
3268
|
if (typeof family === 'number') {
|
|
2697
3269
|
size = family;
|
|
2698
3270
|
family = null;
|
|
2699
|
-
}
|
|
3271
|
+
} // check registered fonts if src is a string
|
|
3272
|
+
|
|
2700
3273
|
|
|
2701
|
-
// check registered fonts if src is a string
|
|
2702
3274
|
if (typeof src === 'string' && this._registeredFonts[src]) {
|
|
2703
3275
|
cacheKey = src;
|
|
2704
|
-
({
|
|
3276
|
+
({
|
|
3277
|
+
src,
|
|
3278
|
+
family
|
|
3279
|
+
} = this._registeredFonts[src]);
|
|
2705
3280
|
} else {
|
|
2706
3281
|
cacheKey = family || src;
|
|
3282
|
+
|
|
2707
3283
|
if (typeof cacheKey !== 'string') {
|
|
2708
3284
|
cacheKey = null;
|
|
2709
3285
|
}
|
|
@@ -2711,26 +3287,25 @@ var FontsMixin = {
|
|
|
2711
3287
|
|
|
2712
3288
|
if (size != null) {
|
|
2713
3289
|
this.fontSize(size);
|
|
2714
|
-
}
|
|
3290
|
+
} // fast path: check if the font is already in the PDF
|
|
3291
|
+
|
|
2715
3292
|
|
|
2716
|
-
// fast path: check if the font is already in the PDF
|
|
2717
3293
|
if (font = this._fontFamilies[cacheKey]) {
|
|
2718
3294
|
this._font = font;
|
|
2719
3295
|
return this;
|
|
2720
|
-
}
|
|
3296
|
+
} // load the font
|
|
2721
3297
|
|
|
2722
|
-
// load the font
|
|
2723
|
-
const id = `F${++this._fontCount}`;
|
|
2724
|
-
this._font = PDFFontFactory.open(this, src, family, id);
|
|
2725
3298
|
|
|
2726
|
-
|
|
3299
|
+
const id = `F${++this._fontCount}`;
|
|
3300
|
+
this._font = PDFFontFactory.open(this, src, family, id); // check for existing font familes with the same name already in the PDF
|
|
2727
3301
|
// useful if the font was passed as a buffer
|
|
3302
|
+
|
|
2728
3303
|
if (font = this._fontFamilies[this._font.name]) {
|
|
2729
3304
|
this._font = font;
|
|
2730
3305
|
return this;
|
|
2731
|
-
}
|
|
3306
|
+
} // save the font for reuse later
|
|
3307
|
+
|
|
2732
3308
|
|
|
2733
|
-
// save the font for reuse later
|
|
2734
3309
|
if (cacheKey) {
|
|
2735
3310
|
this._fontFamilies[cacheKey] = this._font;
|
|
2736
3311
|
}
|
|
@@ -2751,6 +3326,7 @@ var FontsMixin = {
|
|
|
2751
3326
|
if (includeGap == null) {
|
|
2752
3327
|
includeGap = false;
|
|
2753
3328
|
}
|
|
3329
|
+
|
|
2754
3330
|
return this._font.lineHeight(this._fontSize, includeGap);
|
|
2755
3331
|
},
|
|
2756
3332
|
|
|
@@ -2759,9 +3335,9 @@ var FontsMixin = {
|
|
|
2759
3335
|
src,
|
|
2760
3336
|
family
|
|
2761
3337
|
};
|
|
2762
|
-
|
|
2763
3338
|
return this;
|
|
2764
3339
|
}
|
|
3340
|
+
|
|
2765
3341
|
};
|
|
2766
3342
|
|
|
2767
3343
|
class LineWrapper extends events.EventEmitter {
|
|
@@ -2773,6 +3349,7 @@ class LineWrapper extends events.EventEmitter {
|
|
|
2773
3349
|
this.wordSpacing = options.wordSpacing === 0;
|
|
2774
3350
|
this.columns = options.columns || 1;
|
|
2775
3351
|
this.columnGap = options.columnGap != null ? options.columnGap : 18; // 1/4 inch
|
|
3352
|
+
|
|
2776
3353
|
this.lineWidth = (options.width - this.columnGap * (this.columns - 1)) / this.columns;
|
|
2777
3354
|
this.spaceLeft = this.lineWidth;
|
|
2778
3355
|
this.startX = this.document.x;
|
|
@@ -2780,17 +3357,16 @@ class LineWrapper extends events.EventEmitter {
|
|
|
2780
3357
|
this.column = 1;
|
|
2781
3358
|
this.ellipsis = options.ellipsis;
|
|
2782
3359
|
this.continuedX = 0;
|
|
2783
|
-
this.features = options.features;
|
|
3360
|
+
this.features = options.features; // calculate the maximum Y position the text can appear at
|
|
2784
3361
|
|
|
2785
|
-
// calculate the maximum Y position the text can appear at
|
|
2786
3362
|
if (options.height != null) {
|
|
2787
3363
|
this.height = options.height;
|
|
2788
3364
|
this.maxY = this.startY + options.height;
|
|
2789
3365
|
} else {
|
|
2790
3366
|
this.maxY = this.document.page.maxY();
|
|
2791
|
-
}
|
|
3367
|
+
} // handle paragraph indents
|
|
3368
|
+
|
|
2792
3369
|
|
|
2793
|
-
// handle paragraph indents
|
|
2794
3370
|
this.on('firstLine', options => {
|
|
2795
3371
|
// if this is the first line of the text segment, and
|
|
2796
3372
|
// we're continuing where we left off, indent that much
|
|
@@ -2798,27 +3374,30 @@ class LineWrapper extends events.EventEmitter {
|
|
|
2798
3374
|
const indent = this.continuedX || this.indent;
|
|
2799
3375
|
this.document.x += indent;
|
|
2800
3376
|
this.lineWidth -= indent;
|
|
2801
|
-
|
|
2802
3377
|
return this.once('line', () => {
|
|
2803
3378
|
this.document.x -= indent;
|
|
2804
3379
|
this.lineWidth += indent;
|
|
3380
|
+
|
|
2805
3381
|
if (options.continued && !this.continuedX) {
|
|
2806
3382
|
this.continuedX = this.indent;
|
|
2807
3383
|
}
|
|
3384
|
+
|
|
2808
3385
|
if (!options.continued) {
|
|
2809
3386
|
return this.continuedX = 0;
|
|
2810
3387
|
}
|
|
2811
3388
|
});
|
|
2812
|
-
});
|
|
3389
|
+
}); // handle left aligning last lines of paragraphs
|
|
2813
3390
|
|
|
2814
|
-
// handle left aligning last lines of paragraphs
|
|
2815
3391
|
this.on('lastLine', options => {
|
|
2816
|
-
const {
|
|
3392
|
+
const {
|
|
3393
|
+
align
|
|
3394
|
+
} = options;
|
|
3395
|
+
|
|
2817
3396
|
if (align === 'justify') {
|
|
2818
3397
|
options.align = 'left';
|
|
2819
3398
|
}
|
|
2820
|
-
this.lastLine = true;
|
|
2821
3399
|
|
|
3400
|
+
this.lastLine = true;
|
|
2822
3401
|
return this.once('line', () => {
|
|
2823
3402
|
this.document.y += options.paragraphGap || 0;
|
|
2824
3403
|
options.align = align;
|
|
@@ -2841,10 +3420,9 @@ class LineWrapper extends events.EventEmitter {
|
|
|
2841
3420
|
while (bk = breaker.nextBreak()) {
|
|
2842
3421
|
var shouldContinue;
|
|
2843
3422
|
let word = text.slice((last != null ? last.position : undefined) || 0, bk.position);
|
|
2844
|
-
let w = wordWidths[word] != null ? wordWidths[word] : wordWidths[word] = this.wordWidth(word);
|
|
2845
|
-
|
|
2846
|
-
// if the word is longer than the whole line, chop it up
|
|
3423
|
+
let w = wordWidths[word] != null ? wordWidths[word] : wordWidths[word] = this.wordWidth(word); // if the word is longer than the whole line, chop it up
|
|
2847
3424
|
// TODO: break by grapheme clusters, not JS string characters
|
|
3425
|
+
|
|
2848
3426
|
if (w > this.lineWidth + this.continuedX) {
|
|
2849
3427
|
// make some fake break objects
|
|
2850
3428
|
let lbk = last;
|
|
@@ -2853,6 +3431,7 @@ class LineWrapper extends events.EventEmitter {
|
|
|
2853
3431
|
while (word.length) {
|
|
2854
3432
|
// fit as much of the word as possible into the space we have
|
|
2855
3433
|
var l, mightGrow;
|
|
3434
|
+
|
|
2856
3435
|
if (w > this.spaceLeft) {
|
|
2857
3436
|
// start our check at the end of our available space - this method is faster than a loop of each character and it resolves
|
|
2858
3437
|
// an issue with long loops when processing massive words, such as a huge number of spaces
|
|
@@ -2862,8 +3441,9 @@ class LineWrapper extends events.EventEmitter {
|
|
|
2862
3441
|
} else {
|
|
2863
3442
|
l = word.length;
|
|
2864
3443
|
}
|
|
2865
|
-
|
|
2866
|
-
// shrink or grow word as necessary after our near-guess above
|
|
3444
|
+
|
|
3445
|
+
let mustShrink = w > this.spaceLeft && l > 0; // shrink or grow word as necessary after our near-guess above
|
|
3446
|
+
|
|
2867
3447
|
while (mustShrink || mightGrow) {
|
|
2868
3448
|
if (mustShrink) {
|
|
2869
3449
|
w = this.wordWidth(word.slice(0, --l));
|
|
@@ -2873,14 +3453,15 @@ class LineWrapper extends events.EventEmitter {
|
|
|
2873
3453
|
mustShrink = w > this.spaceLeft && l > 0;
|
|
2874
3454
|
mightGrow = w <= this.spaceLeft && l < word.length;
|
|
2875
3455
|
}
|
|
2876
|
-
}
|
|
3456
|
+
} // send a required break unless this is the last piece and a linebreak is not specified
|
|
3457
|
+
|
|
2877
3458
|
|
|
2878
|
-
// send a required break unless this is the last piece and a linebreak is not specified
|
|
2879
3459
|
fbk.required = bk.required || l < word.length;
|
|
2880
3460
|
shouldContinue = fn(word.slice(0, l), w, fbk, lbk);
|
|
2881
|
-
lbk = {
|
|
3461
|
+
lbk = {
|
|
3462
|
+
required: false
|
|
3463
|
+
}; // get the remaining piece of the word
|
|
2882
3464
|
|
|
2883
|
-
// get the remaining piece of the word
|
|
2884
3465
|
word = word.slice(l);
|
|
2885
3466
|
w = this.wordWidth(word);
|
|
2886
3467
|
|
|
@@ -2896,6 +3477,7 @@ class LineWrapper extends events.EventEmitter {
|
|
|
2896
3477
|
if (shouldContinue === false) {
|
|
2897
3478
|
break;
|
|
2898
3479
|
}
|
|
3480
|
+
|
|
2899
3481
|
last = bk;
|
|
2900
3482
|
}
|
|
2901
3483
|
}
|
|
@@ -2905,20 +3487,24 @@ class LineWrapper extends events.EventEmitter {
|
|
|
2905
3487
|
if (options.indent != null) {
|
|
2906
3488
|
this.indent = options.indent;
|
|
2907
3489
|
}
|
|
3490
|
+
|
|
2908
3491
|
if (options.characterSpacing != null) {
|
|
2909
3492
|
this.characterSpacing = options.characterSpacing;
|
|
2910
3493
|
}
|
|
3494
|
+
|
|
2911
3495
|
if (options.wordSpacing != null) {
|
|
2912
3496
|
this.wordSpacing = options.wordSpacing;
|
|
2913
3497
|
}
|
|
3498
|
+
|
|
2914
3499
|
if (options.ellipsis != null) {
|
|
2915
3500
|
this.ellipsis = options.ellipsis;
|
|
2916
|
-
}
|
|
2917
|
-
|
|
2918
|
-
// make sure we're actually on the page
|
|
3501
|
+
} // make sure we're actually on the page
|
|
2919
3502
|
// and that the first line of is never by
|
|
2920
3503
|
// itself at the bottom of a page (orphans)
|
|
3504
|
+
|
|
3505
|
+
|
|
2921
3506
|
const nextY = this.document.y + this.document.currentLineHeight(true);
|
|
3507
|
+
|
|
2922
3508
|
if (this.document.y > this.maxY || nextY > this.maxY) {
|
|
2923
3509
|
this.nextSection();
|
|
2924
3510
|
}
|
|
@@ -2927,19 +3513,22 @@ class LineWrapper extends events.EventEmitter {
|
|
|
2927
3513
|
let textWidth = 0;
|
|
2928
3514
|
let wc = 0;
|
|
2929
3515
|
let lc = 0;
|
|
3516
|
+
let {
|
|
3517
|
+
y
|
|
3518
|
+
} = this.document; // used to reset Y pos if options.continued (below)
|
|
2930
3519
|
|
|
2931
|
-
let { y } = this.document; // used to reset Y pos if options.continued (below)
|
|
2932
3520
|
const emitLine = () => {
|
|
2933
3521
|
options.textWidth = textWidth + this.wordSpacing * (wc - 1);
|
|
2934
3522
|
options.wordCount = wc;
|
|
2935
3523
|
options.lineWidth = this.lineWidth;
|
|
2936
|
-
({
|
|
3524
|
+
({
|
|
3525
|
+
y
|
|
3526
|
+
} = this.document);
|
|
2937
3527
|
this.emit('line', buffer, options, this);
|
|
2938
3528
|
return lc++;
|
|
2939
3529
|
};
|
|
2940
3530
|
|
|
2941
3531
|
this.emit('sectionStart', options, this);
|
|
2942
|
-
|
|
2943
3532
|
this.eachWord(text, (word, w, bk, last) => {
|
|
2944
3533
|
if (last == null || last.required) {
|
|
2945
3534
|
this.emit('firstLine', options, this);
|
|
@@ -2956,20 +3545,23 @@ class LineWrapper extends events.EventEmitter {
|
|
|
2956
3545
|
// if the user specified a max height and an ellipsis, and is about to pass the
|
|
2957
3546
|
// max height and max columns after the next line, append the ellipsis
|
|
2958
3547
|
const lh = this.document.currentLineHeight(true);
|
|
3548
|
+
|
|
2959
3549
|
if (this.height != null && this.ellipsis && this.document.y + lh * 2 > this.maxY && this.column >= this.columns) {
|
|
2960
3550
|
if (this.ellipsis === true) {
|
|
2961
3551
|
this.ellipsis = '…';
|
|
2962
3552
|
} // map default ellipsis character
|
|
2963
|
-
buffer = buffer.replace(/\s+$/, '');
|
|
2964
|
-
textWidth = this.wordWidth(buffer + this.ellipsis);
|
|
2965
3553
|
|
|
2966
|
-
|
|
3554
|
+
|
|
3555
|
+
buffer = buffer.replace(/\s+$/, '');
|
|
3556
|
+
textWidth = this.wordWidth(buffer + this.ellipsis); // remove characters from the buffer until the ellipsis fits
|
|
2967
3557
|
// to avoid inifinite loop need to stop while-loop if buffer is empty string
|
|
3558
|
+
|
|
2968
3559
|
while (buffer && textWidth > this.lineWidth) {
|
|
2969
3560
|
buffer = buffer.slice(0, -1).replace(/\s+$/, '');
|
|
2970
3561
|
textWidth = this.wordWidth(buffer + this.ellipsis);
|
|
2971
|
-
}
|
|
2972
|
-
|
|
3562
|
+
} // need to add ellipsis only if there is enough space for it
|
|
3563
|
+
|
|
3564
|
+
|
|
2973
3565
|
if (textWidth <= this.lineWidth) {
|
|
2974
3566
|
buffer = buffer + this.ellipsis;
|
|
2975
3567
|
}
|
|
@@ -2988,22 +3580,20 @@ class LineWrapper extends events.EventEmitter {
|
|
|
2988
3580
|
this.emit('lastLine', options, this);
|
|
2989
3581
|
}
|
|
2990
3582
|
|
|
2991
|
-
emitLine();
|
|
2992
|
-
|
|
2993
|
-
// if we've reached the edge of the page,
|
|
3583
|
+
emitLine(); // if we've reached the edge of the page,
|
|
2994
3584
|
// continue on a new page or column
|
|
3585
|
+
|
|
2995
3586
|
if (this.document.y + lh > this.maxY) {
|
|
2996
|
-
const shouldContinue = this.nextSection();
|
|
3587
|
+
const shouldContinue = this.nextSection(); // stop if we reached the maximum height
|
|
2997
3588
|
|
|
2998
|
-
// stop if we reached the maximum height
|
|
2999
3589
|
if (!shouldContinue) {
|
|
3000
3590
|
wc = 0;
|
|
3001
3591
|
buffer = '';
|
|
3002
3592
|
return false;
|
|
3003
3593
|
}
|
|
3004
|
-
}
|
|
3594
|
+
} // reset the space left and buffer
|
|
3595
|
+
|
|
3005
3596
|
|
|
3006
|
-
// reset the space left and buffer
|
|
3007
3597
|
if (bk.required) {
|
|
3008
3598
|
this.spaceLeft = this.lineWidth;
|
|
3009
3599
|
buffer = '';
|
|
@@ -3026,15 +3616,15 @@ class LineWrapper extends events.EventEmitter {
|
|
|
3026
3616
|
emitLine();
|
|
3027
3617
|
}
|
|
3028
3618
|
|
|
3029
|
-
this.emit('sectionEnd', options, this);
|
|
3030
|
-
|
|
3031
|
-
// if the wrap is set to be continued, save the X position
|
|
3619
|
+
this.emit('sectionEnd', options, this); // if the wrap is set to be continued, save the X position
|
|
3032
3620
|
// to start the first line of the next segment at, and reset
|
|
3033
3621
|
// the y position
|
|
3622
|
+
|
|
3034
3623
|
if (options.continued === true) {
|
|
3035
3624
|
if (lc > 1) {
|
|
3036
3625
|
this.continuedX = 0;
|
|
3037
3626
|
}
|
|
3627
|
+
|
|
3038
3628
|
this.continuedX += options.textWidth || 0;
|
|
3039
3629
|
return this.document.y = y;
|
|
3040
3630
|
} else {
|
|
@@ -3057,9 +3647,11 @@ class LineWrapper extends events.EventEmitter {
|
|
|
3057
3647
|
this.startY = this.document.page.margins.top;
|
|
3058
3648
|
this.maxY = this.document.page.maxY();
|
|
3059
3649
|
this.document.x = this.startX;
|
|
3650
|
+
|
|
3060
3651
|
if (this.document._fillColor) {
|
|
3061
3652
|
this.document.fillColor(...(this.document._fillColor || []));
|
|
3062
3653
|
}
|
|
3654
|
+
|
|
3063
3655
|
this.emit('pageBreak', options, this);
|
|
3064
3656
|
} else {
|
|
3065
3657
|
this.document.x += this.lineWidth + this.columnGap;
|
|
@@ -3070,14 +3662,16 @@ class LineWrapper extends events.EventEmitter {
|
|
|
3070
3662
|
this.emit('sectionStart', options, this);
|
|
3071
3663
|
return true;
|
|
3072
3664
|
}
|
|
3073
|
-
}
|
|
3074
3665
|
|
|
3075
|
-
|
|
3666
|
+
}
|
|
3076
3667
|
|
|
3668
|
+
const {
|
|
3669
|
+
number: number$2
|
|
3670
|
+
} = PDFObject;
|
|
3077
3671
|
var TextMixin = {
|
|
3078
3672
|
initText() {
|
|
3079
|
-
this._line = this._line.bind(this);
|
|
3080
|
-
|
|
3673
|
+
this._line = this._line.bind(this); // Current coordinates
|
|
3674
|
+
|
|
3081
3675
|
this.x = 0;
|
|
3082
3676
|
this.y = 0;
|
|
3083
3677
|
return this._lineGap = 0;
|
|
@@ -3092,6 +3686,7 @@ var TextMixin = {
|
|
|
3092
3686
|
if (lines == null) {
|
|
3093
3687
|
lines = 1;
|
|
3094
3688
|
}
|
|
3689
|
+
|
|
3095
3690
|
this.y += this.currentLineHeight(true) * lines + this._lineGap;
|
|
3096
3691
|
return this;
|
|
3097
3692
|
},
|
|
@@ -3100,24 +3695,24 @@ var TextMixin = {
|
|
|
3100
3695
|
if (lines == null) {
|
|
3101
3696
|
lines = 1;
|
|
3102
3697
|
}
|
|
3698
|
+
|
|
3103
3699
|
this.y -= this.currentLineHeight(true) * lines + this._lineGap;
|
|
3104
3700
|
return this;
|
|
3105
3701
|
},
|
|
3106
3702
|
|
|
3107
3703
|
_text(text, x, y, options, lineCallback) {
|
|
3108
|
-
options = this._initOptions(x, y, options);
|
|
3704
|
+
options = this._initOptions(x, y, options); // Convert text to a string
|
|
3109
3705
|
|
|
3110
|
-
|
|
3111
|
-
text = text == null ? '' : `${text}`;
|
|
3706
|
+
text = text == null ? '' : `${text}`; // if the wordSpacing option is specified, remove multiple consecutive spaces
|
|
3112
3707
|
|
|
3113
|
-
// if the wordSpacing option is specified, remove multiple consecutive spaces
|
|
3114
3708
|
if (options.wordSpacing) {
|
|
3115
3709
|
text = text.replace(/\s{2,}/g, ' ');
|
|
3116
|
-
}
|
|
3710
|
+
} // word wrapping
|
|
3711
|
+
|
|
3117
3712
|
|
|
3118
|
-
// word wrapping
|
|
3119
3713
|
if (options.width) {
|
|
3120
3714
|
let wrapper = this._wrapper;
|
|
3715
|
+
|
|
3121
3716
|
if (!wrapper) {
|
|
3122
3717
|
wrapper = new LineWrapper(this, options);
|
|
3123
3718
|
wrapper.on('line', lineCallback);
|
|
@@ -3125,9 +3720,7 @@ var TextMixin = {
|
|
|
3125
3720
|
|
|
3126
3721
|
this._wrapper = options.continued ? wrapper : null;
|
|
3127
3722
|
this._textOptions = options.continued ? options : null;
|
|
3128
|
-
wrapper.wrap(text, options);
|
|
3129
|
-
|
|
3130
|
-
// render paragraphs as single lines
|
|
3723
|
+
wrapper.wrap(text, options); // render paragraphs as single lines
|
|
3131
3724
|
} else {
|
|
3132
3725
|
for (let line of text.split('\n')) {
|
|
3133
3726
|
lineCallback(line, options);
|
|
@@ -3141,20 +3734,20 @@ var TextMixin = {
|
|
|
3141
3734
|
return this._text(text, x, y, options, this._line);
|
|
3142
3735
|
},
|
|
3143
3736
|
|
|
3144
|
-
widthOfString(string, options) {
|
|
3145
|
-
if (options == null) {
|
|
3146
|
-
options = {};
|
|
3147
|
-
}
|
|
3737
|
+
widthOfString(string, options = {}) {
|
|
3148
3738
|
return this._font.widthOfString(string, this._fontSize, options.features) + (options.characterSpacing || 0) * (string.length - 1);
|
|
3149
3739
|
},
|
|
3150
3740
|
|
|
3151
3741
|
heightOfString(text, options) {
|
|
3152
|
-
const {
|
|
3153
|
-
|
|
3742
|
+
const {
|
|
3743
|
+
x,
|
|
3744
|
+
y
|
|
3745
|
+
} = this;
|
|
3154
3746
|
options = this._initOptions(options);
|
|
3155
3747
|
options.height = Infinity; // don't break pages
|
|
3156
3748
|
|
|
3157
3749
|
const lineGap = options.lineGap || this._lineGap || 0;
|
|
3750
|
+
|
|
3158
3751
|
this._text(text, this.x, this.y, options, () => {
|
|
3159
3752
|
return this.y += this.currentLineHeight(true) + lineGap;
|
|
3160
3753
|
});
|
|
@@ -3162,20 +3755,17 @@ var TextMixin = {
|
|
|
3162
3755
|
const height = this.y - y;
|
|
3163
3756
|
this.x = x;
|
|
3164
3757
|
this.y = y;
|
|
3165
|
-
|
|
3166
3758
|
return height;
|
|
3167
3759
|
},
|
|
3168
3760
|
|
|
3169
3761
|
list(list, x, y, options, wrapper) {
|
|
3170
3762
|
options = this._initOptions(x, y, options);
|
|
3171
|
-
|
|
3172
3763
|
const listType = options.listType || 'bullet';
|
|
3173
3764
|
const unit = Math.round(this._font.ascender / 1000 * this._fontSize);
|
|
3174
3765
|
const midLine = unit / 2;
|
|
3175
3766
|
const r = options.bulletRadius || unit / 3;
|
|
3176
3767
|
const indent = options.textIndent || (listType === 'bullet' ? r * 5 : unit * 2);
|
|
3177
3768
|
const itemIndent = options.bulletIndent || (listType === 'bullet' ? r * 8 : unit * 2);
|
|
3178
|
-
|
|
3179
3769
|
let level = 1;
|
|
3180
3770
|
const items = [];
|
|
3181
3771
|
const levels = [];
|
|
@@ -3183,8 +3773,10 @@ var TextMixin = {
|
|
|
3183
3773
|
|
|
3184
3774
|
var flatten = function (list) {
|
|
3185
3775
|
let n = 1;
|
|
3776
|
+
|
|
3186
3777
|
for (let i = 0; i < list.length; i++) {
|
|
3187
3778
|
const item = list[i];
|
|
3779
|
+
|
|
3188
3780
|
if (Array.isArray(item)) {
|
|
3189
3781
|
level++;
|
|
3190
3782
|
flatten(item);
|
|
@@ -3192,6 +3784,7 @@ var TextMixin = {
|
|
|
3192
3784
|
} else {
|
|
3193
3785
|
items.push(item);
|
|
3194
3786
|
levels.push(level);
|
|
3787
|
+
|
|
3195
3788
|
if (listType !== 'bullet') {
|
|
3196
3789
|
numbers.push(n++);
|
|
3197
3790
|
}
|
|
@@ -3205,6 +3798,7 @@ var TextMixin = {
|
|
|
3205
3798
|
switch (listType) {
|
|
3206
3799
|
case 'numbered':
|
|
3207
3800
|
return `${n}.`;
|
|
3801
|
+
|
|
3208
3802
|
case 'lettered':
|
|
3209
3803
|
var letter = String.fromCharCode((n - 1) % 26 + 65);
|
|
3210
3804
|
var times = Math.floor((n - 1) / 26 + 1);
|
|
@@ -3215,11 +3809,11 @@ var TextMixin = {
|
|
|
3215
3809
|
|
|
3216
3810
|
wrapper = new LineWrapper(this, options);
|
|
3217
3811
|
wrapper.on('line', this._line);
|
|
3218
|
-
|
|
3219
3812
|
level = 1;
|
|
3220
3813
|
let i = 0;
|
|
3221
3814
|
wrapper.on('firstLine', () => {
|
|
3222
3815
|
let l;
|
|
3816
|
+
|
|
3223
3817
|
if ((l = levels[i++]) !== level) {
|
|
3224
3818
|
const diff = itemIndent * (l - level);
|
|
3225
3819
|
this.x += diff;
|
|
@@ -3231,66 +3825,58 @@ var TextMixin = {
|
|
|
3231
3825
|
case 'bullet':
|
|
3232
3826
|
this.circle(this.x - indent + r, this.y + midLine, r);
|
|
3233
3827
|
return this.fill();
|
|
3828
|
+
|
|
3234
3829
|
case 'numbered':
|
|
3235
3830
|
case 'lettered':
|
|
3236
3831
|
var text = label(numbers[i - 1]);
|
|
3237
3832
|
return this._fragment(text, this.x - indent, this.y, options);
|
|
3238
3833
|
}
|
|
3239
3834
|
});
|
|
3240
|
-
|
|
3241
3835
|
wrapper.on('sectionStart', () => {
|
|
3242
3836
|
const pos = indent + itemIndent * (level - 1);
|
|
3243
3837
|
this.x += pos;
|
|
3244
3838
|
return wrapper.lineWidth -= pos;
|
|
3245
3839
|
});
|
|
3246
|
-
|
|
3247
3840
|
wrapper.on('sectionEnd', () => {
|
|
3248
3841
|
const pos = indent + itemIndent * (level - 1);
|
|
3249
3842
|
this.x -= pos;
|
|
3250
3843
|
return wrapper.lineWidth += pos;
|
|
3251
3844
|
});
|
|
3252
|
-
|
|
3253
3845
|
wrapper.wrap(items.join('\n'), options);
|
|
3254
|
-
|
|
3255
3846
|
return this;
|
|
3256
3847
|
},
|
|
3257
3848
|
|
|
3258
|
-
_initOptions(x, y, options) {
|
|
3259
|
-
if (x == null) {
|
|
3260
|
-
x = {};
|
|
3261
|
-
}
|
|
3262
|
-
if (options == null) {
|
|
3263
|
-
options = {};
|
|
3264
|
-
}
|
|
3849
|
+
_initOptions(x = {}, y, options = {}) {
|
|
3265
3850
|
if (typeof x === 'object') {
|
|
3266
3851
|
options = x;
|
|
3267
3852
|
x = null;
|
|
3268
|
-
}
|
|
3853
|
+
} // clone options object
|
|
3269
3854
|
|
|
3270
|
-
// clone options object
|
|
3271
|
-
const result = Object.assign({}, options);
|
|
3272
3855
|
|
|
3273
|
-
// extend options with previous values for continued text
|
|
3856
|
+
const result = Object.assign({}, options); // extend options with previous values for continued text
|
|
3857
|
+
|
|
3274
3858
|
if (this._textOptions) {
|
|
3275
3859
|
for (let key in this._textOptions) {
|
|
3276
3860
|
const val = this._textOptions[key];
|
|
3861
|
+
|
|
3277
3862
|
if (key !== 'continued') {
|
|
3278
3863
|
if (result[key] == null) {
|
|
3279
3864
|
result[key] = val;
|
|
3280
3865
|
}
|
|
3281
3866
|
}
|
|
3282
3867
|
}
|
|
3283
|
-
}
|
|
3868
|
+
} // Update the current position
|
|
3869
|
+
|
|
3284
3870
|
|
|
3285
|
-
// Update the current position
|
|
3286
3871
|
if (x != null) {
|
|
3287
3872
|
this.x = x;
|
|
3288
3873
|
}
|
|
3874
|
+
|
|
3289
3875
|
if (y != null) {
|
|
3290
3876
|
this.y = y;
|
|
3291
|
-
}
|
|
3877
|
+
} // wrap to margins if no x or y position passed
|
|
3878
|
+
|
|
3292
3879
|
|
|
3293
|
-
// wrap to margins if no x or y position passed
|
|
3294
3880
|
if (result.lineBreak !== false) {
|
|
3295
3881
|
if (result.width == null) {
|
|
3296
3882
|
result.width = this.page.width - this.x - this.page.margins.right;
|
|
@@ -3300,18 +3886,18 @@ var TextMixin = {
|
|
|
3300
3886
|
if (!result.columns) {
|
|
3301
3887
|
result.columns = 0;
|
|
3302
3888
|
}
|
|
3889
|
+
|
|
3303
3890
|
if (result.columnGap == null) {
|
|
3304
3891
|
result.columnGap = 18;
|
|
3305
3892
|
} // 1/4 inch
|
|
3306
3893
|
|
|
3894
|
+
|
|
3307
3895
|
return result;
|
|
3308
3896
|
},
|
|
3309
3897
|
|
|
3310
|
-
_line(text, options, wrapper) {
|
|
3311
|
-
if (options == null) {
|
|
3312
|
-
options = {};
|
|
3313
|
-
}
|
|
3898
|
+
_line(text, options = {}, wrapper) {
|
|
3314
3899
|
this._fragment(text, this.x, this.y, options);
|
|
3900
|
+
|
|
3315
3901
|
const lineGap = options.lineGap || this._lineGap || 0;
|
|
3316
3902
|
|
|
3317
3903
|
if (!wrapper) {
|
|
@@ -3324,16 +3910,16 @@ var TextMixin = {
|
|
|
3324
3910
|
_fragment(text, x, y, options) {
|
|
3325
3911
|
let dy, encoded, i, positions, textWidth, words;
|
|
3326
3912
|
text = `${text}`.replace(/\n/g, '');
|
|
3913
|
+
|
|
3327
3914
|
if (text.length === 0) {
|
|
3328
3915
|
return;
|
|
3329
|
-
}
|
|
3916
|
+
} // handle options
|
|
3917
|
+
|
|
3330
3918
|
|
|
3331
|
-
// handle options
|
|
3332
3919
|
const align = options.align || 'left';
|
|
3333
3920
|
let wordSpacing = options.wordSpacing || 0;
|
|
3334
|
-
const characterSpacing = options.characterSpacing || 0;
|
|
3921
|
+
const characterSpacing = options.characterSpacing || 0; // text alignments
|
|
3335
3922
|
|
|
3336
|
-
// text alignments
|
|
3337
3923
|
if (options.width) {
|
|
3338
3924
|
switch (align) {
|
|
3339
3925
|
case 'right':
|
|
@@ -3353,9 +3939,9 @@ var TextMixin = {
|
|
|
3353
3939
|
wordSpacing = Math.max(0, (options.lineWidth - textWidth) / Math.max(1, words.length - 1) - spaceWidth);
|
|
3354
3940
|
break;
|
|
3355
3941
|
}
|
|
3356
|
-
}
|
|
3942
|
+
} // text baseline alignments based on http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
|
|
3943
|
+
|
|
3357
3944
|
|
|
3358
|
-
// text baseline alignments based on http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
|
|
3359
3945
|
if (typeof options.baseline === 'number') {
|
|
3360
3946
|
dy = -options.baseline;
|
|
3361
3947
|
} else {
|
|
@@ -3363,52 +3949,68 @@ var TextMixin = {
|
|
|
3363
3949
|
case 'svg-middle':
|
|
3364
3950
|
dy = 0.5 * this._font.xHeight;
|
|
3365
3951
|
break;
|
|
3952
|
+
|
|
3366
3953
|
case 'middle':
|
|
3367
3954
|
case 'svg-central':
|
|
3368
3955
|
dy = 0.5 * (this._font.descender + this._font.ascender);
|
|
3369
3956
|
break;
|
|
3957
|
+
|
|
3370
3958
|
case 'bottom':
|
|
3371
3959
|
case 'ideographic':
|
|
3372
3960
|
dy = this._font.descender;
|
|
3373
3961
|
break;
|
|
3962
|
+
|
|
3374
3963
|
case 'alphabetic':
|
|
3375
3964
|
dy = 0;
|
|
3376
3965
|
break;
|
|
3966
|
+
|
|
3377
3967
|
case 'mathematical':
|
|
3378
3968
|
dy = 0.5 * this._font.ascender;
|
|
3379
3969
|
break;
|
|
3970
|
+
|
|
3380
3971
|
case 'hanging':
|
|
3381
3972
|
dy = 0.8 * this._font.ascender;
|
|
3382
3973
|
break;
|
|
3974
|
+
|
|
3383
3975
|
case 'top':
|
|
3384
3976
|
dy = this._font.ascender;
|
|
3385
3977
|
break;
|
|
3978
|
+
|
|
3386
3979
|
default:
|
|
3387
3980
|
dy = this._font.ascender;
|
|
3388
3981
|
}
|
|
3982
|
+
|
|
3389
3983
|
dy = dy / 1000 * this._fontSize;
|
|
3390
|
-
}
|
|
3984
|
+
} // calculate the actual rendered width of the string after word and character spacing
|
|
3391
3985
|
|
|
3392
|
-
// calculate the actual rendered width of the string after word and character spacing
|
|
3393
|
-
const renderedWidth = options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1);
|
|
3394
3986
|
|
|
3395
|
-
// create link annotations if the link option is given
|
|
3987
|
+
const renderedWidth = options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1); // create link annotations if the link option is given
|
|
3988
|
+
|
|
3396
3989
|
if (options.link != null) {
|
|
3397
3990
|
this.link(x, y, renderedWidth, this.currentLineHeight(), options.link);
|
|
3398
3991
|
}
|
|
3399
3992
|
|
|
3400
|
-
|
|
3993
|
+
if (options.goTo != null) {
|
|
3994
|
+
this.goTo(x, y, renderedWidth, this.currentLineHeight(), options.goTo);
|
|
3995
|
+
}
|
|
3996
|
+
|
|
3997
|
+
if (options.destination != null) {
|
|
3998
|
+
this.addNamedDestination(options.destination, 'XYZ', x, y, null);
|
|
3999
|
+
} // create underline or strikethrough line
|
|
4000
|
+
|
|
4001
|
+
|
|
3401
4002
|
if (options.underline || options.strike) {
|
|
3402
4003
|
this.save();
|
|
4004
|
+
|
|
3403
4005
|
if (!options.stroke) {
|
|
3404
4006
|
this.strokeColor(...(this._fillColor || []));
|
|
3405
4007
|
}
|
|
3406
4008
|
|
|
3407
4009
|
const lineWidth = this._fontSize < 10 ? 0.5 : Math.floor(this._fontSize / 10);
|
|
3408
4010
|
this.lineWidth(lineWidth);
|
|
3409
|
-
|
|
3410
4011
|
const d = options.underline ? 1 : 2;
|
|
3411
4012
|
let lineY = y + this.currentLineHeight() / d;
|
|
4013
|
+
|
|
3412
4014
|
if (options.underline) {
|
|
3413
4015
|
lineY -= lineWidth;
|
|
3414
4016
|
}
|
|
@@ -3419,74 +4021,74 @@ var TextMixin = {
|
|
|
3419
4021
|
this.restore();
|
|
3420
4022
|
}
|
|
3421
4023
|
|
|
3422
|
-
this.save();
|
|
4024
|
+
this.save(); // oblique (angle in degrees or boolean)
|
|
3423
4025
|
|
|
3424
|
-
// oblique (angle in degrees or boolean)
|
|
3425
4026
|
if (options.oblique) {
|
|
3426
4027
|
let skew;
|
|
4028
|
+
|
|
3427
4029
|
if (typeof options.oblique === 'number') {
|
|
3428
4030
|
skew = -Math.tan(options.oblique * Math.PI / 180);
|
|
3429
4031
|
} else {
|
|
3430
4032
|
skew = -0.25;
|
|
3431
4033
|
}
|
|
4034
|
+
|
|
3432
4035
|
this.transform(1, 0, 0, 1, x, y);
|
|
3433
4036
|
this.transform(1, 0, skew, 1, -skew * dy, 0);
|
|
3434
4037
|
this.transform(1, 0, 0, 1, -x, -y);
|
|
3435
|
-
}
|
|
4038
|
+
} // flip coordinate system
|
|
4039
|
+
|
|
3436
4040
|
|
|
3437
|
-
// flip coordinate system
|
|
3438
4041
|
this.transform(1, 0, 0, -1, 0, this.page.height);
|
|
3439
|
-
y = this.page.height - y - dy;
|
|
4042
|
+
y = this.page.height - y - dy; // add current font to page if necessary
|
|
3440
4043
|
|
|
3441
|
-
// add current font to page if necessary
|
|
3442
4044
|
if (this.page.fonts[this._font.id] == null) {
|
|
3443
4045
|
this.page.fonts[this._font.id] = this._font.ref();
|
|
3444
|
-
}
|
|
4046
|
+
} // begin the text object
|
|
3445
4047
|
|
|
3446
|
-
// begin the text object
|
|
3447
|
-
this.addContent('BT');
|
|
3448
4048
|
|
|
3449
|
-
// text position
|
|
3450
|
-
this.addContent(`1 0 0 1 ${number$2(x)} ${number$2(y)} Tm`);
|
|
4049
|
+
this.addContent('BT'); // text position
|
|
3451
4050
|
|
|
3452
|
-
// font and font size
|
|
3453
|
-
|
|
4051
|
+
this.addContent(`1 0 0 1 ${number$2(x)} ${number$2(y)} Tm`); // font and font size
|
|
4052
|
+
|
|
4053
|
+
this.addContent(`/${this._font.id} ${number$2(this._fontSize)} Tf`); // rendering mode
|
|
3454
4054
|
|
|
3455
|
-
// rendering mode
|
|
3456
4055
|
const mode = options.fill && options.stroke ? 2 : options.stroke ? 1 : 0;
|
|
4056
|
+
|
|
3457
4057
|
if (mode) {
|
|
3458
4058
|
this.addContent(`${mode} Tr`);
|
|
3459
|
-
}
|
|
4059
|
+
} // Character spacing
|
|
4060
|
+
|
|
3460
4061
|
|
|
3461
|
-
// Character spacing
|
|
3462
4062
|
if (characterSpacing) {
|
|
3463
4063
|
this.addContent(`${number$2(characterSpacing)} Tc`);
|
|
3464
|
-
}
|
|
3465
|
-
|
|
3466
|
-
// Add the actual text
|
|
4064
|
+
} // Add the actual text
|
|
3467
4065
|
// If we have a word spacing value, we need to encode each word separately
|
|
3468
4066
|
// since the normal Tw operator only works on character code 32, which isn't
|
|
3469
4067
|
// used for embedded fonts.
|
|
4068
|
+
|
|
4069
|
+
|
|
3470
4070
|
if (wordSpacing) {
|
|
3471
4071
|
words = text.trim().split(/\s+/);
|
|
3472
4072
|
wordSpacing += this.widthOfString(' ') + characterSpacing;
|
|
3473
4073
|
wordSpacing *= 1000 / this._fontSize;
|
|
3474
|
-
|
|
3475
4074
|
encoded = [];
|
|
3476
4075
|
positions = [];
|
|
4076
|
+
|
|
3477
4077
|
for (let word of words) {
|
|
3478
4078
|
const [encodedWord, positionsWord] = this._font.encode(word, options.features);
|
|
3479
|
-
encoded = encoded.concat(encodedWord);
|
|
3480
|
-
positions = positions.concat(positionsWord);
|
|
3481
4079
|
|
|
3482
|
-
|
|
4080
|
+
encoded = encoded.concat(encodedWord);
|
|
4081
|
+
positions = positions.concat(positionsWord); // add the word spacing to the end of the word
|
|
3483
4082
|
// clone object because of cache
|
|
4083
|
+
|
|
3484
4084
|
const space = {};
|
|
3485
4085
|
const object = positions[positions.length - 1];
|
|
4086
|
+
|
|
3486
4087
|
for (let key in object) {
|
|
3487
4088
|
const val = object[key];
|
|
3488
4089
|
space[key] = val;
|
|
3489
4090
|
}
|
|
4091
|
+
|
|
3490
4092
|
space.xAdvance += wordSpacing;
|
|
3491
4093
|
positions[positions.length - 1] = space;
|
|
3492
4094
|
}
|
|
@@ -3497,9 +4099,8 @@ var TextMixin = {
|
|
|
3497
4099
|
const scale = this._fontSize / 1000;
|
|
3498
4100
|
const commands = [];
|
|
3499
4101
|
let last = 0;
|
|
3500
|
-
let hadOffset = false;
|
|
4102
|
+
let hadOffset = false; // Adds a segment of text to the TJ command buffer
|
|
3501
4103
|
|
|
3502
|
-
// Adds a segment of text to the TJ command buffer
|
|
3503
4104
|
const addSegment = cur => {
|
|
3504
4105
|
if (last < cur) {
|
|
3505
4106
|
const hex = encoded.slice(last, cur).join('');
|
|
@@ -3508,9 +4109,9 @@ var TextMixin = {
|
|
|
3508
4109
|
}
|
|
3509
4110
|
|
|
3510
4111
|
return last = cur;
|
|
3511
|
-
};
|
|
4112
|
+
}; // Flushes the current TJ commands to the output stream
|
|
4113
|
+
|
|
3512
4114
|
|
|
3513
|
-
// Flushes the current TJ commands to the output stream
|
|
3514
4115
|
const flush = i => {
|
|
3515
4116
|
addSegment(i);
|
|
3516
4117
|
|
|
@@ -3524,44 +4125,41 @@ var TextMixin = {
|
|
|
3524
4125
|
// If we have an x or y offset, we have to break out of the current TJ command
|
|
3525
4126
|
// so we can move the text position.
|
|
3526
4127
|
const pos = positions[i];
|
|
4128
|
+
|
|
3527
4129
|
if (pos.xOffset || pos.yOffset) {
|
|
3528
4130
|
// Flush the current buffer
|
|
3529
|
-
flush(i);
|
|
4131
|
+
flush(i); // Move the text position and flush just the current character
|
|
3530
4132
|
|
|
3531
|
-
// Move the text position and flush just the current character
|
|
3532
4133
|
this.addContent(`1 0 0 1 ${number$2(x + pos.xOffset * scale)} ${number$2(y + pos.yOffset * scale)} Tm`);
|
|
3533
4134
|
flush(i + 1);
|
|
3534
|
-
|
|
3535
4135
|
hadOffset = true;
|
|
3536
4136
|
} else {
|
|
3537
4137
|
// If the last character had an offset, reset the text position
|
|
3538
4138
|
if (hadOffset) {
|
|
3539
4139
|
this.addContent(`1 0 0 1 ${number$2(x)} ${number$2(y)} Tm`);
|
|
3540
4140
|
hadOffset = false;
|
|
3541
|
-
}
|
|
4141
|
+
} // Group segments that don't have any advance adjustments
|
|
4142
|
+
|
|
3542
4143
|
|
|
3543
|
-
// Group segments that don't have any advance adjustments
|
|
3544
4144
|
if (pos.xAdvance - pos.advanceWidth !== 0) {
|
|
3545
4145
|
addSegment(i + 1);
|
|
3546
4146
|
}
|
|
3547
4147
|
}
|
|
3548
4148
|
|
|
3549
4149
|
x += pos.xAdvance * scale;
|
|
3550
|
-
}
|
|
4150
|
+
} // Flush any remaining commands
|
|
3551
4151
|
|
|
3552
|
-
// Flush any remaining commands
|
|
3553
|
-
flush(i);
|
|
3554
4152
|
|
|
3555
|
-
// end the text object
|
|
3556
|
-
|
|
4153
|
+
flush(i); // end the text object
|
|
4154
|
+
|
|
4155
|
+
this.addContent('ET'); // restore flipped coordinate system
|
|
3557
4156
|
|
|
3558
|
-
// restore flipped coordinate system
|
|
3559
4157
|
return this.restore();
|
|
3560
4158
|
}
|
|
4159
|
+
|
|
3561
4160
|
};
|
|
3562
4161
|
|
|
3563
4162
|
const MARKERS = [0xffc0, 0xffc1, 0xffc2, 0xffc3, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9, 0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf];
|
|
3564
|
-
|
|
3565
4163
|
const COLOR_SPACE_MAP = {
|
|
3566
4164
|
1: 'DeviceGray',
|
|
3567
4165
|
3: 'DeviceRGB',
|
|
@@ -3573,35 +4171,36 @@ class JPEG {
|
|
|
3573
4171
|
let marker;
|
|
3574
4172
|
this.data = data;
|
|
3575
4173
|
this.label = label;
|
|
4174
|
+
|
|
3576
4175
|
if (this.data.readUInt16BE(0) !== 0xffd8) {
|
|
3577
4176
|
throw 'SOI not found in JPEG';
|
|
3578
4177
|
}
|
|
3579
4178
|
|
|
3580
4179
|
let pos = 2;
|
|
4180
|
+
|
|
3581
4181
|
while (pos < this.data.length) {
|
|
3582
4182
|
marker = this.data.readUInt16BE(pos);
|
|
3583
4183
|
pos += 2;
|
|
4184
|
+
|
|
3584
4185
|
if (MARKERS.includes(marker)) {
|
|
3585
4186
|
break;
|
|
3586
4187
|
}
|
|
4188
|
+
|
|
3587
4189
|
pos += this.data.readUInt16BE(pos);
|
|
3588
4190
|
}
|
|
3589
4191
|
|
|
3590
4192
|
if (!MARKERS.includes(marker)) {
|
|
3591
4193
|
throw 'Invalid JPEG.';
|
|
3592
4194
|
}
|
|
3593
|
-
pos += 2;
|
|
3594
4195
|
|
|
4196
|
+
pos += 2;
|
|
3595
4197
|
this.bits = this.data[pos++];
|
|
3596
4198
|
this.height = this.data.readUInt16BE(pos);
|
|
3597
4199
|
pos += 2;
|
|
3598
|
-
|
|
3599
4200
|
this.width = this.data.readUInt16BE(pos);
|
|
3600
4201
|
pos += 2;
|
|
3601
|
-
|
|
3602
4202
|
const channels = this.data[pos++];
|
|
3603
4203
|
this.colorSpace = COLOR_SPACE_MAP[channels];
|
|
3604
|
-
|
|
3605
4204
|
this.obj = null;
|
|
3606
4205
|
}
|
|
3607
4206
|
|
|
@@ -3618,20 +4217,19 @@ class JPEG {
|
|
|
3618
4217
|
Height: this.height,
|
|
3619
4218
|
ColorSpace: this.colorSpace,
|
|
3620
4219
|
Filter: 'DCTDecode'
|
|
3621
|
-
});
|
|
3622
|
-
|
|
3623
|
-
// add extra decode params for CMYK images. By swapping the
|
|
4220
|
+
}); // add extra decode params for CMYK images. By swapping the
|
|
3624
4221
|
// min and max values from the default, we invert the colors. See
|
|
3625
4222
|
// section 4.8.4 of the spec.
|
|
4223
|
+
|
|
3626
4224
|
if (this.colorSpace === 'DeviceCMYK') {
|
|
3627
4225
|
this.obj.data['Decode'] = [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0];
|
|
3628
4226
|
}
|
|
3629
4227
|
|
|
3630
|
-
this.obj.end(this.data);
|
|
4228
|
+
this.obj.end(this.data); // free memory
|
|
3631
4229
|
|
|
3632
|
-
// free memory
|
|
3633
4230
|
return this.data = null;
|
|
3634
4231
|
}
|
|
4232
|
+
|
|
3635
4233
|
}
|
|
3636
4234
|
|
|
3637
4235
|
class PNGImage {
|
|
@@ -3646,12 +4244,12 @@ class PNGImage {
|
|
|
3646
4244
|
|
|
3647
4245
|
embed(document) {
|
|
3648
4246
|
this.document = document;
|
|
4247
|
+
|
|
3649
4248
|
if (this.obj) {
|
|
3650
4249
|
return;
|
|
3651
4250
|
}
|
|
3652
4251
|
|
|
3653
4252
|
const hasAlphaChannel = this.image.hasAlphaChannel;
|
|
3654
|
-
|
|
3655
4253
|
this.obj = this.document.ref({
|
|
3656
4254
|
Type: 'XObject',
|
|
3657
4255
|
Subtype: 'Image',
|
|
@@ -3668,7 +4266,6 @@ class PNGImage {
|
|
|
3668
4266
|
BitsPerComponent: this.image.bits,
|
|
3669
4267
|
Columns: this.width
|
|
3670
4268
|
});
|
|
3671
|
-
|
|
3672
4269
|
this.obj.data['DecodeParms'] = params;
|
|
3673
4270
|
params.end();
|
|
3674
4271
|
}
|
|
@@ -3678,14 +4275,13 @@ class PNGImage {
|
|
|
3678
4275
|
} else {
|
|
3679
4276
|
// embed the color palette in the PDF as an object stream
|
|
3680
4277
|
const palette = this.document.ref();
|
|
3681
|
-
palette.end(new Buffer(this.image.palette));
|
|
4278
|
+
palette.end(new Buffer(this.image.palette)); // build the color space array for the image
|
|
3682
4279
|
|
|
3683
|
-
// build the color space array for the image
|
|
3684
4280
|
this.obj.data['ColorSpace'] = ['Indexed', 'DeviceRGB', this.image.palette.length / 3 - 1, palette];
|
|
3685
|
-
}
|
|
3686
|
-
|
|
3687
|
-
// For PNG color types 0, 2 and 3, the transparency data is stored in
|
|
4281
|
+
} // For PNG color types 0, 2 and 3, the transparency data is stored in
|
|
3688
4282
|
// a dedicated PNG chunk.
|
|
4283
|
+
|
|
4284
|
+
|
|
3689
4285
|
if (this.image.transparency.grayscale != null) {
|
|
3690
4286
|
// Use Color Key Masking (spec section 4.8.5)
|
|
3691
4287
|
// An array with N elements, where N is two times the number of color components.
|
|
@@ -3694,8 +4290,11 @@ class PNGImage {
|
|
|
3694
4290
|
} else if (this.image.transparency.rgb) {
|
|
3695
4291
|
// Use Color Key Masking (spec section 4.8.5)
|
|
3696
4292
|
// An array with N elements, where N is two times the number of color components.
|
|
3697
|
-
const {
|
|
4293
|
+
const {
|
|
4294
|
+
rgb
|
|
4295
|
+
} = this.image.transparency;
|
|
3698
4296
|
const mask = [];
|
|
4297
|
+
|
|
3699
4298
|
for (let x of rgb) {
|
|
3700
4299
|
mask.push(x, x);
|
|
3701
4300
|
}
|
|
@@ -3711,6 +4310,7 @@ class PNGImage {
|
|
|
3711
4310
|
// SMask object and store it separately in the PDF.
|
|
3712
4311
|
return this.splitAlphaChannel();
|
|
3713
4312
|
}
|
|
4313
|
+
|
|
3714
4314
|
this.finalize();
|
|
3715
4315
|
}
|
|
3716
4316
|
|
|
@@ -3726,15 +4326,13 @@ class PNGImage {
|
|
|
3726
4326
|
ColorSpace: 'DeviceGray',
|
|
3727
4327
|
Decode: [0, 1]
|
|
3728
4328
|
});
|
|
3729
|
-
|
|
3730
4329
|
sMask.end(this.alphaChannel);
|
|
3731
4330
|
this.obj.data['SMask'] = sMask;
|
|
3732
|
-
}
|
|
4331
|
+
} // add the actual image data
|
|
4332
|
+
|
|
3733
4333
|
|
|
3734
|
-
//
|
|
3735
|
-
this.obj.end(this.imgData);
|
|
4334
|
+
this.obj.end(this.imgData); // free memory
|
|
3736
4335
|
|
|
3737
|
-
// free memory
|
|
3738
4336
|
this.image = null;
|
|
3739
4337
|
return this.imgData = null;
|
|
3740
4338
|
}
|
|
@@ -3746,16 +4344,17 @@ class PNGImage {
|
|
|
3746
4344
|
const pixelCount = this.width * this.height;
|
|
3747
4345
|
const imgData = new Buffer(pixelCount * colorCount);
|
|
3748
4346
|
const alphaChannel = new Buffer(pixelCount);
|
|
3749
|
-
|
|
3750
4347
|
let i = p = a = 0;
|
|
3751
|
-
const len = pixels.length;
|
|
3752
|
-
|
|
4348
|
+
const len = pixels.length; // For 16bit images copy only most significant byte (MSB) - PNG data is always stored in network byte order (MSB first)
|
|
4349
|
+
|
|
3753
4350
|
const skipByteCount = this.image.bits === 16 ? 1 : 0;
|
|
4351
|
+
|
|
3754
4352
|
while (i < len) {
|
|
3755
4353
|
for (let colorIndex = 0; colorIndex < colorCount; colorIndex++) {
|
|
3756
4354
|
imgData[p++] = pixels[i++];
|
|
3757
4355
|
i += skipByteCount;
|
|
3758
4356
|
}
|
|
4357
|
+
|
|
3759
4358
|
alphaChannel[a++] = pixels[i++];
|
|
3760
4359
|
i += skipByteCount;
|
|
3761
4360
|
}
|
|
@@ -3770,8 +4369,8 @@ class PNGImage {
|
|
|
3770
4369
|
const transparency = this.image.transparency.indexed;
|
|
3771
4370
|
return this.image.decodePixels(pixels => {
|
|
3772
4371
|
const alphaChannel = new Buffer(this.width * this.height);
|
|
3773
|
-
|
|
3774
4372
|
let i = 0;
|
|
4373
|
+
|
|
3775
4374
|
for (let j = 0, end = pixels.length; j < end; j++) {
|
|
3776
4375
|
alphaChannel[i++] = transparency[pixels[j]];
|
|
3777
4376
|
}
|
|
@@ -3780,6 +4379,7 @@ class PNGImage {
|
|
|
3780
4379
|
return this.finalize();
|
|
3781
4380
|
});
|
|
3782
4381
|
}
|
|
4382
|
+
|
|
3783
4383
|
}
|
|
3784
4384
|
|
|
3785
4385
|
/*
|
|
@@ -3790,16 +4390,19 @@ By Devon Govett
|
|
|
3790
4390
|
class PDFImage {
|
|
3791
4391
|
static open(src, label) {
|
|
3792
4392
|
let data;
|
|
4393
|
+
|
|
3793
4394
|
if (Buffer.isBuffer(src)) {
|
|
3794
4395
|
data = src;
|
|
3795
4396
|
} else if (src instanceof ArrayBuffer) {
|
|
3796
4397
|
data = new Buffer(new Uint8Array(src));
|
|
3797
4398
|
} else {
|
|
3798
4399
|
let match;
|
|
4400
|
+
|
|
3799
4401
|
if (match = /^data:.+;base64,(.*)$/.exec(src)) {
|
|
3800
4402
|
data = new Buffer(match[1], 'base64');
|
|
3801
4403
|
} else {
|
|
3802
4404
|
data = fs.readFileSync(src);
|
|
4405
|
+
|
|
3803
4406
|
if (!data) {
|
|
3804
4407
|
return;
|
|
3805
4408
|
}
|
|
@@ -3814,6 +4417,7 @@ class PDFImage {
|
|
|
3814
4417
|
throw new Error('Unknown image format.');
|
|
3815
4418
|
}
|
|
3816
4419
|
}
|
|
4420
|
+
|
|
3817
4421
|
}
|
|
3818
4422
|
|
|
3819
4423
|
var ImagesMixin = {
|
|
@@ -3822,11 +4426,9 @@ var ImagesMixin = {
|
|
|
3822
4426
|
return this._imageCount = 0;
|
|
3823
4427
|
},
|
|
3824
4428
|
|
|
3825
|
-
image(src, x, y, options) {
|
|
4429
|
+
image(src, x, y, options = {}) {
|
|
3826
4430
|
let bh, bp, bw, image, ip, left, left1;
|
|
3827
|
-
|
|
3828
|
-
options = {};
|
|
3829
|
-
}
|
|
4431
|
+
|
|
3830
4432
|
if (typeof x === 'object') {
|
|
3831
4433
|
options = x;
|
|
3832
4434
|
x = null;
|
|
@@ -3873,6 +4475,7 @@ var ImagesMixin = {
|
|
|
3873
4475
|
[bw, bh] = options.fit;
|
|
3874
4476
|
bp = bw / bh;
|
|
3875
4477
|
ip = image.width / image.height;
|
|
4478
|
+
|
|
3876
4479
|
if (ip > bp) {
|
|
3877
4480
|
w = bw;
|
|
3878
4481
|
h = bw / ip;
|
|
@@ -3884,6 +4487,7 @@ var ImagesMixin = {
|
|
|
3884
4487
|
[bw, bh] = options.cover;
|
|
3885
4488
|
bp = bw / bh;
|
|
3886
4489
|
ip = image.width / image.height;
|
|
4490
|
+
|
|
3887
4491
|
if (ip > bp) {
|
|
3888
4492
|
h = bh;
|
|
3889
4493
|
w = bh * ip;
|
|
@@ -3905,9 +4509,22 @@ var ImagesMixin = {
|
|
|
3905
4509
|
} else if (options.valign === 'bottom') {
|
|
3906
4510
|
y = y + bh - h;
|
|
3907
4511
|
}
|
|
4512
|
+
} // create link annotations if the link option is given
|
|
4513
|
+
|
|
4514
|
+
|
|
4515
|
+
if (options.link != null) {
|
|
4516
|
+
this.link(x, y, w, h, options.link);
|
|
4517
|
+
}
|
|
4518
|
+
|
|
4519
|
+
if (options.goTo != null) {
|
|
4520
|
+
this.goTo(x, y, w, h, options.goTo);
|
|
3908
4521
|
}
|
|
3909
4522
|
|
|
3910
|
-
|
|
4523
|
+
if (options.destination != null) {
|
|
4524
|
+
this.addNamedDestination(options.destination, 'XYZ', x, y, null);
|
|
4525
|
+
} // Set the current y position to below the image if it is in the document flow
|
|
4526
|
+
|
|
4527
|
+
|
|
3911
4528
|
if (this.y === y) {
|
|
3912
4529
|
this.y += h;
|
|
3913
4530
|
}
|
|
@@ -3916,18 +4533,19 @@ var ImagesMixin = {
|
|
|
3916
4533
|
this.transform(w, 0, 0, -h, x, y + h);
|
|
3917
4534
|
this.addContent(`/${image.label} Do`);
|
|
3918
4535
|
this.restore();
|
|
3919
|
-
|
|
3920
4536
|
return this;
|
|
3921
4537
|
},
|
|
3922
4538
|
|
|
3923
4539
|
openImage(src) {
|
|
3924
4540
|
let image;
|
|
4541
|
+
|
|
3925
4542
|
if (typeof src === 'string') {
|
|
3926
4543
|
image = this._imageRegistry[src];
|
|
3927
4544
|
}
|
|
3928
4545
|
|
|
3929
4546
|
if (!image) {
|
|
3930
4547
|
image = PDFImage.open(src, `I${++this._imageCount}`);
|
|
4548
|
+
|
|
3931
4549
|
if (typeof src === 'string') {
|
|
3932
4550
|
this._imageRegistry[src] = image;
|
|
3933
4551
|
}
|
|
@@ -3935,6 +4553,7 @@ var ImagesMixin = {
|
|
|
3935
4553
|
|
|
3936
4554
|
return image;
|
|
3937
4555
|
}
|
|
4556
|
+
|
|
3938
4557
|
};
|
|
3939
4558
|
|
|
3940
4559
|
var AnnotationsMixin = {
|
|
@@ -3942,18 +4561,21 @@ var AnnotationsMixin = {
|
|
|
3942
4561
|
options.Type = 'Annot';
|
|
3943
4562
|
options.Rect = this._convertRect(x, y, w, h);
|
|
3944
4563
|
options.Border = [0, 0, 0];
|
|
4564
|
+
|
|
3945
4565
|
if (options.Subtype !== 'Link') {
|
|
3946
4566
|
if (options.C == null) {
|
|
3947
4567
|
options.C = this._normalizeColor(options.color || [0, 0, 0]);
|
|
3948
4568
|
}
|
|
3949
4569
|
} // convert colors
|
|
4570
|
+
|
|
4571
|
+
|
|
3950
4572
|
delete options.color;
|
|
3951
4573
|
|
|
3952
4574
|
if (typeof options.Dest === 'string') {
|
|
3953
4575
|
options.Dest = new String(options.Dest);
|
|
3954
|
-
}
|
|
4576
|
+
} // Capitalize keys
|
|
4577
|
+
|
|
3955
4578
|
|
|
3956
|
-
// Capitalize keys
|
|
3957
4579
|
for (let key in options) {
|
|
3958
4580
|
const val = options[key];
|
|
3959
4581
|
options[key[0].toUpperCase() + key.slice(1)] = val;
|
|
@@ -3965,28 +4587,35 @@ var AnnotationsMixin = {
|
|
|
3965
4587
|
return this;
|
|
3966
4588
|
},
|
|
3967
4589
|
|
|
3968
|
-
note(x, y, w, h, contents, options) {
|
|
3969
|
-
if (options == null) {
|
|
3970
|
-
options = {};
|
|
3971
|
-
}
|
|
4590
|
+
note(x, y, w, h, contents, options = {}) {
|
|
3972
4591
|
options.Subtype = 'Text';
|
|
3973
4592
|
options.Contents = new String(contents);
|
|
3974
4593
|
options.Name = 'Comment';
|
|
4594
|
+
|
|
3975
4595
|
if (options.color == null) {
|
|
3976
4596
|
options.color = [243, 223, 92];
|
|
3977
4597
|
}
|
|
4598
|
+
|
|
3978
4599
|
return this.annotate(x, y, w, h, options);
|
|
3979
4600
|
},
|
|
3980
4601
|
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
4602
|
+
goTo(x, y, w, h, name, options = {}) {
|
|
4603
|
+
options.Subtype = 'Link';
|
|
4604
|
+
options.A = this.ref({
|
|
4605
|
+
S: 'GoTo',
|
|
4606
|
+
D: new String(name)
|
|
4607
|
+
});
|
|
4608
|
+
options.A.end();
|
|
4609
|
+
return this.annotate(x, y, w, h, options);
|
|
4610
|
+
},
|
|
4611
|
+
|
|
4612
|
+
link(x, y, w, h, url, options = {}) {
|
|
3985
4613
|
options.Subtype = 'Link';
|
|
3986
4614
|
|
|
3987
4615
|
if (typeof url === 'number') {
|
|
3988
4616
|
// Link to a page in the document (the page must already exist)
|
|
3989
4617
|
const pages = this._root.data.Pages.data;
|
|
4618
|
+
|
|
3990
4619
|
if (url >= 0 && url < pages.Kids.length) {
|
|
3991
4620
|
options.A = this.ref({
|
|
3992
4621
|
S: 'GoTo',
|
|
@@ -4008,75 +4637,54 @@ var AnnotationsMixin = {
|
|
|
4008
4637
|
return this.annotate(x, y, w, h, options);
|
|
4009
4638
|
},
|
|
4010
4639
|
|
|
4011
|
-
_markup(x, y, w, h, options) {
|
|
4012
|
-
if (options == null) {
|
|
4013
|
-
options = {};
|
|
4014
|
-
}
|
|
4640
|
+
_markup(x, y, w, h, options = {}) {
|
|
4015
4641
|
const [x1, y1, x2, y2] = this._convertRect(x, y, w, h);
|
|
4642
|
+
|
|
4016
4643
|
options.QuadPoints = [x1, y2, x2, y2, x1, y1, x2, y1];
|
|
4017
4644
|
options.Contents = new String();
|
|
4018
4645
|
return this.annotate(x, y, w, h, options);
|
|
4019
4646
|
},
|
|
4020
4647
|
|
|
4021
|
-
highlight(x, y, w, h, options) {
|
|
4022
|
-
if (options == null) {
|
|
4023
|
-
options = {};
|
|
4024
|
-
}
|
|
4648
|
+
highlight(x, y, w, h, options = {}) {
|
|
4025
4649
|
options.Subtype = 'Highlight';
|
|
4650
|
+
|
|
4026
4651
|
if (options.color == null) {
|
|
4027
4652
|
options.color = [241, 238, 148];
|
|
4028
4653
|
}
|
|
4654
|
+
|
|
4029
4655
|
return this._markup(x, y, w, h, options);
|
|
4030
4656
|
},
|
|
4031
4657
|
|
|
4032
|
-
underline(x, y, w, h, options) {
|
|
4033
|
-
if (options == null) {
|
|
4034
|
-
options = {};
|
|
4035
|
-
}
|
|
4658
|
+
underline(x, y, w, h, options = {}) {
|
|
4036
4659
|
options.Subtype = 'Underline';
|
|
4037
4660
|
return this._markup(x, y, w, h, options);
|
|
4038
4661
|
},
|
|
4039
4662
|
|
|
4040
|
-
strike(x, y, w, h, options) {
|
|
4041
|
-
if (options == null) {
|
|
4042
|
-
options = {};
|
|
4043
|
-
}
|
|
4663
|
+
strike(x, y, w, h, options = {}) {
|
|
4044
4664
|
options.Subtype = 'StrikeOut';
|
|
4045
4665
|
return this._markup(x, y, w, h, options);
|
|
4046
4666
|
},
|
|
4047
4667
|
|
|
4048
|
-
lineAnnotation(x1, y1, x2, y2, options) {
|
|
4049
|
-
if (options == null) {
|
|
4050
|
-
options = {};
|
|
4051
|
-
}
|
|
4668
|
+
lineAnnotation(x1, y1, x2, y2, options = {}) {
|
|
4052
4669
|
options.Subtype = 'Line';
|
|
4053
4670
|
options.Contents = new String();
|
|
4054
4671
|
options.L = [x1, this.page.height - y1, x2, this.page.height - y2];
|
|
4055
4672
|
return this.annotate(x1, y1, x2, y2, options);
|
|
4056
4673
|
},
|
|
4057
4674
|
|
|
4058
|
-
rectAnnotation(x, y, w, h, options) {
|
|
4059
|
-
if (options == null) {
|
|
4060
|
-
options = {};
|
|
4061
|
-
}
|
|
4675
|
+
rectAnnotation(x, y, w, h, options = {}) {
|
|
4062
4676
|
options.Subtype = 'Square';
|
|
4063
4677
|
options.Contents = new String();
|
|
4064
4678
|
return this.annotate(x, y, w, h, options);
|
|
4065
4679
|
},
|
|
4066
4680
|
|
|
4067
|
-
ellipseAnnotation(x, y, w, h, options) {
|
|
4068
|
-
if (options == null) {
|
|
4069
|
-
options = {};
|
|
4070
|
-
}
|
|
4681
|
+
ellipseAnnotation(x, y, w, h, options = {}) {
|
|
4071
4682
|
options.Subtype = 'Circle';
|
|
4072
4683
|
options.Contents = new String();
|
|
4073
4684
|
return this.annotate(x, y, w, h, options);
|
|
4074
4685
|
},
|
|
4075
4686
|
|
|
4076
|
-
textAnnotation(x, y, w, h, text, options) {
|
|
4077
|
-
if (options == null) {
|
|
4078
|
-
options = {};
|
|
4079
|
-
}
|
|
4687
|
+
textAnnotation(x, y, w, h, text, options = {}) {
|
|
4080
4688
|
options.Subtype = 'FreeText';
|
|
4081
4689
|
options.Contents = new String(text);
|
|
4082
4690
|
options.DA = new String();
|
|
@@ -4086,28 +4694,25 @@ var AnnotationsMixin = {
|
|
|
4086
4694
|
_convertRect(x1, y1, w, h) {
|
|
4087
4695
|
// flip y1 and y2
|
|
4088
4696
|
let y2 = y1;
|
|
4089
|
-
y1 += h;
|
|
4697
|
+
y1 += h; // make x2
|
|
4090
4698
|
|
|
4091
|
-
//
|
|
4092
|
-
let x2 = x1 + w;
|
|
4699
|
+
let x2 = x1 + w; // apply current transformation matrix to points
|
|
4093
4700
|
|
|
4094
|
-
// apply current transformation matrix to points
|
|
4095
4701
|
const [m0, m1, m2, m3, m4, m5] = this._ctm;
|
|
4096
4702
|
x1 = m0 * x1 + m2 * y1 + m4;
|
|
4097
4703
|
y1 = m1 * x1 + m3 * y1 + m5;
|
|
4098
4704
|
x2 = m0 * x2 + m2 * y2 + m4;
|
|
4099
4705
|
y2 = m1 * x2 + m3 * y2 + m5;
|
|
4100
|
-
|
|
4101
4706
|
return [x1, y1, x2, y2];
|
|
4102
4707
|
}
|
|
4708
|
+
|
|
4103
4709
|
};
|
|
4104
4710
|
|
|
4105
4711
|
class PDFOutline {
|
|
4106
|
-
constructor(document, parent, title, dest, options
|
|
4712
|
+
constructor(document, parent, title, dest, options = {
|
|
4713
|
+
expanded: false
|
|
4714
|
+
}) {
|
|
4107
4715
|
this.document = document;
|
|
4108
|
-
if (options == null) {
|
|
4109
|
-
options = { expanded: false };
|
|
4110
|
-
}
|
|
4111
4716
|
this.options = options;
|
|
4112
4717
|
this.outlineData = {};
|
|
4113
4718
|
|
|
@@ -4127,13 +4732,11 @@ class PDFOutline {
|
|
|
4127
4732
|
this.children = [];
|
|
4128
4733
|
}
|
|
4129
4734
|
|
|
4130
|
-
addItem(title, options
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
}
|
|
4735
|
+
addItem(title, options = {
|
|
4736
|
+
expanded: false
|
|
4737
|
+
}) {
|
|
4134
4738
|
const result = new PDFOutline(this.document, this.dictionary, title, this.document.page, options);
|
|
4135
4739
|
this.children.push(result);
|
|
4136
|
-
|
|
4137
4740
|
return result;
|
|
4138
4741
|
}
|
|
4139
4742
|
|
|
@@ -4150,18 +4753,22 @@ class PDFOutline {
|
|
|
4150
4753
|
|
|
4151
4754
|
for (let i = 0, len = this.children.length; i < len; i++) {
|
|
4152
4755
|
const child = this.children[i];
|
|
4756
|
+
|
|
4153
4757
|
if (i > 0) {
|
|
4154
4758
|
child.outlineData.Prev = this.children[i - 1].dictionary;
|
|
4155
4759
|
}
|
|
4760
|
+
|
|
4156
4761
|
if (i < this.children.length - 1) {
|
|
4157
4762
|
child.outlineData.Next = this.children[i + 1].dictionary;
|
|
4158
4763
|
}
|
|
4764
|
+
|
|
4159
4765
|
child.endOutline();
|
|
4160
4766
|
}
|
|
4161
4767
|
}
|
|
4162
4768
|
|
|
4163
4769
|
return this.dictionary.end();
|
|
4164
4770
|
}
|
|
4771
|
+
|
|
4165
4772
|
}
|
|
4166
4773
|
|
|
4167
4774
|
var OutlineMixin = {
|
|
@@ -4171,11 +4778,13 @@ var OutlineMixin = {
|
|
|
4171
4778
|
|
|
4172
4779
|
endOutline() {
|
|
4173
4780
|
this.outline.endOutline();
|
|
4781
|
+
|
|
4174
4782
|
if (this.outline.children.length > 0) {
|
|
4175
4783
|
this._root.data.Outlines = this.outline.dictionary;
|
|
4176
4784
|
return this._root.data.PageMode = 'UseOutlines';
|
|
4177
4785
|
}
|
|
4178
4786
|
}
|
|
4787
|
+
|
|
4179
4788
|
};
|
|
4180
4789
|
|
|
4181
4790
|
/*
|
|
@@ -4186,35 +4795,36 @@ By Devon Govett
|
|
|
4186
4795
|
class PDFDocument extends stream.Readable {
|
|
4187
4796
|
constructor(options = {}) {
|
|
4188
4797
|
super(options);
|
|
4189
|
-
this.options = options;
|
|
4798
|
+
this.options = options; // PDF version
|
|
4190
4799
|
|
|
4191
|
-
// PDF version
|
|
4192
4800
|
switch (options.pdfVersion) {
|
|
4193
4801
|
case '1.4':
|
|
4194
4802
|
this.version = 1.4;
|
|
4195
4803
|
break;
|
|
4804
|
+
|
|
4196
4805
|
case '1.5':
|
|
4197
4806
|
this.version = 1.5;
|
|
4198
4807
|
break;
|
|
4808
|
+
|
|
4199
4809
|
case '1.6':
|
|
4200
4810
|
this.version = 1.6;
|
|
4201
4811
|
break;
|
|
4812
|
+
|
|
4202
4813
|
case '1.7':
|
|
4203
4814
|
case '1.7ext3':
|
|
4204
4815
|
this.version = 1.7;
|
|
4205
4816
|
break;
|
|
4817
|
+
|
|
4206
4818
|
default:
|
|
4207
4819
|
this.version = 1.3;
|
|
4208
4820
|
break;
|
|
4209
|
-
}
|
|
4821
|
+
} // Whether streams should be compressed
|
|
4210
4822
|
|
|
4211
|
-
// Whether streams should be compressed
|
|
4212
|
-
this.compress = this.options.compress != null ? this.options.compress : true;
|
|
4213
4823
|
|
|
4824
|
+
this.compress = this.options.compress != null ? this.options.compress : true;
|
|
4214
4825
|
this._pageBuffer = [];
|
|
4215
|
-
this._pageBufferStart = 0;
|
|
4826
|
+
this._pageBufferStart = 0; // The PDF object store
|
|
4216
4827
|
|
|
4217
|
-
// The PDF object store
|
|
4218
4828
|
this._offsets = [];
|
|
4219
4829
|
this._waiting = 0;
|
|
4220
4830
|
this._ended = false;
|
|
@@ -4224,36 +4834,24 @@ class PDFDocument extends stream.Readable {
|
|
|
4224
4834
|
Count: 0,
|
|
4225
4835
|
Kids: []
|
|
4226
4836
|
});
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
this.document._write(this.id + ' ' + this.gen + ' obj');
|
|
4231
|
-
this.document._write('<<');
|
|
4232
|
-
this.document._write('/Type /Pages');
|
|
4233
|
-
this.document._write(`/Count ${this.data.Count}`);
|
|
4234
|
-
this.document._write(`/Kids [${Buffer.concat(this.data.Kids).slice(0, -1).toString()}]`);
|
|
4235
|
-
this.document._write('>>');
|
|
4236
|
-
this.document._write('endobj');
|
|
4237
|
-
return this.document._refEnd(this);
|
|
4238
|
-
};
|
|
4239
|
-
|
|
4837
|
+
const Names = this.ref({
|
|
4838
|
+
Dests: new PDFNameTree()
|
|
4839
|
+
});
|
|
4240
4840
|
this._root = this.ref({
|
|
4241
4841
|
Type: 'Catalog',
|
|
4242
|
-
Pages
|
|
4243
|
-
|
|
4842
|
+
Pages,
|
|
4843
|
+
Names
|
|
4844
|
+
}); // The current page
|
|
4244
4845
|
|
|
4245
|
-
//
|
|
4246
|
-
this.page = null;
|
|
4846
|
+
this.page = null; // Initialize mixins
|
|
4247
4847
|
|
|
4248
|
-
// Initialize mixins
|
|
4249
4848
|
this.initColor();
|
|
4250
4849
|
this.initVector();
|
|
4251
4850
|
this.initFonts(options.font);
|
|
4252
4851
|
this.initText();
|
|
4253
4852
|
this.initImages();
|
|
4254
|
-
this.initOutline();
|
|
4853
|
+
this.initOutline(); // Initialize the metadata
|
|
4255
4854
|
|
|
4256
|
-
// Initialize the metadata
|
|
4257
4855
|
this.info = {
|
|
4258
4856
|
Producer: 'PDFKit',
|
|
4259
4857
|
Creator: 'PDFKit',
|
|
@@ -4265,22 +4863,20 @@ class PDFDocument extends stream.Readable {
|
|
|
4265
4863
|
const val = this.options.info[key];
|
|
4266
4864
|
this.info[key] = val;
|
|
4267
4865
|
}
|
|
4268
|
-
}
|
|
4866
|
+
} // Generate file ID
|
|
4269
4867
|
|
|
4270
|
-
// Generate file ID
|
|
4271
|
-
this._id = PDFSecurity.generateFileID(this.info);
|
|
4272
4868
|
|
|
4273
|
-
// Initialize security settings
|
|
4274
|
-
this._security = PDFSecurity.create(this, options);
|
|
4869
|
+
this._id = PDFSecurity.generateFileID(this.info); // Initialize security settings
|
|
4275
4870
|
|
|
4276
|
-
// Write the header
|
|
4871
|
+
this._security = PDFSecurity.create(this, options); // Write the header
|
|
4277
4872
|
// PDF version
|
|
4278
|
-
this._write(`%PDF-${this.version}`);
|
|
4279
4873
|
|
|
4280
|
-
// 4 binary chars, as recommended by the spec
|
|
4281
|
-
|
|
4874
|
+
this._write(`%PDF-${this.version}`); // 4 binary chars, as recommended by the spec
|
|
4875
|
+
|
|
4876
|
+
|
|
4877
|
+
this._write('%\xFF\xFF\xFF\xFF'); // Add the first page
|
|
4878
|
+
|
|
4282
4879
|
|
|
4283
|
-
// Add the first page
|
|
4284
4880
|
if (this.options.autoFirstPage !== false) {
|
|
4285
4881
|
this.addPage();
|
|
4286
4882
|
}
|
|
@@ -4289,41 +4885,45 @@ class PDFDocument extends stream.Readable {
|
|
|
4289
4885
|
addPage(options) {
|
|
4290
4886
|
// end the current page if needed
|
|
4291
4887
|
if (options == null) {
|
|
4292
|
-
({
|
|
4888
|
+
({
|
|
4889
|
+
options
|
|
4890
|
+
} = this);
|
|
4293
4891
|
}
|
|
4892
|
+
|
|
4294
4893
|
if (!this.options.bufferPages) {
|
|
4295
4894
|
this.flushPages();
|
|
4296
|
-
}
|
|
4895
|
+
} // create a page object
|
|
4896
|
+
|
|
4297
4897
|
|
|
4298
|
-
// create a page object
|
|
4299
4898
|
this.page = new PDFPage(this, options);
|
|
4300
|
-
this._pageBuffer.push(this.page);
|
|
4301
4899
|
|
|
4302
|
-
// add the page to the object store
|
|
4900
|
+
this._pageBuffer.push(this.page); // add the page to the object store
|
|
4901
|
+
|
|
4902
|
+
|
|
4303
4903
|
const pages = this._root.data.Pages.data;
|
|
4304
|
-
pages.Kids.push(
|
|
4305
|
-
pages.Count++;
|
|
4904
|
+
pages.Kids.push(this.page.dictionary);
|
|
4905
|
+
pages.Count++; // reset x and y coordinates
|
|
4306
4906
|
|
|
4307
|
-
// reset x and y coordinates
|
|
4308
4907
|
this.x = this.page.margins.left;
|
|
4309
|
-
this.y = this.page.margins.top;
|
|
4310
|
-
|
|
4311
|
-
// flip PDF coordinate system so that the origin is in
|
|
4908
|
+
this.y = this.page.margins.top; // flip PDF coordinate system so that the origin is in
|
|
4312
4909
|
// the top left rather than the bottom left
|
|
4910
|
+
|
|
4313
4911
|
this._ctm = [1, 0, 0, 1, 0, 0];
|
|
4314
4912
|
this.transform(1, 0, 0, -1, 0, this.page.height);
|
|
4315
|
-
|
|
4316
4913
|
this.emit('pageAdded');
|
|
4317
|
-
|
|
4318
4914
|
return this;
|
|
4319
4915
|
}
|
|
4320
4916
|
|
|
4321
4917
|
bufferedPageRange() {
|
|
4322
|
-
return {
|
|
4918
|
+
return {
|
|
4919
|
+
start: this._pageBufferStart,
|
|
4920
|
+
count: this._pageBuffer.length
|
|
4921
|
+
};
|
|
4323
4922
|
}
|
|
4324
4923
|
|
|
4325
4924
|
switchToPage(n) {
|
|
4326
4925
|
let page;
|
|
4926
|
+
|
|
4327
4927
|
if (!(page = this._pageBuffer[n - this._pageBufferStart])) {
|
|
4328
4928
|
throw new Error(`switchToPage(${n}) out of bounds, current buffer covers pages ${this._pageBufferStart} to ${this._pageBufferStart + this._pageBuffer.length - 1}`);
|
|
4329
4929
|
}
|
|
@@ -4337,20 +4937,38 @@ class PDFDocument extends stream.Readable {
|
|
|
4337
4937
|
const pages = this._pageBuffer;
|
|
4338
4938
|
this._pageBuffer = [];
|
|
4339
4939
|
this._pageBufferStart += pages.length;
|
|
4940
|
+
|
|
4340
4941
|
for (let page of pages) {
|
|
4341
4942
|
page.end();
|
|
4342
4943
|
}
|
|
4343
4944
|
}
|
|
4344
4945
|
|
|
4946
|
+
addNamedDestination(name, ...args) {
|
|
4947
|
+
if (args.length === 0) {
|
|
4948
|
+
args = ['XYZ', null, null, null];
|
|
4949
|
+
}
|
|
4950
|
+
|
|
4951
|
+
if (args[0] === 'XYZ' && args[2] !== null) {
|
|
4952
|
+
args[2] = this.page.height - args[2];
|
|
4953
|
+
}
|
|
4954
|
+
|
|
4955
|
+
args.unshift(this.page.dictionary);
|
|
4956
|
+
|
|
4957
|
+
this._root.data.Names.data.Dests.add(name, args);
|
|
4958
|
+
}
|
|
4959
|
+
|
|
4345
4960
|
ref(data) {
|
|
4346
4961
|
const ref = new PDFReference(this, this._offsets.length + 1, data);
|
|
4962
|
+
|
|
4347
4963
|
this._offsets.push(null); // placeholder for this object's offset once it is finalized
|
|
4964
|
+
|
|
4965
|
+
|
|
4348
4966
|
this._waiting++;
|
|
4349
4967
|
return ref;
|
|
4350
4968
|
}
|
|
4351
4969
|
|
|
4352
|
-
_read() {}
|
|
4353
|
-
|
|
4970
|
+
_read() {} // do nothing, but this method is required by node
|
|
4971
|
+
|
|
4354
4972
|
|
|
4355
4973
|
_write(data) {
|
|
4356
4974
|
if (!Buffer.isBuffer(data)) {
|
|
@@ -4368,8 +4986,10 @@ class PDFDocument extends stream.Readable {
|
|
|
4368
4986
|
|
|
4369
4987
|
_refEnd(ref) {
|
|
4370
4988
|
this._offsets[ref.id - 1] = ref.offset;
|
|
4989
|
+
|
|
4371
4990
|
if (--this._waiting === 0 && this._ended) {
|
|
4372
4991
|
this._finalize();
|
|
4992
|
+
|
|
4373
4993
|
return this._ended = false;
|
|
4374
4994
|
}
|
|
4375
4995
|
}
|
|
@@ -4380,34 +5000,25 @@ class PDFDocument extends stream.Readable {
|
|
|
4380
5000
|
PDFDocument#write is deprecated, and will be removed in a future version of PDFKit. \
|
|
4381
5001
|
Please pipe the document into a Node stream.\
|
|
4382
5002
|
`);
|
|
4383
|
-
|
|
4384
5003
|
console.warn(err.stack);
|
|
4385
|
-
|
|
4386
5004
|
this.pipe(fs.createWriteStream(filename));
|
|
4387
5005
|
this.end();
|
|
4388
5006
|
return this.once('end', fn);
|
|
4389
5007
|
}
|
|
4390
5008
|
|
|
4391
|
-
output(fn) {
|
|
4392
|
-
// more difficult to support this. It would involve concatenating all the buffers together
|
|
4393
|
-
throw new Error(`\
|
|
4394
|
-
PDFDocument#output is deprecated, and has been removed from PDFKit. \
|
|
4395
|
-
Please pipe the document into a Node stream.\
|
|
4396
|
-
`);
|
|
4397
|
-
}
|
|
4398
|
-
|
|
4399
5009
|
end() {
|
|
4400
5010
|
this.flushPages();
|
|
4401
5011
|
this._info = this.ref();
|
|
5012
|
+
|
|
4402
5013
|
for (let key in this.info) {
|
|
4403
5014
|
let val = this.info[key];
|
|
5015
|
+
|
|
4404
5016
|
if (typeof val === 'string') {
|
|
4405
5017
|
val = new String(val);
|
|
4406
5018
|
}
|
|
4407
5019
|
|
|
4408
5020
|
let entry = this.ref(val);
|
|
4409
5021
|
entry.end();
|
|
4410
|
-
|
|
4411
5022
|
this._info.data[key] = entry;
|
|
4412
5023
|
}
|
|
4413
5024
|
|
|
@@ -4421,8 +5032,11 @@ Please pipe the document into a Node stream.\
|
|
|
4421
5032
|
this.endOutline();
|
|
4422
5033
|
|
|
4423
5034
|
this._root.end();
|
|
5035
|
+
|
|
4424
5036
|
this._root.data.Pages.end();
|
|
4425
5037
|
|
|
5038
|
+
this._root.data.Names.end();
|
|
5039
|
+
|
|
4426
5040
|
if (this._security) {
|
|
4427
5041
|
this._security.end();
|
|
4428
5042
|
}
|
|
@@ -4437,40 +5051,49 @@ Please pipe the document into a Node stream.\
|
|
|
4437
5051
|
_finalize(fn) {
|
|
4438
5052
|
// generate xref
|
|
4439
5053
|
const xRefOffset = this._offset;
|
|
5054
|
+
|
|
4440
5055
|
this._write('xref');
|
|
5056
|
+
|
|
4441
5057
|
this._write(`0 ${this._offsets.length + 1}`);
|
|
5058
|
+
|
|
4442
5059
|
this._write('0000000000 65535 f ');
|
|
4443
5060
|
|
|
4444
5061
|
for (let offset of this._offsets) {
|
|
4445
5062
|
offset = `0000000000${offset}`.slice(-10);
|
|
5063
|
+
|
|
4446
5064
|
this._write(offset + ' 00000 n ');
|
|
4447
|
-
}
|
|
5065
|
+
} // trailer
|
|
5066
|
+
|
|
4448
5067
|
|
|
4449
|
-
// trailer
|
|
4450
5068
|
const trailer = {
|
|
4451
5069
|
Size: this._offsets.length + 1,
|
|
4452
5070
|
Root: this._root,
|
|
4453
5071
|
Info: this._info,
|
|
4454
5072
|
ID: [this._id, this._id]
|
|
4455
5073
|
};
|
|
5074
|
+
|
|
4456
5075
|
if (this._security) {
|
|
4457
5076
|
trailer.Encrypt = this._security.dictionary;
|
|
4458
5077
|
}
|
|
4459
5078
|
|
|
4460
5079
|
this._write('trailer');
|
|
5080
|
+
|
|
4461
5081
|
this._write(PDFObject.convert(trailer));
|
|
4462
5082
|
|
|
4463
5083
|
this._write('startxref');
|
|
5084
|
+
|
|
4464
5085
|
this._write(`${xRefOffset}`);
|
|
4465
|
-
this._write('%%EOF');
|
|
4466
5086
|
|
|
4467
|
-
// end the stream
|
|
5087
|
+
this._write('%%EOF'); // end the stream
|
|
5088
|
+
|
|
5089
|
+
|
|
4468
5090
|
return this.push(null);
|
|
4469
5091
|
}
|
|
4470
5092
|
|
|
4471
5093
|
toString() {
|
|
4472
5094
|
return '[object PDFDocument]';
|
|
4473
5095
|
}
|
|
5096
|
+
|
|
4474
5097
|
}
|
|
4475
5098
|
|
|
4476
5099
|
const mixin = methods => {
|