@kittl/pdfkit 0.17.3 → 0.17.4
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/.yarn/install-state.gz +0 -0
- package/.yarn/releases/yarn-4.13.0.cjs +940 -0
- package/.yarnrc.yml +3 -0
- package/CHANGELOG.md +14 -0
- package/README.md +11 -4
- package/js/pdfkit.es.js +522 -161
- package/js/pdfkit.es.js.map +1 -1
- package/js/pdfkit.js +520 -161
- package/js/pdfkit.js.map +1 -1
- package/js/pdfkit.standalone.js +11001 -15058
- package/package.json +8 -5
- package/types/index.d.ts +1160 -0
package/js/pdfkit.js
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
var stream = require('stream');
|
|
4
4
|
var zlib = require('zlib');
|
|
5
|
-
var
|
|
5
|
+
var utils = require('@noble/hashes/utils');
|
|
6
|
+
var md5 = require('js-md5');
|
|
7
|
+
var sha256 = require('@noble/hashes/sha256');
|
|
8
|
+
var aes = require('@noble/ciphers/aes');
|
|
6
9
|
var fs = require('fs');
|
|
7
10
|
var fontkit = require('fontkit');
|
|
8
11
|
var events = require('events');
|
|
9
12
|
var LineBreaker = require('linebreak');
|
|
10
|
-
var exif = require('jpeg-exif');
|
|
11
13
|
var PNG = require('png-js');
|
|
12
14
|
|
|
13
15
|
class PDFAbstractReference {
|
|
@@ -359,6 +361,7 @@ class PDFPage {
|
|
|
359
361
|
this._options = options;
|
|
360
362
|
this.size = options.size || 'letter';
|
|
361
363
|
this.layout = options.layout || 'portrait';
|
|
364
|
+
this.userUnit = options.userUnit || 1.0;
|
|
362
365
|
const dimensions = Array.isArray(this.size) ? this.size : SIZES[this.size.toUpperCase()];
|
|
363
366
|
this.width = dimensions[this.layout === 'portrait' ? 0 : 1];
|
|
364
367
|
this.height = dimensions[this.layout === 'portrait' ? 1 : 0];
|
|
@@ -374,7 +377,8 @@ class PDFPage {
|
|
|
374
377
|
Parent: this.document._root.data.Pages,
|
|
375
378
|
MediaBox: [0, 0, this.width, this.height],
|
|
376
379
|
Contents: this.content,
|
|
377
|
-
Resources: this.resources
|
|
380
|
+
Resources: this.resources,
|
|
381
|
+
UserUnit: this.userUnit
|
|
378
382
|
});
|
|
379
383
|
this.markings = [];
|
|
380
384
|
}
|
|
@@ -430,6 +434,7 @@ class PDFPage {
|
|
|
430
434
|
for (let color of Object.values(this.document.spotColors)) {
|
|
431
435
|
this.resources.data.ColorSpace[color.id] = color;
|
|
432
436
|
}
|
|
437
|
+
this.document._writeSpaceToResources(this.resources);
|
|
433
438
|
this.resources.end();
|
|
434
439
|
return this.content.end();
|
|
435
440
|
}
|
|
@@ -447,6 +452,58 @@ class PDFNameTree extends PDFTree {
|
|
|
447
452
|
}
|
|
448
453
|
}
|
|
449
454
|
|
|
455
|
+
function md5Hash(data) {
|
|
456
|
+
return new Uint8Array(md5.arrayBuffer(data));
|
|
457
|
+
}
|
|
458
|
+
function md5Hex(data) {
|
|
459
|
+
return md5(data);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function sha256Hash(data) {
|
|
463
|
+
return sha256.sha256(data);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function aesCbcEncrypt(data, key, iv, padding = true) {
|
|
467
|
+
return aes.cbc(key, iv, {
|
|
468
|
+
disablePadding: !padding
|
|
469
|
+
}).encrypt(data);
|
|
470
|
+
}
|
|
471
|
+
function aesEcbEncrypt(data, key) {
|
|
472
|
+
return aes.ecb(key, {
|
|
473
|
+
disablePadding: true
|
|
474
|
+
}).encrypt(data);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function rc4(data, key) {
|
|
478
|
+
const s = new Uint8Array(256);
|
|
479
|
+
for (let i = 0; i < 256; i++) {
|
|
480
|
+
s[i] = i;
|
|
481
|
+
}
|
|
482
|
+
let j = 0;
|
|
483
|
+
for (let i = 0; i < 256; i++) {
|
|
484
|
+
j = j + s[i] + key[i % key.length] & 0xff;
|
|
485
|
+
[s[i], s[j]] = [s[j], s[i]];
|
|
486
|
+
}
|
|
487
|
+
const output = new Uint8Array(data.length);
|
|
488
|
+
for (let i = 0, j = 0, k = 0; k < data.length; k++) {
|
|
489
|
+
i = i + 1 & 0xff;
|
|
490
|
+
j = j + s[i] & 0xff;
|
|
491
|
+
[s[i], s[j]] = [s[j], s[i]];
|
|
492
|
+
output[k] = data[k] ^ s[s[i] + s[j] & 0xff];
|
|
493
|
+
}
|
|
494
|
+
return output;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function randomBytes(length) {
|
|
498
|
+
const bytes = new Uint8Array(length);
|
|
499
|
+
if (globalThis.crypto?.getRandomValues) {
|
|
500
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
501
|
+
} else {
|
|
502
|
+
require('crypto').randomFillSync(bytes);
|
|
503
|
+
}
|
|
504
|
+
return bytes;
|
|
505
|
+
}
|
|
506
|
+
|
|
450
507
|
function inRange(value, rangeGroup) {
|
|
451
508
|
if (value < rangeGroup[0]) return false;
|
|
452
509
|
let startRange = 0;
|
|
@@ -545,10 +602,10 @@ class PDFSecurity {
|
|
|
545
602
|
}
|
|
546
603
|
infoStr += `${key}: ${info[key].valueOf()}\n`;
|
|
547
604
|
}
|
|
548
|
-
return
|
|
605
|
+
return Buffer.from(md5Hash(infoStr));
|
|
549
606
|
}
|
|
550
607
|
static generateRandomWordArray(bytes) {
|
|
551
|
-
return
|
|
608
|
+
return randomBytes(bytes);
|
|
552
609
|
}
|
|
553
610
|
static create(document, options = {}) {
|
|
554
611
|
if (!options.ownerPassword && !options.userPassword) {
|
|
@@ -640,8 +697,8 @@ class PDFSecurity {
|
|
|
640
697
|
encDict.StrF = 'StdCF';
|
|
641
698
|
}
|
|
642
699
|
encDict.R = r;
|
|
643
|
-
encDict.O =
|
|
644
|
-
encDict.U =
|
|
700
|
+
encDict.O = Buffer.from(ownerPasswordEntry);
|
|
701
|
+
encDict.U = Buffer.from(userPasswordEntry);
|
|
645
702
|
encDict.P = permissions;
|
|
646
703
|
}
|
|
647
704
|
_setupEncryptionV5(encDict, options) {
|
|
@@ -651,10 +708,10 @@ class PDFSecurity {
|
|
|
651
708
|
const processedOwnerPassword = options.ownerPassword ? processPasswordR5(options.ownerPassword) : processedUserPassword;
|
|
652
709
|
this.encryptionKey = getEncryptionKeyR5(PDFSecurity.generateRandomWordArray);
|
|
653
710
|
const userPasswordEntry = getUserPasswordR5(processedUserPassword, PDFSecurity.generateRandomWordArray);
|
|
654
|
-
const userKeySalt =
|
|
711
|
+
const userKeySalt = userPasswordEntry.slice(40, 48);
|
|
655
712
|
const userEncryptionKeyEntry = getUserEncryptionKeyR5(processedUserPassword, userKeySalt, this.encryptionKey);
|
|
656
713
|
const ownerPasswordEntry = getOwnerPasswordR5(processedOwnerPassword, userPasswordEntry, PDFSecurity.generateRandomWordArray);
|
|
657
|
-
const ownerKeySalt =
|
|
714
|
+
const ownerKeySalt = ownerPasswordEntry.slice(40, 48);
|
|
658
715
|
const ownerEncryptionKeyEntry = getOwnerEncryptionKeyR5(processedOwnerPassword, ownerKeySalt, userPasswordEntry, this.encryptionKey);
|
|
659
716
|
const permsEntry = getEncryptedPermissionsR5(permissions, this.encryptionKey, PDFSecurity.generateRandomWordArray);
|
|
660
717
|
encDict.V = 5;
|
|
@@ -669,36 +726,37 @@ class PDFSecurity {
|
|
|
669
726
|
encDict.StmF = 'StdCF';
|
|
670
727
|
encDict.StrF = 'StdCF';
|
|
671
728
|
encDict.R = 5;
|
|
672
|
-
encDict.O =
|
|
673
|
-
encDict.OE =
|
|
674
|
-
encDict.U =
|
|
675
|
-
encDict.UE =
|
|
729
|
+
encDict.O = Buffer.from(ownerPasswordEntry);
|
|
730
|
+
encDict.OE = Buffer.from(ownerEncryptionKeyEntry);
|
|
731
|
+
encDict.U = Buffer.from(userPasswordEntry);
|
|
732
|
+
encDict.UE = Buffer.from(userEncryptionKeyEntry);
|
|
676
733
|
encDict.P = permissions;
|
|
677
|
-
encDict.Perms =
|
|
734
|
+
encDict.Perms = Buffer.from(permsEntry);
|
|
678
735
|
}
|
|
679
736
|
getEncryptFn(obj, gen) {
|
|
680
737
|
let digest;
|
|
681
738
|
if (this.version < 5) {
|
|
682
|
-
|
|
739
|
+
const suffix = new Uint8Array([obj & 0xff, obj >> 8 & 0xff, obj >> 16 & 0xff, gen & 0xff, gen >> 8 & 0xff]);
|
|
740
|
+
digest = utils.concatBytes(this.encryptionKey, suffix);
|
|
683
741
|
}
|
|
684
742
|
if (this.version === 1 || this.version === 2) {
|
|
685
|
-
let key =
|
|
686
|
-
|
|
687
|
-
|
|
743
|
+
let key = md5Hash(digest);
|
|
744
|
+
const keyLen = Math.min(16, this.keyBits / 8 + 5);
|
|
745
|
+
key = key.slice(0, keyLen);
|
|
746
|
+
return buffer => Buffer.from(rc4(new Uint8Array(buffer), key));
|
|
688
747
|
}
|
|
689
748
|
let key;
|
|
690
749
|
if (this.version === 4) {
|
|
691
|
-
|
|
750
|
+
const saltMarker = new Uint8Array([0x73, 0x41, 0x6c, 0x54]);
|
|
751
|
+
key = md5Hash(utils.concatBytes(digest, saltMarker));
|
|
692
752
|
} else {
|
|
693
753
|
key = this.encryptionKey;
|
|
694
754
|
}
|
|
695
755
|
const iv = PDFSecurity.generateRandomWordArray(16);
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
iv
|
|
756
|
+
return buffer => {
|
|
757
|
+
const encrypted = aesCbcEncrypt(new Uint8Array(buffer), key, iv, true);
|
|
758
|
+
return Buffer.from(utils.concatBytes(iv, encrypted));
|
|
700
759
|
};
|
|
701
|
-
return buffer => wordArrayToBuffer(iv.clone().concat(CryptoJS.AES.encrypt(CryptoJS.lib.WordArray.create(buffer), key, options).ciphertext));
|
|
702
760
|
}
|
|
703
761
|
end() {
|
|
704
762
|
this.dictionary.end();
|
|
@@ -749,89 +807,97 @@ function getPermissionsR3(permissionObject = {}) {
|
|
|
749
807
|
return permissions;
|
|
750
808
|
}
|
|
751
809
|
function getUserPasswordR2(encryptionKey) {
|
|
752
|
-
return
|
|
810
|
+
return rc4(processPasswordR2R3R4(), encryptionKey);
|
|
753
811
|
}
|
|
754
812
|
function getUserPasswordR3R4(documentId, encryptionKey) {
|
|
755
|
-
const key = encryptionKey.
|
|
756
|
-
let cipher =
|
|
813
|
+
const key = encryptionKey.slice();
|
|
814
|
+
let cipher = md5Hash(utils.concatBytes(processPasswordR2R3R4(), new Uint8Array(documentId)));
|
|
757
815
|
for (let i = 0; i < 20; i++) {
|
|
758
|
-
const
|
|
759
|
-
for (let j = 0; j <
|
|
760
|
-
|
|
816
|
+
const xorKey = new Uint8Array(key.length);
|
|
817
|
+
for (let j = 0; j < key.length; j++) {
|
|
818
|
+
xorKey[j] = encryptionKey[j] ^ i;
|
|
761
819
|
}
|
|
762
|
-
cipher =
|
|
820
|
+
cipher = rc4(cipher, xorKey);
|
|
763
821
|
}
|
|
764
|
-
|
|
822
|
+
const result = new Uint8Array(32);
|
|
823
|
+
result.set(cipher);
|
|
824
|
+
return result;
|
|
765
825
|
}
|
|
766
826
|
function getOwnerPasswordR2R3R4(r, keyBits, paddedUserPassword, paddedOwnerPassword) {
|
|
767
827
|
let digest = paddedOwnerPassword;
|
|
768
828
|
let round = r >= 3 ? 51 : 1;
|
|
769
829
|
for (let i = 0; i < round; i++) {
|
|
770
|
-
digest =
|
|
830
|
+
digest = md5Hash(digest);
|
|
771
831
|
}
|
|
772
|
-
const
|
|
773
|
-
key
|
|
832
|
+
const keyLen = keyBits / 8;
|
|
833
|
+
let key = digest.slice(0, keyLen);
|
|
774
834
|
let cipher = paddedUserPassword;
|
|
775
835
|
round = r >= 3 ? 20 : 1;
|
|
776
836
|
for (let i = 0; i < round; i++) {
|
|
777
|
-
const
|
|
778
|
-
for (let j = 0; j <
|
|
779
|
-
|
|
837
|
+
const xorKey = new Uint8Array(keyLen);
|
|
838
|
+
for (let j = 0; j < keyLen; j++) {
|
|
839
|
+
xorKey[j] = key[j] ^ i;
|
|
780
840
|
}
|
|
781
|
-
cipher =
|
|
841
|
+
cipher = rc4(cipher, xorKey);
|
|
782
842
|
}
|
|
783
843
|
return cipher;
|
|
784
844
|
}
|
|
785
845
|
function getEncryptionKeyR2R3R4(r, keyBits, documentId, paddedUserPassword, ownerPasswordEntry, permissions) {
|
|
786
|
-
|
|
846
|
+
const permBytes = new Uint8Array([permissions & 0xff, permissions >> 8 & 0xff, permissions >> 16 & 0xff, permissions >> 24 & 0xff]);
|
|
847
|
+
let key = utils.concatBytes(paddedUserPassword, ownerPasswordEntry, permBytes, new Uint8Array(documentId));
|
|
787
848
|
const round = r >= 3 ? 51 : 1;
|
|
849
|
+
const keyLen = keyBits / 8;
|
|
788
850
|
for (let i = 0; i < round; i++) {
|
|
789
|
-
key =
|
|
790
|
-
key
|
|
851
|
+
key = md5Hash(key);
|
|
852
|
+
key = key.slice(0, keyLen);
|
|
791
853
|
}
|
|
792
854
|
return key;
|
|
793
855
|
}
|
|
794
856
|
function getUserPasswordR5(processedUserPassword, generateRandomWordArray) {
|
|
795
857
|
const validationSalt = generateRandomWordArray(8);
|
|
796
858
|
const keySalt = generateRandomWordArray(8);
|
|
797
|
-
|
|
859
|
+
const hash = sha256Hash(utils.concatBytes(processedUserPassword, validationSalt));
|
|
860
|
+
return utils.concatBytes(hash, validationSalt, keySalt);
|
|
798
861
|
}
|
|
799
862
|
function getUserEncryptionKeyR5(processedUserPassword, userKeySalt, encryptionKey) {
|
|
800
|
-
const key =
|
|
801
|
-
const
|
|
802
|
-
|
|
803
|
-
padding: CryptoJS.pad.NoPadding,
|
|
804
|
-
iv: CryptoJS.lib.WordArray.create(null, 16)
|
|
805
|
-
};
|
|
806
|
-
return CryptoJS.AES.encrypt(encryptionKey, key, options).ciphertext;
|
|
863
|
+
const key = sha256Hash(utils.concatBytes(processedUserPassword, userKeySalt));
|
|
864
|
+
const iv = new Uint8Array(16);
|
|
865
|
+
return aesCbcEncrypt(encryptionKey, key, iv, false);
|
|
807
866
|
}
|
|
808
867
|
function getOwnerPasswordR5(processedOwnerPassword, userPasswordEntry, generateRandomWordArray) {
|
|
809
868
|
const validationSalt = generateRandomWordArray(8);
|
|
810
869
|
const keySalt = generateRandomWordArray(8);
|
|
811
|
-
|
|
870
|
+
const hash = sha256Hash(utils.concatBytes(processedOwnerPassword, validationSalt, userPasswordEntry));
|
|
871
|
+
return utils.concatBytes(hash, validationSalt, keySalt);
|
|
812
872
|
}
|
|
813
873
|
function getOwnerEncryptionKeyR5(processedOwnerPassword, ownerKeySalt, userPasswordEntry, encryptionKey) {
|
|
814
|
-
const key =
|
|
815
|
-
const
|
|
816
|
-
|
|
817
|
-
padding: CryptoJS.pad.NoPadding,
|
|
818
|
-
iv: CryptoJS.lib.WordArray.create(null, 16)
|
|
819
|
-
};
|
|
820
|
-
return CryptoJS.AES.encrypt(encryptionKey, key, options).ciphertext;
|
|
874
|
+
const key = sha256Hash(utils.concatBytes(processedOwnerPassword, ownerKeySalt, userPasswordEntry));
|
|
875
|
+
const iv = new Uint8Array(16);
|
|
876
|
+
return aesCbcEncrypt(encryptionKey, key, iv, false);
|
|
821
877
|
}
|
|
822
878
|
function getEncryptionKeyR5(generateRandomWordArray) {
|
|
823
879
|
return generateRandomWordArray(32);
|
|
824
880
|
}
|
|
825
881
|
function getEncryptedPermissionsR5(permissions, encryptionKey, generateRandomWordArray) {
|
|
826
|
-
const
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
882
|
+
const data = new Uint8Array(16);
|
|
883
|
+
data[0] = permissions & 0xff;
|
|
884
|
+
data[1] = permissions >> 8 & 0xff;
|
|
885
|
+
data[2] = permissions >> 16 & 0xff;
|
|
886
|
+
data[3] = permissions >> 24 & 0xff;
|
|
887
|
+
data[4] = 0xff;
|
|
888
|
+
data[5] = 0xff;
|
|
889
|
+
data[6] = 0xff;
|
|
890
|
+
data[7] = 0xff;
|
|
891
|
+
data[8] = 0x54;
|
|
892
|
+
data[9] = 0x61;
|
|
893
|
+
data[10] = 0x64;
|
|
894
|
+
data[11] = 0x62;
|
|
895
|
+
const randomPart = generateRandomWordArray(4);
|
|
896
|
+
data.set(randomPart, 12);
|
|
897
|
+
return aesEcbEncrypt(data, encryptionKey);
|
|
832
898
|
}
|
|
833
899
|
function processPasswordR2R3R4(password = '') {
|
|
834
|
-
const out =
|
|
900
|
+
const out = new Uint8Array(32);
|
|
835
901
|
const length = password.length;
|
|
836
902
|
let index = 0;
|
|
837
903
|
while (index < length && index < 32) {
|
|
@@ -846,26 +912,16 @@ function processPasswordR2R3R4(password = '') {
|
|
|
846
912
|
out[index] = PASSWORD_PADDING[index - length];
|
|
847
913
|
index++;
|
|
848
914
|
}
|
|
849
|
-
return
|
|
915
|
+
return out;
|
|
850
916
|
}
|
|
851
917
|
function processPasswordR5(password = '') {
|
|
852
918
|
password = unescape(encodeURIComponent(saslprep(password)));
|
|
853
919
|
const length = Math.min(127, password.length);
|
|
854
|
-
const out =
|
|
920
|
+
const out = new Uint8Array(length);
|
|
855
921
|
for (let i = 0; i < length; i++) {
|
|
856
922
|
out[i] = password.charCodeAt(i);
|
|
857
923
|
}
|
|
858
|
-
return
|
|
859
|
-
}
|
|
860
|
-
function lsbFirstWord(data) {
|
|
861
|
-
return (data & 0xff) << 24 | (data & 0xff00) << 8 | data >> 8 & 0xff00 | data >> 24 & 0xff;
|
|
862
|
-
}
|
|
863
|
-
function wordArrayToBuffer(wordArray) {
|
|
864
|
-
const byteArray = [];
|
|
865
|
-
for (let i = 0; i < wordArray.sigBytes; i++) {
|
|
866
|
-
byteArray.push(wordArray.words[Math.floor(i / 4)] >> 8 * (3 - i % 4) & 0xff);
|
|
867
|
-
}
|
|
868
|
-
return Buffer.from(byteArray);
|
|
924
|
+
return out;
|
|
869
925
|
}
|
|
870
926
|
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];
|
|
871
927
|
|
|
@@ -1163,6 +1219,7 @@ var ColorMixin = {
|
|
|
1163
1219
|
this._opacityCount = 0;
|
|
1164
1220
|
this._patternCount = 0;
|
|
1165
1221
|
this._gradCount = 0;
|
|
1222
|
+
this._activeColorProfile = null;
|
|
1166
1223
|
},
|
|
1167
1224
|
_normalizeColor(color) {
|
|
1168
1225
|
if (typeof color === 'string') {
|
|
@@ -1182,7 +1239,7 @@ var ColorMixin = {
|
|
|
1182
1239
|
if (color.length === 3) {
|
|
1183
1240
|
color = color.map(part => part / 255);
|
|
1184
1241
|
} else if (color.length === 4) {
|
|
1185
|
-
color = color.map(part => part /
|
|
1242
|
+
color = color.map(part => part / 255);
|
|
1186
1243
|
}
|
|
1187
1244
|
return color;
|
|
1188
1245
|
}
|
|
@@ -1222,6 +1279,13 @@ var ColorMixin = {
|
|
|
1222
1279
|
if (color instanceof SpotColor) {
|
|
1223
1280
|
return color.id;
|
|
1224
1281
|
}
|
|
1282
|
+
if (this._activeColorProfile) {
|
|
1283
|
+
const profile = this._activeColorProfile;
|
|
1284
|
+
if (profile.channels !== color.length) {
|
|
1285
|
+
throw Error("Profile channels don't match color channels");
|
|
1286
|
+
}
|
|
1287
|
+
return profile.label;
|
|
1288
|
+
}
|
|
1225
1289
|
return color.length === 4 ? 'DeviceCMYK' : 'DeviceRGB';
|
|
1226
1290
|
},
|
|
1227
1291
|
fillColor(color, opacity) {
|
|
@@ -1251,6 +1315,18 @@ var ColorMixin = {
|
|
|
1251
1315
|
this._doOpacity(null, opacity);
|
|
1252
1316
|
return this;
|
|
1253
1317
|
},
|
|
1318
|
+
beingColorProfile(label) {
|
|
1319
|
+
const profile = this._colorProfiles[label];
|
|
1320
|
+
if (!profile) {
|
|
1321
|
+
throw Error('Invalid color space label, the profile should be set first');
|
|
1322
|
+
}
|
|
1323
|
+
this._activeColorProfile = profile;
|
|
1324
|
+
return this;
|
|
1325
|
+
},
|
|
1326
|
+
endColorProfile() {
|
|
1327
|
+
this._activeColorProfile = null;
|
|
1328
|
+
return this;
|
|
1329
|
+
},
|
|
1254
1330
|
_doOpacity(fillOpacity, strokeOpacity) {
|
|
1255
1331
|
let dictionary, name;
|
|
1256
1332
|
if (fillOpacity == null && strokeOpacity == null) {
|
|
@@ -1473,86 +1549,176 @@ const parameters = {
|
|
|
1473
1549
|
Z: 0,
|
|
1474
1550
|
z: 0
|
|
1475
1551
|
};
|
|
1552
|
+
const isCommand = function (c) {
|
|
1553
|
+
return c in parameters;
|
|
1554
|
+
};
|
|
1555
|
+
const isWsp = function (c) {
|
|
1556
|
+
const codePoint = c.codePointAt(0);
|
|
1557
|
+
return codePoint === 0x20 || codePoint === 0x9 || codePoint === 0xd || codePoint === 0xa;
|
|
1558
|
+
};
|
|
1559
|
+
const isDigit = function (c) {
|
|
1560
|
+
const codePoint = c.codePointAt(0);
|
|
1561
|
+
if (codePoint == null) {
|
|
1562
|
+
return false;
|
|
1563
|
+
}
|
|
1564
|
+
return 48 <= codePoint && codePoint <= 57;
|
|
1565
|
+
};
|
|
1566
|
+
const readNumber = function (string, cursor) {
|
|
1567
|
+
let i = cursor;
|
|
1568
|
+
let value = '';
|
|
1569
|
+
let state = 'none';
|
|
1570
|
+
for (; i < string.length; i += 1) {
|
|
1571
|
+
const c = string[i];
|
|
1572
|
+
if (c === '+' || c === '-') {
|
|
1573
|
+
if (state === 'none') {
|
|
1574
|
+
state = 'sign';
|
|
1575
|
+
value += c;
|
|
1576
|
+
continue;
|
|
1577
|
+
}
|
|
1578
|
+
if (state === 'e') {
|
|
1579
|
+
state = 'exponent_sign';
|
|
1580
|
+
value += c;
|
|
1581
|
+
continue;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
if (isDigit(c)) {
|
|
1585
|
+
if (state === 'none' || state === 'sign' || state === 'whole') {
|
|
1586
|
+
state = 'whole';
|
|
1587
|
+
value += c;
|
|
1588
|
+
continue;
|
|
1589
|
+
}
|
|
1590
|
+
if (state === 'decimal_point' || state === 'decimal') {
|
|
1591
|
+
state = 'decimal';
|
|
1592
|
+
value += c;
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
if (state === 'e' || state === 'exponent_sign' || state === 'exponent') {
|
|
1596
|
+
state = 'exponent';
|
|
1597
|
+
value += c;
|
|
1598
|
+
continue;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
if (c === '.') {
|
|
1602
|
+
if (state === 'none' || state === 'sign' || state === 'whole') {
|
|
1603
|
+
state = 'decimal_point';
|
|
1604
|
+
value += c;
|
|
1605
|
+
continue;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
if (c === 'E' || c === 'e') {
|
|
1609
|
+
if (state === 'whole' || state === 'decimal_point' || state === 'decimal') {
|
|
1610
|
+
state = 'e';
|
|
1611
|
+
value += c;
|
|
1612
|
+
continue;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
break;
|
|
1616
|
+
}
|
|
1617
|
+
const number = Number.parseFloat(value);
|
|
1618
|
+
if (Number.isNaN(number)) {
|
|
1619
|
+
return [cursor, null];
|
|
1620
|
+
}
|
|
1621
|
+
return [i - 1, number];
|
|
1622
|
+
};
|
|
1476
1623
|
const parse = function (path) {
|
|
1477
|
-
|
|
1478
|
-
|
|
1624
|
+
const pathData = [];
|
|
1625
|
+
let command = null;
|
|
1479
1626
|
let args = [];
|
|
1480
|
-
let
|
|
1481
|
-
let
|
|
1482
|
-
let
|
|
1483
|
-
for (let
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1627
|
+
let argsCount = 0;
|
|
1628
|
+
let canHaveComma = false;
|
|
1629
|
+
let hadComma = false;
|
|
1630
|
+
for (let i = 0; i < path.length; i += 1) {
|
|
1631
|
+
const c = path.charAt(i);
|
|
1632
|
+
if (isWsp(c)) {
|
|
1633
|
+
continue;
|
|
1634
|
+
}
|
|
1635
|
+
if (canHaveComma && c === ',') {
|
|
1636
|
+
if (hadComma) {
|
|
1637
|
+
break;
|
|
1638
|
+
}
|
|
1639
|
+
hadComma = true;
|
|
1640
|
+
continue;
|
|
1641
|
+
}
|
|
1642
|
+
if (isCommand(c)) {
|
|
1643
|
+
if (hadComma) {
|
|
1644
|
+
return pathData;
|
|
1645
|
+
}
|
|
1646
|
+
if (command == null) {
|
|
1647
|
+
if (c !== 'M' && c !== 'm') {
|
|
1648
|
+
return pathData;
|
|
1649
|
+
}
|
|
1650
|
+
} else {
|
|
1651
|
+
if (args.length !== 0) {
|
|
1652
|
+
return pathData;
|
|
1489
1653
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1654
|
+
}
|
|
1655
|
+
command = c;
|
|
1656
|
+
args = [];
|
|
1657
|
+
argsCount = parameters[command];
|
|
1658
|
+
canHaveComma = false;
|
|
1659
|
+
if (argsCount === 0) {
|
|
1660
|
+
pathData.push({
|
|
1661
|
+
command,
|
|
1492
1662
|
args
|
|
1493
|
-
};
|
|
1494
|
-
args = [];
|
|
1495
|
-
curArg = '';
|
|
1496
|
-
foundDecimal = false;
|
|
1663
|
+
});
|
|
1497
1664
|
}
|
|
1498
|
-
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1665
|
+
continue;
|
|
1666
|
+
}
|
|
1667
|
+
if (command == null) {
|
|
1668
|
+
return pathData;
|
|
1669
|
+
}
|
|
1670
|
+
let newCursor = i;
|
|
1671
|
+
let number = null;
|
|
1672
|
+
if (command === 'A' || command === 'a') {
|
|
1673
|
+
const position = args.length;
|
|
1674
|
+
if (position === 0 || position === 1) {
|
|
1675
|
+
if (c !== '+' && c !== '-') {
|
|
1676
|
+
[newCursor, number] = readNumber(path, i);
|
|
1677
|
+
}
|
|
1502
1678
|
}
|
|
1503
|
-
if (
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
if (cmd === 'M') {
|
|
1510
|
-
cmd = 'L';
|
|
1679
|
+
if (position === 2 || position === 5 || position === 6) {
|
|
1680
|
+
[newCursor, number] = readNumber(path, i);
|
|
1681
|
+
}
|
|
1682
|
+
if (position === 3 || position === 4) {
|
|
1683
|
+
if (c === '0') {
|
|
1684
|
+
number = 0;
|
|
1511
1685
|
}
|
|
1512
|
-
if (
|
|
1513
|
-
|
|
1686
|
+
if (c === '1') {
|
|
1687
|
+
number = 1;
|
|
1514
1688
|
}
|
|
1515
|
-
} else {
|
|
1516
|
-
args[args.length] = +curArg;
|
|
1517
1689
|
}
|
|
1518
|
-
foundDecimal = c === '.';
|
|
1519
|
-
curArg = ['-', '.'].includes(c) ? c : '';
|
|
1520
1690
|
} else {
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1691
|
+
[newCursor, number] = readNumber(path, i);
|
|
1692
|
+
}
|
|
1693
|
+
if (number == null) {
|
|
1694
|
+
return pathData;
|
|
1695
|
+
}
|
|
1696
|
+
args.push(number);
|
|
1697
|
+
canHaveComma = true;
|
|
1698
|
+
hadComma = false;
|
|
1699
|
+
i = newCursor;
|
|
1700
|
+
if (args.length === argsCount) {
|
|
1701
|
+
pathData.push({
|
|
1702
|
+
command,
|
|
1531
1703
|
args
|
|
1532
|
-
};
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
cmd = 'L';
|
|
1704
|
+
});
|
|
1705
|
+
if (command === 'M') {
|
|
1706
|
+
command = 'L';
|
|
1536
1707
|
}
|
|
1537
|
-
if (
|
|
1538
|
-
|
|
1708
|
+
if (command === 'm') {
|
|
1709
|
+
command = 'l';
|
|
1539
1710
|
}
|
|
1540
|
-
|
|
1541
|
-
args[args.length] = +curArg;
|
|
1711
|
+
args = [];
|
|
1542
1712
|
}
|
|
1543
1713
|
}
|
|
1544
|
-
|
|
1545
|
-
cmd,
|
|
1546
|
-
args
|
|
1547
|
-
};
|
|
1548
|
-
return ret;
|
|
1714
|
+
return pathData;
|
|
1549
1715
|
};
|
|
1550
1716
|
const apply = function (commands, doc) {
|
|
1551
1717
|
cx = cy = px = py = sx = sy = 0;
|
|
1552
1718
|
for (let i = 0; i < commands.length; i++) {
|
|
1553
1719
|
const c = commands[i];
|
|
1554
|
-
if (typeof runners[c.
|
|
1555
|
-
runners[c.
|
|
1720
|
+
if (typeof runners[c.command] === 'function') {
|
|
1721
|
+
runners[c.command](doc, c.args);
|
|
1556
1722
|
}
|
|
1557
1723
|
}
|
|
1558
1724
|
};
|
|
@@ -2584,7 +2750,7 @@ begincmap
|
|
|
2584
2750
|
1 begincodespacerange
|
|
2585
2751
|
<0000><ffff>
|
|
2586
2752
|
endcodespacerange
|
|
2587
|
-
|
|
2753
|
+
${ranges.length} beginbfrange
|
|
2588
2754
|
${ranges.join('\n')}
|
|
2589
2755
|
endbfrange
|
|
2590
2756
|
endcmap
|
|
@@ -2763,6 +2929,7 @@ class LineWrapper extends events.EventEmitter {
|
|
|
2763
2929
|
this.document = document;
|
|
2764
2930
|
this.horizontalScaling = options.horizontalScaling || 100;
|
|
2765
2931
|
this.indent = (options.indent || 0) * this.horizontalScaling / 100;
|
|
2932
|
+
this.indentAllLines = options.indentAllLines || false;
|
|
2766
2933
|
this.characterSpacing = (options.characterSpacing || 0) * this.horizontalScaling / 100;
|
|
2767
2934
|
this.wordSpacing = (options.wordSpacing === 0) * this.horizontalScaling / 100;
|
|
2768
2935
|
this.columns = options.columns || 1;
|
|
@@ -3004,7 +3171,13 @@ class LineWrapper extends events.EventEmitter {
|
|
|
3004
3171
|
this.column = 1;
|
|
3005
3172
|
this.startY = this.document.page.margins.top;
|
|
3006
3173
|
this.maxY = this.document.page.maxY();
|
|
3007
|
-
this.
|
|
3174
|
+
if (this.indentAllLines) {
|
|
3175
|
+
const indent = this.continuedX || this.indent;
|
|
3176
|
+
this.document.x += indent;
|
|
3177
|
+
this.lineWidth -= indent;
|
|
3178
|
+
} else {
|
|
3179
|
+
this.document.x = this.startX;
|
|
3180
|
+
}
|
|
3008
3181
|
if (this.document._fillColor) {
|
|
3009
3182
|
this.document.fillColor(...this.document._fillColor);
|
|
3010
3183
|
}
|
|
@@ -3408,7 +3581,11 @@ var TextMixin = {
|
|
|
3408
3581
|
}
|
|
3409
3582
|
const renderedWidth = options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1);
|
|
3410
3583
|
if (options.link != null) {
|
|
3411
|
-
|
|
3584
|
+
const linkOptions = {};
|
|
3585
|
+
if (this._currentStructureElement && this._currentStructureElement.dictionary.data.S === 'Link') {
|
|
3586
|
+
linkOptions.structParent = this._currentStructureElement;
|
|
3587
|
+
}
|
|
3588
|
+
this.link(x, y, renderedWidth, this.currentLineHeight(), options.link, linkOptions);
|
|
3412
3589
|
}
|
|
3413
3590
|
if (options.goTo != null) {
|
|
3414
3591
|
this.goTo(x, y, renderedWidth, this.currentLineHeight(), options.goTo);
|
|
@@ -3537,6 +3714,47 @@ var TextMixin = {
|
|
|
3537
3714
|
}
|
|
3538
3715
|
};
|
|
3539
3716
|
|
|
3717
|
+
const parseExifOrientation = data => {
|
|
3718
|
+
if (!data || data.length < 20) return null;
|
|
3719
|
+
let pos = 2;
|
|
3720
|
+
while (pos < data.length - 4) {
|
|
3721
|
+
while (pos < data.length && data[pos] !== 0xff) pos++;
|
|
3722
|
+
if (pos >= data.length - 4) return null;
|
|
3723
|
+
const marker = data.readUInt16BE(pos);
|
|
3724
|
+
pos += 2;
|
|
3725
|
+
if (marker === 0xffda) return null;
|
|
3726
|
+
if (marker >= 0xffd0 && marker <= 0xffd9 || marker === 0xff01) continue;
|
|
3727
|
+
if (pos + 2 > data.length) return null;
|
|
3728
|
+
const segmentLength = data.readUInt16BE(pos);
|
|
3729
|
+
if (marker === 0xffe1 && pos + 8 <= data.length) {
|
|
3730
|
+
const exifHeader = data.subarray(pos + 2, pos + 8).toString('binary');
|
|
3731
|
+
if (exifHeader === 'Exif\x00\x00') {
|
|
3732
|
+
const tiffStart = pos + 8;
|
|
3733
|
+
if (tiffStart + 8 > data.length) return null;
|
|
3734
|
+
const byteOrder = data.subarray(tiffStart, tiffStart + 2).toString('ascii');
|
|
3735
|
+
const isLittleEndian = byteOrder === 'II';
|
|
3736
|
+
if (!isLittleEndian && byteOrder !== 'MM') return null;
|
|
3737
|
+
const read16 = isLittleEndian ? o => data.readUInt16LE(o) : o => data.readUInt16BE(o);
|
|
3738
|
+
const read32 = isLittleEndian ? o => data.readUInt32LE(o) : o => data.readUInt32BE(o);
|
|
3739
|
+
if (read16(tiffStart + 2) !== 42) return null;
|
|
3740
|
+
const ifdPos = tiffStart + read32(tiffStart + 4);
|
|
3741
|
+
if (ifdPos + 2 > data.length) return null;
|
|
3742
|
+
const entryCount = read16(ifdPos);
|
|
3743
|
+
for (let i = 0; i < entryCount; i++) {
|
|
3744
|
+
const entryPos = ifdPos + 2 + i * 12;
|
|
3745
|
+
if (entryPos + 12 > data.length) return null;
|
|
3746
|
+
if (read16(entryPos) === 0x0112) {
|
|
3747
|
+
const value = read16(entryPos + 8);
|
|
3748
|
+
return value >= 1 && value <= 8 ? value : null;
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
return null;
|
|
3752
|
+
}
|
|
3753
|
+
}
|
|
3754
|
+
pos += segmentLength;
|
|
3755
|
+
}
|
|
3756
|
+
return null;
|
|
3757
|
+
};
|
|
3540
3758
|
const MARKERS = [0xffc0, 0xffc1, 0xffc2, 0xffc3, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9, 0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf];
|
|
3541
3759
|
const COLOR_SPACE_MAP = {
|
|
3542
3760
|
1: 'DeviceGray',
|
|
@@ -3551,9 +3769,11 @@ class JPEG {
|
|
|
3551
3769
|
if (this.data.readUInt16BE(0) !== 0xffd8) {
|
|
3552
3770
|
throw 'SOI not found in JPEG';
|
|
3553
3771
|
}
|
|
3554
|
-
this.orientation =
|
|
3772
|
+
this.orientation = parseExifOrientation(this.data) || 1;
|
|
3555
3773
|
let pos = 2;
|
|
3556
3774
|
while (pos < this.data.length) {
|
|
3775
|
+
while (pos < this.data.length && this.data[pos] !== 0xff) pos++;
|
|
3776
|
+
if (pos >= this.data.length) break;
|
|
3557
3777
|
marker = this.data.readUInt16BE(pos);
|
|
3558
3778
|
pos += 2;
|
|
3559
3779
|
if (MARKERS.includes(marker)) {
|
|
@@ -3705,12 +3925,16 @@ class PNGImage {
|
|
|
3705
3925
|
}
|
|
3706
3926
|
loadIndexedAlphaChannel() {
|
|
3707
3927
|
const transparency = this.image.transparency.indexed;
|
|
3928
|
+
const isInterlaced = this.image.interlaceMethod === 1;
|
|
3708
3929
|
return this.image.decodePixels(pixels => {
|
|
3709
3930
|
const alphaChannel = Buffer.alloc(this.width * this.height);
|
|
3710
3931
|
let i = 0;
|
|
3711
3932
|
for (let j = 0, end = pixels.length; j < end; j++) {
|
|
3712
3933
|
alphaChannel[i++] = transparency[pixels[j]];
|
|
3713
3934
|
}
|
|
3935
|
+
if (isInterlaced) {
|
|
3936
|
+
this.imgData = zlib.deflateSync(Buffer.from(pixels));
|
|
3937
|
+
}
|
|
3714
3938
|
this.alphaChannel = zlib.deflateSync(alphaChannel);
|
|
3715
3939
|
return this.finalize();
|
|
3716
3940
|
});
|
|
@@ -3732,7 +3956,7 @@ class PDFImage {
|
|
|
3732
3956
|
data = Buffer.from(new Uint8Array(src));
|
|
3733
3957
|
} else {
|
|
3734
3958
|
const split = src.split(',');
|
|
3735
|
-
if (split[0].startsWith('data:') && split[0].endsWith(';base64
|
|
3959
|
+
if (split[0].startsWith('data:') && split[0].endsWith(';base64')) {
|
|
3736
3960
|
if (split.length === 1) {
|
|
3737
3961
|
throw Error('Empty base64');
|
|
3738
3962
|
}
|
|
@@ -3942,6 +4166,12 @@ var ImagesMixin = {
|
|
|
3942
4166
|
}
|
|
3943
4167
|
};
|
|
3944
4168
|
|
|
4169
|
+
class PDFAnnotationReference {
|
|
4170
|
+
constructor(annotationRef) {
|
|
4171
|
+
this.annotationRef = annotationRef;
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
|
|
3945
4175
|
var AnnotationsMixin = {
|
|
3946
4176
|
annotate(x, y, w, h, options) {
|
|
3947
4177
|
options.Type = 'Annot';
|
|
@@ -3959,12 +4189,18 @@ var AnnotationsMixin = {
|
|
|
3959
4189
|
if (typeof options.Dest === 'string') {
|
|
3960
4190
|
options.Dest = new String(options.Dest);
|
|
3961
4191
|
}
|
|
4192
|
+
const structParent = options.structParent;
|
|
4193
|
+
delete options.structParent;
|
|
3962
4194
|
for (let key in options) {
|
|
3963
4195
|
const val = options[key];
|
|
3964
4196
|
options[key[0].toUpperCase() + key.slice(1)] = val;
|
|
3965
4197
|
}
|
|
3966
4198
|
const ref = this.ref(options);
|
|
3967
4199
|
this.page.annotations.push(ref);
|
|
4200
|
+
if (structParent && typeof structParent.add === 'function') {
|
|
4201
|
+
const annotRef = new PDFAnnotationReference(ref);
|
|
4202
|
+
structParent.add(annotRef);
|
|
4203
|
+
}
|
|
3968
4204
|
ref.end();
|
|
3969
4205
|
return this;
|
|
3970
4206
|
},
|
|
@@ -4008,6 +4244,9 @@ var AnnotationsMixin = {
|
|
|
4008
4244
|
});
|
|
4009
4245
|
options.A.end();
|
|
4010
4246
|
}
|
|
4247
|
+
if (options.structParent && !options.Contents) {
|
|
4248
|
+
options.Contents = new String('');
|
|
4249
|
+
}
|
|
4011
4250
|
return this.annotate(x, y, w, h, options);
|
|
4012
4251
|
},
|
|
4013
4252
|
_markup(x, y, w, h, options = {}) {
|
|
@@ -4079,15 +4318,26 @@ var AnnotationsMixin = {
|
|
|
4079
4318
|
}
|
|
4080
4319
|
};
|
|
4081
4320
|
|
|
4321
|
+
const DEFAULT_OPTIONS = {
|
|
4322
|
+
top: 0,
|
|
4323
|
+
left: 0,
|
|
4324
|
+
zoom: 0,
|
|
4325
|
+
fit: true,
|
|
4326
|
+
pageNumber: null,
|
|
4327
|
+
expanded: false
|
|
4328
|
+
};
|
|
4082
4329
|
class PDFOutline {
|
|
4083
|
-
constructor(document, parent, title, dest, options = {
|
|
4084
|
-
expanded: false
|
|
4085
|
-
}) {
|
|
4330
|
+
constructor(document, parent, title, dest, options = DEFAULT_OPTIONS) {
|
|
4086
4331
|
this.document = document;
|
|
4087
4332
|
this.options = options;
|
|
4088
4333
|
this.outlineData = {};
|
|
4089
4334
|
if (dest !== null) {
|
|
4090
|
-
|
|
4335
|
+
const destWidth = dest.data.MediaBox[2];
|
|
4336
|
+
const destHeight = dest.data.MediaBox[3];
|
|
4337
|
+
const top = destHeight - (options.top || 0);
|
|
4338
|
+
const left = destWidth - (options.left || 0);
|
|
4339
|
+
const zoom = options.zoom || 0;
|
|
4340
|
+
this.outlineData['Dest'] = options.fit ? [dest, 'Fit'] : [dest, 'XYZ', left, top, zoom];
|
|
4091
4341
|
}
|
|
4092
4342
|
if (parent !== null) {
|
|
4093
4343
|
this.outlineData['Parent'] = parent;
|
|
@@ -4098,10 +4348,10 @@ class PDFOutline {
|
|
|
4098
4348
|
this.dictionary = this.document.ref(this.outlineData);
|
|
4099
4349
|
this.children = [];
|
|
4100
4350
|
}
|
|
4101
|
-
addItem(title, options = {
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
const result = new PDFOutline(this.document, this.dictionary, title,
|
|
4351
|
+
addItem(title, options = DEFAULT_OPTIONS) {
|
|
4352
|
+
const pages = this.document._root.data.Pages.data.Kids;
|
|
4353
|
+
const dest = options.pageNumber != null ? pages[options.pageNumber] : this.document.page.dictionary;
|
|
4354
|
+
const result = new PDFOutline(this.document, this.dictionary, title, dest, options);
|
|
4105
4355
|
this.children.push(result);
|
|
4106
4356
|
return result;
|
|
4107
4357
|
}
|
|
@@ -4137,7 +4387,7 @@ var OutlineMixin = {
|
|
|
4137
4387
|
this.outline.endOutline();
|
|
4138
4388
|
if (this.outline.children.length > 0) {
|
|
4139
4389
|
this._root.data.Outlines = this.outline.dictionary;
|
|
4140
|
-
return this._root.data.PageMode = 'UseOutlines';
|
|
4390
|
+
return this._root.data.PageMode = this._root.data.PageMode || 'UseOutlines';
|
|
4141
4391
|
}
|
|
4142
4392
|
}
|
|
4143
4393
|
};
|
|
@@ -4208,6 +4458,9 @@ class PDFStructureElement {
|
|
|
4208
4458
|
if (child instanceof PDFStructureContent) {
|
|
4209
4459
|
this._addContentToParentTree(child);
|
|
4210
4460
|
}
|
|
4461
|
+
if (child instanceof PDFAnnotationReference) {
|
|
4462
|
+
this._addAnnotationToParentTree(child.annotationRef);
|
|
4463
|
+
}
|
|
4211
4464
|
if (typeof child === 'function' && this._attached) {
|
|
4212
4465
|
child = this._contentForClosure(child);
|
|
4213
4466
|
}
|
|
@@ -4223,6 +4476,12 @@ class PDFStructureElement {
|
|
|
4223
4476
|
pageStructParents[mcid] = this.dictionary;
|
|
4224
4477
|
});
|
|
4225
4478
|
}
|
|
4479
|
+
_addAnnotationToParentTree(annotRef) {
|
|
4480
|
+
const parentTreeKey = this.document.createStructParentTreeNextKey();
|
|
4481
|
+
annotRef.data.StructParent = parentTreeKey;
|
|
4482
|
+
const parentTree = this.document.getStructParentTree();
|
|
4483
|
+
parentTree.add(parentTreeKey, this.dictionary);
|
|
4484
|
+
}
|
|
4226
4485
|
setParent(parentRef) {
|
|
4227
4486
|
if (this.dictionary.data.P) {
|
|
4228
4487
|
throw new Error(`Structure element added to more than one parent`);
|
|
@@ -4254,11 +4513,17 @@ class PDFStructureElement {
|
|
|
4254
4513
|
this._flush();
|
|
4255
4514
|
}
|
|
4256
4515
|
_isValidChild(child) {
|
|
4257
|
-
return child instanceof PDFStructureElement || child instanceof PDFStructureContent || typeof child === 'function';
|
|
4516
|
+
return child instanceof PDFStructureElement || child instanceof PDFStructureContent || child instanceof PDFAnnotationReference || typeof child === 'function';
|
|
4258
4517
|
}
|
|
4259
4518
|
_contentForClosure(closure) {
|
|
4260
4519
|
const content = this.document.markStructureContent(this.dictionary.data.S);
|
|
4520
|
+
const prevStructElement = this.document._currentStructureElement;
|
|
4521
|
+
this.document._currentStructureElement = this;
|
|
4522
|
+
const wasEnded = this._ended;
|
|
4523
|
+
this._ended = false;
|
|
4261
4524
|
closure();
|
|
4525
|
+
this._ended = wasEnded;
|
|
4526
|
+
this.document._currentStructureElement = prevStructElement;
|
|
4262
4527
|
this.document.endMarkedContent();
|
|
4263
4528
|
this._addContentToParentTree(content);
|
|
4264
4529
|
return content;
|
|
@@ -4311,6 +4576,15 @@ class PDFStructureElement {
|
|
|
4311
4576
|
}
|
|
4312
4577
|
});
|
|
4313
4578
|
}
|
|
4579
|
+
if (child instanceof PDFAnnotationReference) {
|
|
4580
|
+
const pageRef = this.document.page.dictionary;
|
|
4581
|
+
const objr = {
|
|
4582
|
+
Type: 'OBJR',
|
|
4583
|
+
Obj: child.annotationRef,
|
|
4584
|
+
Pg: pageRef
|
|
4585
|
+
};
|
|
4586
|
+
this.dictionary.data.K.push(objr);
|
|
4587
|
+
}
|
|
4314
4588
|
}
|
|
4315
4589
|
}
|
|
4316
4590
|
|
|
@@ -4404,6 +4678,13 @@ var MarkingsMixin = {
|
|
|
4404
4678
|
endMarkedContent() {
|
|
4405
4679
|
this.page.markings.pop();
|
|
4406
4680
|
this.addContent('EMC');
|
|
4681
|
+
if (this._textOptions) {
|
|
4682
|
+
delete this._textOptions.link;
|
|
4683
|
+
delete this._textOptions.goTo;
|
|
4684
|
+
delete this._textOptions.destination;
|
|
4685
|
+
delete this._textOptions.underline;
|
|
4686
|
+
delete this._textOptions.strike;
|
|
4687
|
+
}
|
|
4407
4688
|
return this;
|
|
4408
4689
|
},
|
|
4409
4690
|
struct(type, options = {}, children = null) {
|
|
@@ -4850,7 +5131,7 @@ var AttachmentsMixin = {
|
|
|
4850
5131
|
if (options.type) {
|
|
4851
5132
|
refBody.Subtype = options.type.replace('/', '#2F');
|
|
4852
5133
|
}
|
|
4853
|
-
const checksum =
|
|
5134
|
+
const checksum = md5Hex(new Uint8Array(data));
|
|
4854
5135
|
refBody.Params.CheckSum = new String(checksum);
|
|
4855
5136
|
refBody.Params.Size = data.byteLength;
|
|
4856
5137
|
let ref;
|
|
@@ -4910,6 +5191,9 @@ var PDFA = {
|
|
|
4910
5191
|
this._addColorOutputIntent();
|
|
4911
5192
|
},
|
|
4912
5193
|
_addColorOutputIntent() {
|
|
5194
|
+
if (this._root.data.OutputIntents && this._root.data.OutputIntents.length !== 0) {
|
|
5195
|
+
return;
|
|
5196
|
+
}
|
|
4913
5197
|
const iccProfile = fs.readFileSync(`${__dirname}/data/sRGB_IEC61966_2_1.icc`);
|
|
4914
5198
|
const colorProfileRef = this.ref({
|
|
4915
5199
|
Length: iccProfile.length,
|
|
@@ -5493,7 +5777,7 @@ function renderRow(row, rowIndex) {
|
|
|
5493
5777
|
function renderCell(cell, rowStruct) {
|
|
5494
5778
|
const cellRenderer = () => {
|
|
5495
5779
|
if (cell.backgroundColor != null) {
|
|
5496
|
-
this.document.save().rect(cell.x, cell.y, cell.width, cell.height).fill(
|
|
5780
|
+
this.document.save().fillColor(cell.backgroundColor).rect(cell.x, cell.y, cell.width, cell.height).fill().restore();
|
|
5497
5781
|
}
|
|
5498
5782
|
renderBorder.call(this, cell.border, cell.borderColor, cell.x, cell.y, cell.width, cell.height);
|
|
5499
5783
|
if (cell.debug) {
|
|
@@ -5554,20 +5838,20 @@ function renderBorder(border, borderColor, x, y, width, height, mask) {
|
|
|
5554
5838
|
const doc = this.document;
|
|
5555
5839
|
if ([border.right, border.bottom, border.left].every(val => val === border.top)) {
|
|
5556
5840
|
if (border.top > 0) {
|
|
5557
|
-
doc.save().lineWidth(border.top).rect(x, y, width, height).stroke(
|
|
5841
|
+
doc.save().lineWidth(border.top).strokeColor(borderColor.top).rect(x, y, width, height).stroke().restore();
|
|
5558
5842
|
}
|
|
5559
5843
|
} else {
|
|
5560
5844
|
if (border.top > 0) {
|
|
5561
|
-
doc.save().lineWidth(border.top).moveTo(x, y).lineTo(x + width, y).stroke(
|
|
5845
|
+
doc.save().lineWidth(border.top).moveTo(x, y).strokeColor(borderColor.top).lineTo(x + width, y).stroke().restore();
|
|
5562
5846
|
}
|
|
5563
5847
|
if (border.right > 0) {
|
|
5564
|
-
doc.save().lineWidth(border.right).moveTo(x + width, y).lineTo(x + width, y + height).stroke(
|
|
5848
|
+
doc.save().lineWidth(border.right).moveTo(x + width, y).strokeColor(borderColor.right).lineTo(x + width, y + height).stroke().restore();
|
|
5565
5849
|
}
|
|
5566
5850
|
if (border.bottom > 0) {
|
|
5567
|
-
doc.save().lineWidth(border.bottom).moveTo(x + width, y + height).lineTo(x, y + height).stroke(
|
|
5851
|
+
doc.save().lineWidth(border.bottom).moveTo(x + width, y + height).strokeColor(borderColor.bottom).lineTo(x, y + height).stroke().restore();
|
|
5568
5852
|
}
|
|
5569
5853
|
if (border.left > 0) {
|
|
5570
|
-
doc.save().lineWidth(border.left).moveTo(x, y + height).lineTo(x, y).stroke(
|
|
5854
|
+
doc.save().lineWidth(border.left).moveTo(x, y + height).strokeColor(borderColor.left).lineTo(x, y).stroke().restore();
|
|
5571
5855
|
}
|
|
5572
5856
|
}
|
|
5573
5857
|
}
|
|
@@ -5729,6 +6013,73 @@ var MetadataMixin = {
|
|
|
5729
6013
|
}
|
|
5730
6014
|
};
|
|
5731
6015
|
|
|
6016
|
+
class ICCProfile {
|
|
6017
|
+
constructor(label, data, channels, alternate) {
|
|
6018
|
+
this.label = label;
|
|
6019
|
+
this.data = data;
|
|
6020
|
+
this.channels = channels;
|
|
6021
|
+
this.alternate = alternate;
|
|
6022
|
+
this.ref = null;
|
|
6023
|
+
this.streamRef = null;
|
|
6024
|
+
}
|
|
6025
|
+
embed(document) {
|
|
6026
|
+
if (this.ref) {
|
|
6027
|
+
return;
|
|
6028
|
+
}
|
|
6029
|
+
this.document = document;
|
|
6030
|
+
this.streamRef = this.document.ref({
|
|
6031
|
+
Alternate: this.alternate,
|
|
6032
|
+
N: this.channels,
|
|
6033
|
+
Length: this.data.length
|
|
6034
|
+
});
|
|
6035
|
+
this.streamRef.write(this.data);
|
|
6036
|
+
this.streamRef.end();
|
|
6037
|
+
this.ref = this.document.ref([`ICCBased ${this.streamRef}`]);
|
|
6038
|
+
this.ref.end();
|
|
6039
|
+
this.data = null;
|
|
6040
|
+
}
|
|
6041
|
+
}
|
|
6042
|
+
|
|
6043
|
+
var ColorSpaceMixin = {
|
|
6044
|
+
initColorSpace() {
|
|
6045
|
+
this._colorProfiles = {};
|
|
6046
|
+
},
|
|
6047
|
+
iccProfile(label, data, channels, alternate) {
|
|
6048
|
+
const profile = new ICCProfile(label, data, channels, alternate);
|
|
6049
|
+
profile.embed(this);
|
|
6050
|
+
this._colorProfiles[label] = profile;
|
|
6051
|
+
return this;
|
|
6052
|
+
},
|
|
6053
|
+
_writeSpaceToResources(resources) {
|
|
6054
|
+
resources.data.ColorSpace = resources.data.ColorSpace || {};
|
|
6055
|
+
Object.entries(this._colorProfiles).forEach(([k, v]) => {
|
|
6056
|
+
resources.data.ColorSpace[k] = v.ref;
|
|
6057
|
+
});
|
|
6058
|
+
}
|
|
6059
|
+
};
|
|
6060
|
+
|
|
6061
|
+
var OutputIntent = {
|
|
6062
|
+
initOutputIntent() {
|
|
6063
|
+
this._root.data.OutputIntents = this._root.data.OutputIntents || [];
|
|
6064
|
+
},
|
|
6065
|
+
outputIntent(type, s, info, outputConditionIdentifier, destOutputProfileLabel) {
|
|
6066
|
+
const profile = this._colorProfiles[destOutputProfileLabel];
|
|
6067
|
+
if (profile) {
|
|
6068
|
+
throw Error('Invalid color profile label, the profile should be set first');
|
|
6069
|
+
}
|
|
6070
|
+
const intentRef = this.ref({
|
|
6071
|
+
Type: type,
|
|
6072
|
+
S: s,
|
|
6073
|
+
Info: info,
|
|
6074
|
+
OutputConditionIdentifier: outputConditionIdentifier,
|
|
6075
|
+
DestOutputProfile: profile.ref
|
|
6076
|
+
});
|
|
6077
|
+
intentRef.end();
|
|
6078
|
+
this._root.data.OutputIntents.push(intentRef);
|
|
6079
|
+
return this;
|
|
6080
|
+
}
|
|
6081
|
+
};
|
|
6082
|
+
|
|
5732
6083
|
class PDFDocument extends stream.Readable {
|
|
5733
6084
|
constructor(options = {}) {
|
|
5734
6085
|
super(options);
|
|
@@ -5774,8 +6125,13 @@ class PDFDocument extends stream.Readable {
|
|
|
5774
6125
|
if (this.options.lang) {
|
|
5775
6126
|
this._root.data.Lang = new String(this.options.lang);
|
|
5776
6127
|
}
|
|
6128
|
+
if (this.options.pageLayout) {
|
|
6129
|
+
const layout = this.options.pageLayout;
|
|
6130
|
+
this._root.data.PageLayout = layout.charAt(0).toUpperCase() + layout.slice(1);
|
|
6131
|
+
}
|
|
5777
6132
|
this.page = null;
|
|
5778
6133
|
this.initMetadata();
|
|
6134
|
+
this.initColorSpace();
|
|
5779
6135
|
this.initColor();
|
|
5780
6136
|
this.initVector();
|
|
5781
6137
|
this.initFonts(options.font);
|
|
@@ -5785,6 +6141,7 @@ class PDFDocument extends stream.Readable {
|
|
|
5785
6141
|
this.initMarkings(options);
|
|
5786
6142
|
this.initTables();
|
|
5787
6143
|
this.initSubset(options);
|
|
6144
|
+
this.initOutputIntent();
|
|
5788
6145
|
this.info = {
|
|
5789
6146
|
Producer: 'PDFKit',
|
|
5790
6147
|
Creator: 'PDFKit',
|
|
@@ -5983,6 +6340,7 @@ const mixin = methods => {
|
|
|
5983
6340
|
Object.assign(PDFDocument.prototype, methods);
|
|
5984
6341
|
};
|
|
5985
6342
|
mixin(MetadataMixin);
|
|
6343
|
+
mixin(ColorSpaceMixin);
|
|
5986
6344
|
mixin(ColorMixin);
|
|
5987
6345
|
mixin(VectorMixin);
|
|
5988
6346
|
mixin(FontsMixin);
|
|
@@ -5995,6 +6353,7 @@ mixin(AcroFormMixin);
|
|
|
5995
6353
|
mixin(AttachmentsMixin);
|
|
5996
6354
|
mixin(SubsetMixin);
|
|
5997
6355
|
mixin(TableMixin);
|
|
6356
|
+
mixin(OutputIntent);
|
|
5998
6357
|
PDFDocument.LineWrapper = LineWrapper;
|
|
5999
6358
|
|
|
6000
6359
|
module.exports = PDFDocument;
|