@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.es.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import stream from 'stream';
|
|
2
2
|
import zlib from 'zlib';
|
|
3
|
-
import
|
|
3
|
+
import { concatBytes } from '@noble/hashes/utils';
|
|
4
|
+
import md5 from 'js-md5';
|
|
5
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
6
|
+
import { cbc, ecb } from '@noble/ciphers/aes';
|
|
4
7
|
import fs from 'fs';
|
|
5
8
|
import * as fontkit from 'fontkit';
|
|
6
9
|
import { EventEmitter } from 'events';
|
|
7
10
|
import LineBreaker from 'linebreak';
|
|
8
|
-
import exif from 'jpeg-exif';
|
|
9
11
|
import PNG from 'png-js';
|
|
10
12
|
|
|
11
13
|
class PDFAbstractReference {
|
|
@@ -363,6 +365,7 @@ class PDFPage {
|
|
|
363
365
|
this._options = options;
|
|
364
366
|
this.size = options.size || 'letter';
|
|
365
367
|
this.layout = options.layout || 'portrait';
|
|
368
|
+
this.userUnit = options.userUnit || 1.0;
|
|
366
369
|
const dimensions = Array.isArray(this.size) ? this.size : SIZES[this.size.toUpperCase()];
|
|
367
370
|
this.width = dimensions[this.layout === 'portrait' ? 0 : 1];
|
|
368
371
|
this.height = dimensions[this.layout === 'portrait' ? 1 : 0];
|
|
@@ -378,7 +381,8 @@ class PDFPage {
|
|
|
378
381
|
Parent: this.document._root.data.Pages,
|
|
379
382
|
MediaBox: [0, 0, this.width, this.height],
|
|
380
383
|
Contents: this.content,
|
|
381
|
-
Resources: this.resources
|
|
384
|
+
Resources: this.resources,
|
|
385
|
+
UserUnit: this.userUnit
|
|
382
386
|
});
|
|
383
387
|
this.markings = [];
|
|
384
388
|
}
|
|
@@ -434,6 +438,7 @@ class PDFPage {
|
|
|
434
438
|
for (let color of Object.values(this.document.spotColors)) {
|
|
435
439
|
this.resources.data.ColorSpace[color.id] = color;
|
|
436
440
|
}
|
|
441
|
+
this.document._writeSpaceToResources(this.resources);
|
|
437
442
|
this.resources.end();
|
|
438
443
|
return this.content.end();
|
|
439
444
|
}
|
|
@@ -451,6 +456,59 @@ class PDFNameTree extends PDFTree {
|
|
|
451
456
|
}
|
|
452
457
|
}
|
|
453
458
|
|
|
459
|
+
function md5Hash(data) {
|
|
460
|
+
return new Uint8Array(md5.arrayBuffer(data));
|
|
461
|
+
}
|
|
462
|
+
function md5Hex(data) {
|
|
463
|
+
return md5(data);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function sha256Hash(data) {
|
|
467
|
+
return sha256(data);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function aesCbcEncrypt(data, key, iv) {
|
|
471
|
+
let padding = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
|
|
472
|
+
return cbc(key, iv, {
|
|
473
|
+
disablePadding: !padding
|
|
474
|
+
}).encrypt(data);
|
|
475
|
+
}
|
|
476
|
+
function aesEcbEncrypt(data, key) {
|
|
477
|
+
return ecb(key, {
|
|
478
|
+
disablePadding: true
|
|
479
|
+
}).encrypt(data);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function rc4(data, key) {
|
|
483
|
+
const s = new Uint8Array(256);
|
|
484
|
+
for (let i = 0; i < 256; i++) {
|
|
485
|
+
s[i] = i;
|
|
486
|
+
}
|
|
487
|
+
let j = 0;
|
|
488
|
+
for (let i = 0; i < 256; i++) {
|
|
489
|
+
j = j + s[i] + key[i % key.length] & 0xff;
|
|
490
|
+
[s[i], s[j]] = [s[j], s[i]];
|
|
491
|
+
}
|
|
492
|
+
const output = new Uint8Array(data.length);
|
|
493
|
+
for (let i = 0, j = 0, k = 0; k < data.length; k++) {
|
|
494
|
+
i = i + 1 & 0xff;
|
|
495
|
+
j = j + s[i] & 0xff;
|
|
496
|
+
[s[i], s[j]] = [s[j], s[i]];
|
|
497
|
+
output[k] = data[k] ^ s[s[i] + s[j] & 0xff];
|
|
498
|
+
}
|
|
499
|
+
return output;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function randomBytes(length) {
|
|
503
|
+
const bytes = new Uint8Array(length);
|
|
504
|
+
if (globalThis.crypto?.getRandomValues) {
|
|
505
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
506
|
+
} else {
|
|
507
|
+
require('crypto').randomFillSync(bytes);
|
|
508
|
+
}
|
|
509
|
+
return bytes;
|
|
510
|
+
}
|
|
511
|
+
|
|
454
512
|
function inRange(value, rangeGroup) {
|
|
455
513
|
if (value < rangeGroup[0]) return false;
|
|
456
514
|
let startRange = 0;
|
|
@@ -551,10 +609,10 @@ class PDFSecurity {
|
|
|
551
609
|
}
|
|
552
610
|
infoStr += `${key}: ${info[key].valueOf()}\n`;
|
|
553
611
|
}
|
|
554
|
-
return
|
|
612
|
+
return Buffer.from(md5Hash(infoStr));
|
|
555
613
|
}
|
|
556
614
|
static generateRandomWordArray(bytes) {
|
|
557
|
-
return
|
|
615
|
+
return randomBytes(bytes);
|
|
558
616
|
}
|
|
559
617
|
static create(document) {
|
|
560
618
|
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
@@ -648,8 +706,8 @@ class PDFSecurity {
|
|
|
648
706
|
encDict.StrF = 'StdCF';
|
|
649
707
|
}
|
|
650
708
|
encDict.R = r;
|
|
651
|
-
encDict.O =
|
|
652
|
-
encDict.U =
|
|
709
|
+
encDict.O = Buffer.from(ownerPasswordEntry);
|
|
710
|
+
encDict.U = Buffer.from(userPasswordEntry);
|
|
653
711
|
encDict.P = permissions;
|
|
654
712
|
}
|
|
655
713
|
_setupEncryptionV5(encDict, options) {
|
|
@@ -659,10 +717,10 @@ class PDFSecurity {
|
|
|
659
717
|
const processedOwnerPassword = options.ownerPassword ? processPasswordR5(options.ownerPassword) : processedUserPassword;
|
|
660
718
|
this.encryptionKey = getEncryptionKeyR5(PDFSecurity.generateRandomWordArray);
|
|
661
719
|
const userPasswordEntry = getUserPasswordR5(processedUserPassword, PDFSecurity.generateRandomWordArray);
|
|
662
|
-
const userKeySalt =
|
|
720
|
+
const userKeySalt = userPasswordEntry.slice(40, 48);
|
|
663
721
|
const userEncryptionKeyEntry = getUserEncryptionKeyR5(processedUserPassword, userKeySalt, this.encryptionKey);
|
|
664
722
|
const ownerPasswordEntry = getOwnerPasswordR5(processedOwnerPassword, userPasswordEntry, PDFSecurity.generateRandomWordArray);
|
|
665
|
-
const ownerKeySalt =
|
|
723
|
+
const ownerKeySalt = ownerPasswordEntry.slice(40, 48);
|
|
666
724
|
const ownerEncryptionKeyEntry = getOwnerEncryptionKeyR5(processedOwnerPassword, ownerKeySalt, userPasswordEntry, this.encryptionKey);
|
|
667
725
|
const permsEntry = getEncryptedPermissionsR5(permissions, this.encryptionKey, PDFSecurity.generateRandomWordArray);
|
|
668
726
|
encDict.V = 5;
|
|
@@ -677,36 +735,37 @@ class PDFSecurity {
|
|
|
677
735
|
encDict.StmF = 'StdCF';
|
|
678
736
|
encDict.StrF = 'StdCF';
|
|
679
737
|
encDict.R = 5;
|
|
680
|
-
encDict.O =
|
|
681
|
-
encDict.OE =
|
|
682
|
-
encDict.U =
|
|
683
|
-
encDict.UE =
|
|
738
|
+
encDict.O = Buffer.from(ownerPasswordEntry);
|
|
739
|
+
encDict.OE = Buffer.from(ownerEncryptionKeyEntry);
|
|
740
|
+
encDict.U = Buffer.from(userPasswordEntry);
|
|
741
|
+
encDict.UE = Buffer.from(userEncryptionKeyEntry);
|
|
684
742
|
encDict.P = permissions;
|
|
685
|
-
encDict.Perms =
|
|
743
|
+
encDict.Perms = Buffer.from(permsEntry);
|
|
686
744
|
}
|
|
687
745
|
getEncryptFn(obj, gen) {
|
|
688
746
|
let digest;
|
|
689
747
|
if (this.version < 5) {
|
|
690
|
-
|
|
748
|
+
const suffix = new Uint8Array([obj & 0xff, obj >> 8 & 0xff, obj >> 16 & 0xff, gen & 0xff, gen >> 8 & 0xff]);
|
|
749
|
+
digest = concatBytes(this.encryptionKey, suffix);
|
|
691
750
|
}
|
|
692
751
|
if (this.version === 1 || this.version === 2) {
|
|
693
|
-
let key =
|
|
694
|
-
|
|
695
|
-
|
|
752
|
+
let key = md5Hash(digest);
|
|
753
|
+
const keyLen = Math.min(16, this.keyBits / 8 + 5);
|
|
754
|
+
key = key.slice(0, keyLen);
|
|
755
|
+
return buffer => Buffer.from(rc4(new Uint8Array(buffer), key));
|
|
696
756
|
}
|
|
697
757
|
let key;
|
|
698
758
|
if (this.version === 4) {
|
|
699
|
-
|
|
759
|
+
const saltMarker = new Uint8Array([0x73, 0x41, 0x6c, 0x54]);
|
|
760
|
+
key = md5Hash(concatBytes(digest, saltMarker));
|
|
700
761
|
} else {
|
|
701
762
|
key = this.encryptionKey;
|
|
702
763
|
}
|
|
703
764
|
const iv = PDFSecurity.generateRandomWordArray(16);
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
iv
|
|
765
|
+
return buffer => {
|
|
766
|
+
const encrypted = aesCbcEncrypt(new Uint8Array(buffer), key, iv, true);
|
|
767
|
+
return Buffer.from(concatBytes(iv, encrypted));
|
|
708
768
|
};
|
|
709
|
-
return buffer => wordArrayToBuffer(iv.clone().concat(CryptoJS.AES.encrypt(CryptoJS.lib.WordArray.create(buffer), key, options).ciphertext));
|
|
710
769
|
}
|
|
711
770
|
end() {
|
|
712
771
|
this.dictionary.end();
|
|
@@ -759,90 +818,98 @@ function getPermissionsR3() {
|
|
|
759
818
|
return permissions;
|
|
760
819
|
}
|
|
761
820
|
function getUserPasswordR2(encryptionKey) {
|
|
762
|
-
return
|
|
821
|
+
return rc4(processPasswordR2R3R4(), encryptionKey);
|
|
763
822
|
}
|
|
764
823
|
function getUserPasswordR3R4(documentId, encryptionKey) {
|
|
765
|
-
const key = encryptionKey.
|
|
766
|
-
let cipher =
|
|
824
|
+
const key = encryptionKey.slice();
|
|
825
|
+
let cipher = md5Hash(concatBytes(processPasswordR2R3R4(), new Uint8Array(documentId)));
|
|
767
826
|
for (let i = 0; i < 20; i++) {
|
|
768
|
-
const
|
|
769
|
-
for (let j = 0; j <
|
|
770
|
-
|
|
827
|
+
const xorKey = new Uint8Array(key.length);
|
|
828
|
+
for (let j = 0; j < key.length; j++) {
|
|
829
|
+
xorKey[j] = encryptionKey[j] ^ i;
|
|
771
830
|
}
|
|
772
|
-
cipher =
|
|
831
|
+
cipher = rc4(cipher, xorKey);
|
|
773
832
|
}
|
|
774
|
-
|
|
833
|
+
const result = new Uint8Array(32);
|
|
834
|
+
result.set(cipher);
|
|
835
|
+
return result;
|
|
775
836
|
}
|
|
776
837
|
function getOwnerPasswordR2R3R4(r, keyBits, paddedUserPassword, paddedOwnerPassword) {
|
|
777
838
|
let digest = paddedOwnerPassword;
|
|
778
839
|
let round = r >= 3 ? 51 : 1;
|
|
779
840
|
for (let i = 0; i < round; i++) {
|
|
780
|
-
digest =
|
|
841
|
+
digest = md5Hash(digest);
|
|
781
842
|
}
|
|
782
|
-
const
|
|
783
|
-
key
|
|
843
|
+
const keyLen = keyBits / 8;
|
|
844
|
+
let key = digest.slice(0, keyLen);
|
|
784
845
|
let cipher = paddedUserPassword;
|
|
785
846
|
round = r >= 3 ? 20 : 1;
|
|
786
847
|
for (let i = 0; i < round; i++) {
|
|
787
|
-
const
|
|
788
|
-
for (let j = 0; j <
|
|
789
|
-
|
|
848
|
+
const xorKey = new Uint8Array(keyLen);
|
|
849
|
+
for (let j = 0; j < keyLen; j++) {
|
|
850
|
+
xorKey[j] = key[j] ^ i;
|
|
790
851
|
}
|
|
791
|
-
cipher =
|
|
852
|
+
cipher = rc4(cipher, xorKey);
|
|
792
853
|
}
|
|
793
854
|
return cipher;
|
|
794
855
|
}
|
|
795
856
|
function getEncryptionKeyR2R3R4(r, keyBits, documentId, paddedUserPassword, ownerPasswordEntry, permissions) {
|
|
796
|
-
|
|
857
|
+
const permBytes = new Uint8Array([permissions & 0xff, permissions >> 8 & 0xff, permissions >> 16 & 0xff, permissions >> 24 & 0xff]);
|
|
858
|
+
let key = concatBytes(paddedUserPassword, ownerPasswordEntry, permBytes, new Uint8Array(documentId));
|
|
797
859
|
const round = r >= 3 ? 51 : 1;
|
|
860
|
+
const keyLen = keyBits / 8;
|
|
798
861
|
for (let i = 0; i < round; i++) {
|
|
799
|
-
key =
|
|
800
|
-
key
|
|
862
|
+
key = md5Hash(key);
|
|
863
|
+
key = key.slice(0, keyLen);
|
|
801
864
|
}
|
|
802
865
|
return key;
|
|
803
866
|
}
|
|
804
867
|
function getUserPasswordR5(processedUserPassword, generateRandomWordArray) {
|
|
805
868
|
const validationSalt = generateRandomWordArray(8);
|
|
806
869
|
const keySalt = generateRandomWordArray(8);
|
|
807
|
-
|
|
870
|
+
const hash = sha256Hash(concatBytes(processedUserPassword, validationSalt));
|
|
871
|
+
return concatBytes(hash, validationSalt, keySalt);
|
|
808
872
|
}
|
|
809
873
|
function getUserEncryptionKeyR5(processedUserPassword, userKeySalt, encryptionKey) {
|
|
810
|
-
const key =
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
padding: CryptoJS.pad.NoPadding,
|
|
814
|
-
iv: CryptoJS.lib.WordArray.create(null, 16)
|
|
815
|
-
};
|
|
816
|
-
return CryptoJS.AES.encrypt(encryptionKey, key, options).ciphertext;
|
|
874
|
+
const key = sha256Hash(concatBytes(processedUserPassword, userKeySalt));
|
|
875
|
+
const iv = new Uint8Array(16);
|
|
876
|
+
return aesCbcEncrypt(encryptionKey, key, iv, false);
|
|
817
877
|
}
|
|
818
878
|
function getOwnerPasswordR5(processedOwnerPassword, userPasswordEntry, generateRandomWordArray) {
|
|
819
879
|
const validationSalt = generateRandomWordArray(8);
|
|
820
880
|
const keySalt = generateRandomWordArray(8);
|
|
821
|
-
|
|
881
|
+
const hash = sha256Hash(concatBytes(processedOwnerPassword, validationSalt, userPasswordEntry));
|
|
882
|
+
return concatBytes(hash, validationSalt, keySalt);
|
|
822
883
|
}
|
|
823
884
|
function getOwnerEncryptionKeyR5(processedOwnerPassword, ownerKeySalt, userPasswordEntry, encryptionKey) {
|
|
824
|
-
const key =
|
|
825
|
-
const
|
|
826
|
-
|
|
827
|
-
padding: CryptoJS.pad.NoPadding,
|
|
828
|
-
iv: CryptoJS.lib.WordArray.create(null, 16)
|
|
829
|
-
};
|
|
830
|
-
return CryptoJS.AES.encrypt(encryptionKey, key, options).ciphertext;
|
|
885
|
+
const key = sha256Hash(concatBytes(processedOwnerPassword, ownerKeySalt, userPasswordEntry));
|
|
886
|
+
const iv = new Uint8Array(16);
|
|
887
|
+
return aesCbcEncrypt(encryptionKey, key, iv, false);
|
|
831
888
|
}
|
|
832
889
|
function getEncryptionKeyR5(generateRandomWordArray) {
|
|
833
890
|
return generateRandomWordArray(32);
|
|
834
891
|
}
|
|
835
892
|
function getEncryptedPermissionsR5(permissions, encryptionKey, generateRandomWordArray) {
|
|
836
|
-
const
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
893
|
+
const data = new Uint8Array(16);
|
|
894
|
+
data[0] = permissions & 0xff;
|
|
895
|
+
data[1] = permissions >> 8 & 0xff;
|
|
896
|
+
data[2] = permissions >> 16 & 0xff;
|
|
897
|
+
data[3] = permissions >> 24 & 0xff;
|
|
898
|
+
data[4] = 0xff;
|
|
899
|
+
data[5] = 0xff;
|
|
900
|
+
data[6] = 0xff;
|
|
901
|
+
data[7] = 0xff;
|
|
902
|
+
data[8] = 0x54;
|
|
903
|
+
data[9] = 0x61;
|
|
904
|
+
data[10] = 0x64;
|
|
905
|
+
data[11] = 0x62;
|
|
906
|
+
const randomPart = generateRandomWordArray(4);
|
|
907
|
+
data.set(randomPart, 12);
|
|
908
|
+
return aesEcbEncrypt(data, encryptionKey);
|
|
842
909
|
}
|
|
843
910
|
function processPasswordR2R3R4() {
|
|
844
911
|
let password = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
|
845
|
-
const out =
|
|
912
|
+
const out = new Uint8Array(32);
|
|
846
913
|
const length = password.length;
|
|
847
914
|
let index = 0;
|
|
848
915
|
while (index < length && index < 32) {
|
|
@@ -857,27 +924,17 @@ function processPasswordR2R3R4() {
|
|
|
857
924
|
out[index] = PASSWORD_PADDING[index - length];
|
|
858
925
|
index++;
|
|
859
926
|
}
|
|
860
|
-
return
|
|
927
|
+
return out;
|
|
861
928
|
}
|
|
862
929
|
function processPasswordR5() {
|
|
863
930
|
let password = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
|
864
931
|
password = unescape(encodeURIComponent(saslprep(password)));
|
|
865
932
|
const length = Math.min(127, password.length);
|
|
866
|
-
const out =
|
|
933
|
+
const out = new Uint8Array(length);
|
|
867
934
|
for (let i = 0; i < length; i++) {
|
|
868
935
|
out[i] = password.charCodeAt(i);
|
|
869
936
|
}
|
|
870
|
-
return
|
|
871
|
-
}
|
|
872
|
-
function lsbFirstWord(data) {
|
|
873
|
-
return (data & 0xff) << 24 | (data & 0xff00) << 8 | data >> 8 & 0xff00 | data >> 24 & 0xff;
|
|
874
|
-
}
|
|
875
|
-
function wordArrayToBuffer(wordArray) {
|
|
876
|
-
const byteArray = [];
|
|
877
|
-
for (let i = 0; i < wordArray.sigBytes; i++) {
|
|
878
|
-
byteArray.push(wordArray.words[Math.floor(i / 4)] >> 8 * (3 - i % 4) & 0xff);
|
|
879
|
-
}
|
|
880
|
-
return Buffer.from(byteArray);
|
|
937
|
+
return out;
|
|
881
938
|
}
|
|
882
939
|
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];
|
|
883
940
|
|
|
@@ -1175,6 +1232,7 @@ var ColorMixin = {
|
|
|
1175
1232
|
this._opacityCount = 0;
|
|
1176
1233
|
this._patternCount = 0;
|
|
1177
1234
|
this._gradCount = 0;
|
|
1235
|
+
this._activeColorProfile = null;
|
|
1178
1236
|
},
|
|
1179
1237
|
_normalizeColor(color) {
|
|
1180
1238
|
if (typeof color === 'string') {
|
|
@@ -1194,7 +1252,7 @@ var ColorMixin = {
|
|
|
1194
1252
|
if (color.length === 3) {
|
|
1195
1253
|
color = color.map(part => part / 255);
|
|
1196
1254
|
} else if (color.length === 4) {
|
|
1197
|
-
color = color.map(part => part /
|
|
1255
|
+
color = color.map(part => part / 255);
|
|
1198
1256
|
}
|
|
1199
1257
|
return color;
|
|
1200
1258
|
}
|
|
@@ -1234,6 +1292,13 @@ var ColorMixin = {
|
|
|
1234
1292
|
if (color instanceof SpotColor) {
|
|
1235
1293
|
return color.id;
|
|
1236
1294
|
}
|
|
1295
|
+
if (this._activeColorProfile) {
|
|
1296
|
+
const profile = this._activeColorProfile;
|
|
1297
|
+
if (profile.channels !== color.length) {
|
|
1298
|
+
throw Error("Profile channels don't match color channels");
|
|
1299
|
+
}
|
|
1300
|
+
return profile.label;
|
|
1301
|
+
}
|
|
1237
1302
|
return color.length === 4 ? 'DeviceCMYK' : 'DeviceRGB';
|
|
1238
1303
|
},
|
|
1239
1304
|
fillColor(color, opacity) {
|
|
@@ -1263,6 +1328,18 @@ var ColorMixin = {
|
|
|
1263
1328
|
this._doOpacity(null, opacity);
|
|
1264
1329
|
return this;
|
|
1265
1330
|
},
|
|
1331
|
+
beingColorProfile(label) {
|
|
1332
|
+
const profile = this._colorProfiles[label];
|
|
1333
|
+
if (!profile) {
|
|
1334
|
+
throw Error('Invalid color space label, the profile should be set first');
|
|
1335
|
+
}
|
|
1336
|
+
this._activeColorProfile = profile;
|
|
1337
|
+
return this;
|
|
1338
|
+
},
|
|
1339
|
+
endColorProfile() {
|
|
1340
|
+
this._activeColorProfile = null;
|
|
1341
|
+
return this;
|
|
1342
|
+
},
|
|
1266
1343
|
_doOpacity(fillOpacity, strokeOpacity) {
|
|
1267
1344
|
let dictionary, name;
|
|
1268
1345
|
if (fillOpacity == null && strokeOpacity == null) {
|
|
@@ -1485,86 +1562,176 @@ const parameters = {
|
|
|
1485
1562
|
Z: 0,
|
|
1486
1563
|
z: 0
|
|
1487
1564
|
};
|
|
1565
|
+
const isCommand = function (c) {
|
|
1566
|
+
return c in parameters;
|
|
1567
|
+
};
|
|
1568
|
+
const isWsp = function (c) {
|
|
1569
|
+
const codePoint = c.codePointAt(0);
|
|
1570
|
+
return codePoint === 0x20 || codePoint === 0x9 || codePoint === 0xd || codePoint === 0xa;
|
|
1571
|
+
};
|
|
1572
|
+
const isDigit = function (c) {
|
|
1573
|
+
const codePoint = c.codePointAt(0);
|
|
1574
|
+
if (codePoint == null) {
|
|
1575
|
+
return false;
|
|
1576
|
+
}
|
|
1577
|
+
return 48 <= codePoint && codePoint <= 57;
|
|
1578
|
+
};
|
|
1579
|
+
const readNumber = function (string, cursor) {
|
|
1580
|
+
let i = cursor;
|
|
1581
|
+
let value = '';
|
|
1582
|
+
let state = 'none';
|
|
1583
|
+
for (; i < string.length; i += 1) {
|
|
1584
|
+
const c = string[i];
|
|
1585
|
+
if (c === '+' || c === '-') {
|
|
1586
|
+
if (state === 'none') {
|
|
1587
|
+
state = 'sign';
|
|
1588
|
+
value += c;
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
if (state === 'e') {
|
|
1592
|
+
state = 'exponent_sign';
|
|
1593
|
+
value += c;
|
|
1594
|
+
continue;
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
if (isDigit(c)) {
|
|
1598
|
+
if (state === 'none' || state === 'sign' || state === 'whole') {
|
|
1599
|
+
state = 'whole';
|
|
1600
|
+
value += c;
|
|
1601
|
+
continue;
|
|
1602
|
+
}
|
|
1603
|
+
if (state === 'decimal_point' || state === 'decimal') {
|
|
1604
|
+
state = 'decimal';
|
|
1605
|
+
value += c;
|
|
1606
|
+
continue;
|
|
1607
|
+
}
|
|
1608
|
+
if (state === 'e' || state === 'exponent_sign' || state === 'exponent') {
|
|
1609
|
+
state = 'exponent';
|
|
1610
|
+
value += c;
|
|
1611
|
+
continue;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
if (c === '.') {
|
|
1615
|
+
if (state === 'none' || state === 'sign' || state === 'whole') {
|
|
1616
|
+
state = 'decimal_point';
|
|
1617
|
+
value += c;
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
if (c === 'E' || c === 'e') {
|
|
1622
|
+
if (state === 'whole' || state === 'decimal_point' || state === 'decimal') {
|
|
1623
|
+
state = 'e';
|
|
1624
|
+
value += c;
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
break;
|
|
1629
|
+
}
|
|
1630
|
+
const number = Number.parseFloat(value);
|
|
1631
|
+
if (Number.isNaN(number)) {
|
|
1632
|
+
return [cursor, null];
|
|
1633
|
+
}
|
|
1634
|
+
return [i - 1, number];
|
|
1635
|
+
};
|
|
1488
1636
|
const parse = function (path) {
|
|
1489
|
-
|
|
1490
|
-
|
|
1637
|
+
const pathData = [];
|
|
1638
|
+
let command = null;
|
|
1491
1639
|
let args = [];
|
|
1492
|
-
let
|
|
1493
|
-
let
|
|
1494
|
-
let
|
|
1495
|
-
for (let
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1640
|
+
let argsCount = 0;
|
|
1641
|
+
let canHaveComma = false;
|
|
1642
|
+
let hadComma = false;
|
|
1643
|
+
for (let i = 0; i < path.length; i += 1) {
|
|
1644
|
+
const c = path.charAt(i);
|
|
1645
|
+
if (isWsp(c)) {
|
|
1646
|
+
continue;
|
|
1647
|
+
}
|
|
1648
|
+
if (canHaveComma && c === ',') {
|
|
1649
|
+
if (hadComma) {
|
|
1650
|
+
break;
|
|
1651
|
+
}
|
|
1652
|
+
hadComma = true;
|
|
1653
|
+
continue;
|
|
1654
|
+
}
|
|
1655
|
+
if (isCommand(c)) {
|
|
1656
|
+
if (hadComma) {
|
|
1657
|
+
return pathData;
|
|
1658
|
+
}
|
|
1659
|
+
if (command == null) {
|
|
1660
|
+
if (c !== 'M' && c !== 'm') {
|
|
1661
|
+
return pathData;
|
|
1501
1662
|
}
|
|
1502
|
-
|
|
1503
|
-
|
|
1663
|
+
} else {
|
|
1664
|
+
if (args.length !== 0) {
|
|
1665
|
+
return pathData;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
command = c;
|
|
1669
|
+
args = [];
|
|
1670
|
+
argsCount = parameters[command];
|
|
1671
|
+
canHaveComma = false;
|
|
1672
|
+
if (argsCount === 0) {
|
|
1673
|
+
pathData.push({
|
|
1674
|
+
command,
|
|
1504
1675
|
args
|
|
1505
|
-
};
|
|
1506
|
-
args = [];
|
|
1507
|
-
curArg = '';
|
|
1508
|
-
foundDecimal = false;
|
|
1676
|
+
});
|
|
1509
1677
|
}
|
|
1510
|
-
|
|
1511
|
-
}
|
|
1512
|
-
|
|
1513
|
-
|
|
1678
|
+
continue;
|
|
1679
|
+
}
|
|
1680
|
+
if (command == null) {
|
|
1681
|
+
return pathData;
|
|
1682
|
+
}
|
|
1683
|
+
let newCursor = i;
|
|
1684
|
+
let number = null;
|
|
1685
|
+
if (command === 'A' || command === 'a') {
|
|
1686
|
+
const position = args.length;
|
|
1687
|
+
if (position === 0 || position === 1) {
|
|
1688
|
+
if (c !== '+' && c !== '-') {
|
|
1689
|
+
[newCursor, number] = readNumber(path, i);
|
|
1690
|
+
}
|
|
1514
1691
|
}
|
|
1515
|
-
if (
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
if (cmd === 'M') {
|
|
1522
|
-
cmd = 'L';
|
|
1692
|
+
if (position === 2 || position === 5 || position === 6) {
|
|
1693
|
+
[newCursor, number] = readNumber(path, i);
|
|
1694
|
+
}
|
|
1695
|
+
if (position === 3 || position === 4) {
|
|
1696
|
+
if (c === '0') {
|
|
1697
|
+
number = 0;
|
|
1523
1698
|
}
|
|
1524
|
-
if (
|
|
1525
|
-
|
|
1699
|
+
if (c === '1') {
|
|
1700
|
+
number = 1;
|
|
1526
1701
|
}
|
|
1527
|
-
} else {
|
|
1528
|
-
args[args.length] = +curArg;
|
|
1529
1702
|
}
|
|
1530
|
-
foundDecimal = c === '.';
|
|
1531
|
-
curArg = ['-', '.'].includes(c) ? c : '';
|
|
1532
1703
|
} else {
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1704
|
+
[newCursor, number] = readNumber(path, i);
|
|
1705
|
+
}
|
|
1706
|
+
if (number == null) {
|
|
1707
|
+
return pathData;
|
|
1708
|
+
}
|
|
1709
|
+
args.push(number);
|
|
1710
|
+
canHaveComma = true;
|
|
1711
|
+
hadComma = false;
|
|
1712
|
+
i = newCursor;
|
|
1713
|
+
if (args.length === argsCount) {
|
|
1714
|
+
pathData.push({
|
|
1715
|
+
command,
|
|
1543
1716
|
args
|
|
1544
|
-
};
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
cmd = 'L';
|
|
1717
|
+
});
|
|
1718
|
+
if (command === 'M') {
|
|
1719
|
+
command = 'L';
|
|
1548
1720
|
}
|
|
1549
|
-
if (
|
|
1550
|
-
|
|
1721
|
+
if (command === 'm') {
|
|
1722
|
+
command = 'l';
|
|
1551
1723
|
}
|
|
1552
|
-
|
|
1553
|
-
args[args.length] = +curArg;
|
|
1724
|
+
args = [];
|
|
1554
1725
|
}
|
|
1555
1726
|
}
|
|
1556
|
-
|
|
1557
|
-
cmd,
|
|
1558
|
-
args
|
|
1559
|
-
};
|
|
1560
|
-
return ret;
|
|
1727
|
+
return pathData;
|
|
1561
1728
|
};
|
|
1562
1729
|
const apply = function (commands, doc) {
|
|
1563
1730
|
cx = cy = px = py = sx = sy = 0;
|
|
1564
1731
|
for (let i = 0; i < commands.length; i++) {
|
|
1565
1732
|
const c = commands[i];
|
|
1566
|
-
if (typeof runners[c.
|
|
1567
|
-
runners[c.
|
|
1733
|
+
if (typeof runners[c.command] === 'function') {
|
|
1734
|
+
runners[c.command](doc, c.args);
|
|
1568
1735
|
}
|
|
1569
1736
|
}
|
|
1570
1737
|
};
|
|
@@ -2603,7 +2770,7 @@ begincmap
|
|
|
2603
2770
|
1 begincodespacerange
|
|
2604
2771
|
<0000><ffff>
|
|
2605
2772
|
endcodespacerange
|
|
2606
|
-
|
|
2773
|
+
${ranges.length} beginbfrange
|
|
2607
2774
|
${ranges.join('\n')}
|
|
2608
2775
|
endbfrange
|
|
2609
2776
|
endcmap
|
|
@@ -2788,6 +2955,7 @@ class LineWrapper extends EventEmitter {
|
|
|
2788
2955
|
this.document = document;
|
|
2789
2956
|
this.horizontalScaling = options.horizontalScaling || 100;
|
|
2790
2957
|
this.indent = (options.indent || 0) * this.horizontalScaling / 100;
|
|
2958
|
+
this.indentAllLines = options.indentAllLines || false;
|
|
2791
2959
|
this.characterSpacing = (options.characterSpacing || 0) * this.horizontalScaling / 100;
|
|
2792
2960
|
this.wordSpacing = (options.wordSpacing === 0) * this.horizontalScaling / 100;
|
|
2793
2961
|
this.columns = options.columns || 1;
|
|
@@ -3029,7 +3197,13 @@ class LineWrapper extends EventEmitter {
|
|
|
3029
3197
|
this.column = 1;
|
|
3030
3198
|
this.startY = this.document.page.margins.top;
|
|
3031
3199
|
this.maxY = this.document.page.maxY();
|
|
3032
|
-
this.
|
|
3200
|
+
if (this.indentAllLines) {
|
|
3201
|
+
const indent = this.continuedX || this.indent;
|
|
3202
|
+
this.document.x += indent;
|
|
3203
|
+
this.lineWidth -= indent;
|
|
3204
|
+
} else {
|
|
3205
|
+
this.document.x = this.startX;
|
|
3206
|
+
}
|
|
3033
3207
|
if (this.document._fillColor) {
|
|
3034
3208
|
this.document.fillColor(...this.document._fillColor);
|
|
3035
3209
|
}
|
|
@@ -3439,7 +3613,11 @@ var TextMixin = {
|
|
|
3439
3613
|
}
|
|
3440
3614
|
const renderedWidth = options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1);
|
|
3441
3615
|
if (options.link != null) {
|
|
3442
|
-
|
|
3616
|
+
const linkOptions = {};
|
|
3617
|
+
if (this._currentStructureElement && this._currentStructureElement.dictionary.data.S === 'Link') {
|
|
3618
|
+
linkOptions.structParent = this._currentStructureElement;
|
|
3619
|
+
}
|
|
3620
|
+
this.link(x, y, renderedWidth, this.currentLineHeight(), options.link, linkOptions);
|
|
3443
3621
|
}
|
|
3444
3622
|
if (options.goTo != null) {
|
|
3445
3623
|
this.goTo(x, y, renderedWidth, this.currentLineHeight(), options.goTo);
|
|
@@ -3568,6 +3746,47 @@ var TextMixin = {
|
|
|
3568
3746
|
}
|
|
3569
3747
|
};
|
|
3570
3748
|
|
|
3749
|
+
const parseExifOrientation = data => {
|
|
3750
|
+
if (!data || data.length < 20) return null;
|
|
3751
|
+
let pos = 2;
|
|
3752
|
+
while (pos < data.length - 4) {
|
|
3753
|
+
while (pos < data.length && data[pos] !== 0xff) pos++;
|
|
3754
|
+
if (pos >= data.length - 4) return null;
|
|
3755
|
+
const marker = data.readUInt16BE(pos);
|
|
3756
|
+
pos += 2;
|
|
3757
|
+
if (marker === 0xffda) return null;
|
|
3758
|
+
if (marker >= 0xffd0 && marker <= 0xffd9 || marker === 0xff01) continue;
|
|
3759
|
+
if (pos + 2 > data.length) return null;
|
|
3760
|
+
const segmentLength = data.readUInt16BE(pos);
|
|
3761
|
+
if (marker === 0xffe1 && pos + 8 <= data.length) {
|
|
3762
|
+
const exifHeader = data.subarray(pos + 2, pos + 8).toString('binary');
|
|
3763
|
+
if (exifHeader === 'Exif\x00\x00') {
|
|
3764
|
+
const tiffStart = pos + 8;
|
|
3765
|
+
if (tiffStart + 8 > data.length) return null;
|
|
3766
|
+
const byteOrder = data.subarray(tiffStart, tiffStart + 2).toString('ascii');
|
|
3767
|
+
const isLittleEndian = byteOrder === 'II';
|
|
3768
|
+
if (!isLittleEndian && byteOrder !== 'MM') return null;
|
|
3769
|
+
const read16 = isLittleEndian ? o => data.readUInt16LE(o) : o => data.readUInt16BE(o);
|
|
3770
|
+
const read32 = isLittleEndian ? o => data.readUInt32LE(o) : o => data.readUInt32BE(o);
|
|
3771
|
+
if (read16(tiffStart + 2) !== 42) return null;
|
|
3772
|
+
const ifdPos = tiffStart + read32(tiffStart + 4);
|
|
3773
|
+
if (ifdPos + 2 > data.length) return null;
|
|
3774
|
+
const entryCount = read16(ifdPos);
|
|
3775
|
+
for (let i = 0; i < entryCount; i++) {
|
|
3776
|
+
const entryPos = ifdPos + 2 + i * 12;
|
|
3777
|
+
if (entryPos + 12 > data.length) return null;
|
|
3778
|
+
if (read16(entryPos) === 0x0112) {
|
|
3779
|
+
const value = read16(entryPos + 8);
|
|
3780
|
+
return value >= 1 && value <= 8 ? value : null;
|
|
3781
|
+
}
|
|
3782
|
+
}
|
|
3783
|
+
return null;
|
|
3784
|
+
}
|
|
3785
|
+
}
|
|
3786
|
+
pos += segmentLength;
|
|
3787
|
+
}
|
|
3788
|
+
return null;
|
|
3789
|
+
};
|
|
3571
3790
|
const MARKERS = [0xffc0, 0xffc1, 0xffc2, 0xffc3, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9, 0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf];
|
|
3572
3791
|
const COLOR_SPACE_MAP = {
|
|
3573
3792
|
1: 'DeviceGray',
|
|
@@ -3582,9 +3801,11 @@ class JPEG {
|
|
|
3582
3801
|
if (this.data.readUInt16BE(0) !== 0xffd8) {
|
|
3583
3802
|
throw 'SOI not found in JPEG';
|
|
3584
3803
|
}
|
|
3585
|
-
this.orientation =
|
|
3804
|
+
this.orientation = parseExifOrientation(this.data) || 1;
|
|
3586
3805
|
let pos = 2;
|
|
3587
3806
|
while (pos < this.data.length) {
|
|
3807
|
+
while (pos < this.data.length && this.data[pos] !== 0xff) pos++;
|
|
3808
|
+
if (pos >= this.data.length) break;
|
|
3588
3809
|
marker = this.data.readUInt16BE(pos);
|
|
3589
3810
|
pos += 2;
|
|
3590
3811
|
if (MARKERS.includes(marker)) {
|
|
@@ -3736,12 +3957,16 @@ class PNGImage {
|
|
|
3736
3957
|
}
|
|
3737
3958
|
loadIndexedAlphaChannel() {
|
|
3738
3959
|
const transparency = this.image.transparency.indexed;
|
|
3960
|
+
const isInterlaced = this.image.interlaceMethod === 1;
|
|
3739
3961
|
return this.image.decodePixels(pixels => {
|
|
3740
3962
|
const alphaChannel = Buffer.alloc(this.width * this.height);
|
|
3741
3963
|
let i = 0;
|
|
3742
3964
|
for (let j = 0, end = pixels.length; j < end; j++) {
|
|
3743
3965
|
alphaChannel[i++] = transparency[pixels[j]];
|
|
3744
3966
|
}
|
|
3967
|
+
if (isInterlaced) {
|
|
3968
|
+
this.imgData = zlib.deflateSync(Buffer.from(pixels));
|
|
3969
|
+
}
|
|
3745
3970
|
this.alphaChannel = zlib.deflateSync(alphaChannel);
|
|
3746
3971
|
return this.finalize();
|
|
3747
3972
|
});
|
|
@@ -3763,7 +3988,7 @@ class PDFImage {
|
|
|
3763
3988
|
data = Buffer.from(new Uint8Array(src));
|
|
3764
3989
|
} else {
|
|
3765
3990
|
const split = src.split(',');
|
|
3766
|
-
if (split[0].startsWith('data:') && split[0].endsWith(';base64
|
|
3991
|
+
if (split[0].startsWith('data:') && split[0].endsWith(';base64')) {
|
|
3767
3992
|
if (split.length === 1) {
|
|
3768
3993
|
throw Error('Empty base64');
|
|
3769
3994
|
}
|
|
@@ -3974,6 +4199,12 @@ var ImagesMixin = {
|
|
|
3974
4199
|
}
|
|
3975
4200
|
};
|
|
3976
4201
|
|
|
4202
|
+
class PDFAnnotationReference {
|
|
4203
|
+
constructor(annotationRef) {
|
|
4204
|
+
this.annotationRef = annotationRef;
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
|
|
3977
4208
|
var AnnotationsMixin = {
|
|
3978
4209
|
annotate(x, y, w, h, options) {
|
|
3979
4210
|
options.Type = 'Annot';
|
|
@@ -3991,12 +4222,18 @@ var AnnotationsMixin = {
|
|
|
3991
4222
|
if (typeof options.Dest === 'string') {
|
|
3992
4223
|
options.Dest = new String(options.Dest);
|
|
3993
4224
|
}
|
|
4225
|
+
const structParent = options.structParent;
|
|
4226
|
+
delete options.structParent;
|
|
3994
4227
|
for (let key in options) {
|
|
3995
4228
|
const val = options[key];
|
|
3996
4229
|
options[key[0].toUpperCase() + key.slice(1)] = val;
|
|
3997
4230
|
}
|
|
3998
4231
|
const ref = this.ref(options);
|
|
3999
4232
|
this.page.annotations.push(ref);
|
|
4233
|
+
if (structParent && typeof structParent.add === 'function') {
|
|
4234
|
+
const annotRef = new PDFAnnotationReference(ref);
|
|
4235
|
+
structParent.add(annotRef);
|
|
4236
|
+
}
|
|
4000
4237
|
ref.end();
|
|
4001
4238
|
return this;
|
|
4002
4239
|
},
|
|
@@ -4043,6 +4280,9 @@ var AnnotationsMixin = {
|
|
|
4043
4280
|
});
|
|
4044
4281
|
options.A.end();
|
|
4045
4282
|
}
|
|
4283
|
+
if (options.structParent && !options.Contents) {
|
|
4284
|
+
options.Contents = new String('');
|
|
4285
|
+
}
|
|
4046
4286
|
return this.annotate(x, y, w, h, options);
|
|
4047
4287
|
},
|
|
4048
4288
|
_markup(x, y, w, h) {
|
|
@@ -4124,16 +4364,27 @@ var AnnotationsMixin = {
|
|
|
4124
4364
|
}
|
|
4125
4365
|
};
|
|
4126
4366
|
|
|
4367
|
+
const DEFAULT_OPTIONS = {
|
|
4368
|
+
top: 0,
|
|
4369
|
+
left: 0,
|
|
4370
|
+
zoom: 0,
|
|
4371
|
+
fit: true,
|
|
4372
|
+
pageNumber: null,
|
|
4373
|
+
expanded: false
|
|
4374
|
+
};
|
|
4127
4375
|
class PDFOutline {
|
|
4128
4376
|
constructor(document, parent, title, dest) {
|
|
4129
|
-
let options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] :
|
|
4130
|
-
expanded: false
|
|
4131
|
-
};
|
|
4377
|
+
let options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : DEFAULT_OPTIONS;
|
|
4132
4378
|
this.document = document;
|
|
4133
4379
|
this.options = options;
|
|
4134
4380
|
this.outlineData = {};
|
|
4135
4381
|
if (dest !== null) {
|
|
4136
|
-
|
|
4382
|
+
const destWidth = dest.data.MediaBox[2];
|
|
4383
|
+
const destHeight = dest.data.MediaBox[3];
|
|
4384
|
+
const top = destHeight - (options.top || 0);
|
|
4385
|
+
const left = destWidth - (options.left || 0);
|
|
4386
|
+
const zoom = options.zoom || 0;
|
|
4387
|
+
this.outlineData['Dest'] = options.fit ? [dest, 'Fit'] : [dest, 'XYZ', left, top, zoom];
|
|
4137
4388
|
}
|
|
4138
4389
|
if (parent !== null) {
|
|
4139
4390
|
this.outlineData['Parent'] = parent;
|
|
@@ -4145,10 +4396,10 @@ class PDFOutline {
|
|
|
4145
4396
|
this.children = [];
|
|
4146
4397
|
}
|
|
4147
4398
|
addItem(title) {
|
|
4148
|
-
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] :
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
const result = new PDFOutline(this.document, this.dictionary, title,
|
|
4399
|
+
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_OPTIONS;
|
|
4400
|
+
const pages = this.document._root.data.Pages.data.Kids;
|
|
4401
|
+
const dest = options.pageNumber != null ? pages[options.pageNumber] : this.document.page.dictionary;
|
|
4402
|
+
const result = new PDFOutline(this.document, this.dictionary, title, dest, options);
|
|
4152
4403
|
this.children.push(result);
|
|
4153
4404
|
return result;
|
|
4154
4405
|
}
|
|
@@ -4184,7 +4435,7 @@ var OutlineMixin = {
|
|
|
4184
4435
|
this.outline.endOutline();
|
|
4185
4436
|
if (this.outline.children.length > 0) {
|
|
4186
4437
|
this._root.data.Outlines = this.outline.dictionary;
|
|
4187
|
-
return this._root.data.PageMode = 'UseOutlines';
|
|
4438
|
+
return this._root.data.PageMode = this._root.data.PageMode || 'UseOutlines';
|
|
4188
4439
|
}
|
|
4189
4440
|
}
|
|
4190
4441
|
};
|
|
@@ -4257,6 +4508,9 @@ class PDFStructureElement {
|
|
|
4257
4508
|
if (child instanceof PDFStructureContent) {
|
|
4258
4509
|
this._addContentToParentTree(child);
|
|
4259
4510
|
}
|
|
4511
|
+
if (child instanceof PDFAnnotationReference) {
|
|
4512
|
+
this._addAnnotationToParentTree(child.annotationRef);
|
|
4513
|
+
}
|
|
4260
4514
|
if (typeof child === 'function' && this._attached) {
|
|
4261
4515
|
child = this._contentForClosure(child);
|
|
4262
4516
|
}
|
|
@@ -4273,6 +4527,12 @@ class PDFStructureElement {
|
|
|
4273
4527
|
pageStructParents[mcid] = this.dictionary;
|
|
4274
4528
|
});
|
|
4275
4529
|
}
|
|
4530
|
+
_addAnnotationToParentTree(annotRef) {
|
|
4531
|
+
const parentTreeKey = this.document.createStructParentTreeNextKey();
|
|
4532
|
+
annotRef.data.StructParent = parentTreeKey;
|
|
4533
|
+
const parentTree = this.document.getStructParentTree();
|
|
4534
|
+
parentTree.add(parentTreeKey, this.dictionary);
|
|
4535
|
+
}
|
|
4276
4536
|
setParent(parentRef) {
|
|
4277
4537
|
if (this.dictionary.data.P) {
|
|
4278
4538
|
throw new Error(`Structure element added to more than one parent`);
|
|
@@ -4304,11 +4564,17 @@ class PDFStructureElement {
|
|
|
4304
4564
|
this._flush();
|
|
4305
4565
|
}
|
|
4306
4566
|
_isValidChild(child) {
|
|
4307
|
-
return child instanceof PDFStructureElement || child instanceof PDFStructureContent || typeof child === 'function';
|
|
4567
|
+
return child instanceof PDFStructureElement || child instanceof PDFStructureContent || child instanceof PDFAnnotationReference || typeof child === 'function';
|
|
4308
4568
|
}
|
|
4309
4569
|
_contentForClosure(closure) {
|
|
4310
4570
|
const content = this.document.markStructureContent(this.dictionary.data.S);
|
|
4571
|
+
const prevStructElement = this.document._currentStructureElement;
|
|
4572
|
+
this.document._currentStructureElement = this;
|
|
4573
|
+
const wasEnded = this._ended;
|
|
4574
|
+
this._ended = false;
|
|
4311
4575
|
closure();
|
|
4576
|
+
this._ended = wasEnded;
|
|
4577
|
+
this.document._currentStructureElement = prevStructElement;
|
|
4312
4578
|
this.document.endMarkedContent();
|
|
4313
4579
|
this._addContentToParentTree(content);
|
|
4314
4580
|
return content;
|
|
@@ -4362,6 +4628,15 @@ class PDFStructureElement {
|
|
|
4362
4628
|
}
|
|
4363
4629
|
});
|
|
4364
4630
|
}
|
|
4631
|
+
if (child instanceof PDFAnnotationReference) {
|
|
4632
|
+
const pageRef = this.document.page.dictionary;
|
|
4633
|
+
const objr = {
|
|
4634
|
+
Type: 'OBJR',
|
|
4635
|
+
Obj: child.annotationRef,
|
|
4636
|
+
Pg: pageRef
|
|
4637
|
+
};
|
|
4638
|
+
this.dictionary.data.K.push(objr);
|
|
4639
|
+
}
|
|
4365
4640
|
}
|
|
4366
4641
|
}
|
|
4367
4642
|
|
|
@@ -4457,6 +4732,13 @@ var MarkingsMixin = {
|
|
|
4457
4732
|
endMarkedContent() {
|
|
4458
4733
|
this.page.markings.pop();
|
|
4459
4734
|
this.addContent('EMC');
|
|
4735
|
+
if (this._textOptions) {
|
|
4736
|
+
delete this._textOptions.link;
|
|
4737
|
+
delete this._textOptions.goTo;
|
|
4738
|
+
delete this._textOptions.destination;
|
|
4739
|
+
delete this._textOptions.underline;
|
|
4740
|
+
delete this._textOptions.strike;
|
|
4741
|
+
}
|
|
4460
4742
|
return this;
|
|
4461
4743
|
},
|
|
4462
4744
|
struct(type) {
|
|
@@ -4915,7 +5197,7 @@ var AttachmentsMixin = {
|
|
|
4915
5197
|
if (options.type) {
|
|
4916
5198
|
refBody.Subtype = options.type.replace('/', '#2F');
|
|
4917
5199
|
}
|
|
4918
|
-
const checksum =
|
|
5200
|
+
const checksum = md5Hex(new Uint8Array(data));
|
|
4919
5201
|
refBody.Params.CheckSum = new String(checksum);
|
|
4920
5202
|
refBody.Params.Size = data.byteLength;
|
|
4921
5203
|
let ref;
|
|
@@ -4975,6 +5257,9 @@ var PDFA = {
|
|
|
4975
5257
|
this._addColorOutputIntent();
|
|
4976
5258
|
},
|
|
4977
5259
|
_addColorOutputIntent() {
|
|
5260
|
+
if (this._root.data.OutputIntents && this._root.data.OutputIntents.length !== 0) {
|
|
5261
|
+
return;
|
|
5262
|
+
}
|
|
4978
5263
|
const iccProfile = fs.readFileSync(`${__dirname}/data/sRGB_IEC61966_2_1.icc`);
|
|
4979
5264
|
const colorProfileRef = this.ref({
|
|
4980
5265
|
Length: iccProfile.length,
|
|
@@ -5567,7 +5852,7 @@ function renderRow(row, rowIndex) {
|
|
|
5567
5852
|
function renderCell(cell, rowStruct) {
|
|
5568
5853
|
const cellRenderer = () => {
|
|
5569
5854
|
if (cell.backgroundColor != null) {
|
|
5570
|
-
this.document.save().rect(cell.x, cell.y, cell.width, cell.height).fill(
|
|
5855
|
+
this.document.save().fillColor(cell.backgroundColor).rect(cell.x, cell.y, cell.width, cell.height).fill().restore();
|
|
5571
5856
|
}
|
|
5572
5857
|
renderBorder.call(this, cell.border, cell.borderColor, cell.x, cell.y, cell.width, cell.height);
|
|
5573
5858
|
if (cell.debug) {
|
|
@@ -5631,20 +5916,20 @@ function renderBorder(border, borderColor, x, y, width, height, mask) {
|
|
|
5631
5916
|
const doc = this.document;
|
|
5632
5917
|
if ([border.right, border.bottom, border.left].every(val => val === border.top)) {
|
|
5633
5918
|
if (border.top > 0) {
|
|
5634
|
-
doc.save().lineWidth(border.top).rect(x, y, width, height).stroke(
|
|
5919
|
+
doc.save().lineWidth(border.top).strokeColor(borderColor.top).rect(x, y, width, height).stroke().restore();
|
|
5635
5920
|
}
|
|
5636
5921
|
} else {
|
|
5637
5922
|
if (border.top > 0) {
|
|
5638
|
-
doc.save().lineWidth(border.top).moveTo(x, y).lineTo(x + width, y).stroke(
|
|
5923
|
+
doc.save().lineWidth(border.top).moveTo(x, y).strokeColor(borderColor.top).lineTo(x + width, y).stroke().restore();
|
|
5639
5924
|
}
|
|
5640
5925
|
if (border.right > 0) {
|
|
5641
|
-
doc.save().lineWidth(border.right).moveTo(x + width, y).lineTo(x + width, y + height).stroke(
|
|
5926
|
+
doc.save().lineWidth(border.right).moveTo(x + width, y).strokeColor(borderColor.right).lineTo(x + width, y + height).stroke().restore();
|
|
5642
5927
|
}
|
|
5643
5928
|
if (border.bottom > 0) {
|
|
5644
|
-
doc.save().lineWidth(border.bottom).moveTo(x + width, y + height).lineTo(x, y + height).stroke(
|
|
5929
|
+
doc.save().lineWidth(border.bottom).moveTo(x + width, y + height).strokeColor(borderColor.bottom).lineTo(x, y + height).stroke().restore();
|
|
5645
5930
|
}
|
|
5646
5931
|
if (border.left > 0) {
|
|
5647
|
-
doc.save().lineWidth(border.left).moveTo(x, y + height).lineTo(x, y).stroke(
|
|
5932
|
+
doc.save().lineWidth(border.left).moveTo(x, y + height).strokeColor(borderColor.left).lineTo(x, y).stroke().restore();
|
|
5648
5933
|
}
|
|
5649
5934
|
}
|
|
5650
5935
|
}
|
|
@@ -5810,6 +6095,74 @@ var MetadataMixin = {
|
|
|
5810
6095
|
}
|
|
5811
6096
|
};
|
|
5812
6097
|
|
|
6098
|
+
class ICCProfile {
|
|
6099
|
+
constructor(label, data, channels, alternate) {
|
|
6100
|
+
this.label = label;
|
|
6101
|
+
this.data = data;
|
|
6102
|
+
this.channels = channels;
|
|
6103
|
+
this.alternate = alternate;
|
|
6104
|
+
this.ref = null;
|
|
6105
|
+
this.streamRef = null;
|
|
6106
|
+
}
|
|
6107
|
+
embed(document) {
|
|
6108
|
+
if (this.ref) {
|
|
6109
|
+
return;
|
|
6110
|
+
}
|
|
6111
|
+
this.document = document;
|
|
6112
|
+
this.streamRef = this.document.ref({
|
|
6113
|
+
Alternate: this.alternate,
|
|
6114
|
+
N: this.channels,
|
|
6115
|
+
Length: this.data.length
|
|
6116
|
+
});
|
|
6117
|
+
this.streamRef.write(this.data);
|
|
6118
|
+
this.streamRef.end();
|
|
6119
|
+
this.ref = this.document.ref([`ICCBased ${this.streamRef}`]);
|
|
6120
|
+
this.ref.end();
|
|
6121
|
+
this.data = null;
|
|
6122
|
+
}
|
|
6123
|
+
}
|
|
6124
|
+
|
|
6125
|
+
var ColorSpaceMixin = {
|
|
6126
|
+
initColorSpace() {
|
|
6127
|
+
this._colorProfiles = {};
|
|
6128
|
+
},
|
|
6129
|
+
iccProfile(label, data, channels, alternate) {
|
|
6130
|
+
const profile = new ICCProfile(label, data, channels, alternate);
|
|
6131
|
+
profile.embed(this);
|
|
6132
|
+
this._colorProfiles[label] = profile;
|
|
6133
|
+
return this;
|
|
6134
|
+
},
|
|
6135
|
+
_writeSpaceToResources(resources) {
|
|
6136
|
+
resources.data.ColorSpace = resources.data.ColorSpace || {};
|
|
6137
|
+
Object.entries(this._colorProfiles).forEach(_ref => {
|
|
6138
|
+
let [k, v] = _ref;
|
|
6139
|
+
resources.data.ColorSpace[k] = v.ref;
|
|
6140
|
+
});
|
|
6141
|
+
}
|
|
6142
|
+
};
|
|
6143
|
+
|
|
6144
|
+
var OutputIntent = {
|
|
6145
|
+
initOutputIntent() {
|
|
6146
|
+
this._root.data.OutputIntents = this._root.data.OutputIntents || [];
|
|
6147
|
+
},
|
|
6148
|
+
outputIntent(type, s, info, outputConditionIdentifier, destOutputProfileLabel) {
|
|
6149
|
+
const profile = this._colorProfiles[destOutputProfileLabel];
|
|
6150
|
+
if (profile) {
|
|
6151
|
+
throw Error('Invalid color profile label, the profile should be set first');
|
|
6152
|
+
}
|
|
6153
|
+
const intentRef = this.ref({
|
|
6154
|
+
Type: type,
|
|
6155
|
+
S: s,
|
|
6156
|
+
Info: info,
|
|
6157
|
+
OutputConditionIdentifier: outputConditionIdentifier,
|
|
6158
|
+
DestOutputProfile: profile.ref
|
|
6159
|
+
});
|
|
6160
|
+
intentRef.end();
|
|
6161
|
+
this._root.data.OutputIntents.push(intentRef);
|
|
6162
|
+
return this;
|
|
6163
|
+
}
|
|
6164
|
+
};
|
|
6165
|
+
|
|
5813
6166
|
class PDFDocument extends stream.Readable {
|
|
5814
6167
|
constructor() {
|
|
5815
6168
|
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
@@ -5856,8 +6209,13 @@ class PDFDocument extends stream.Readable {
|
|
|
5856
6209
|
if (this.options.lang) {
|
|
5857
6210
|
this._root.data.Lang = new String(this.options.lang);
|
|
5858
6211
|
}
|
|
6212
|
+
if (this.options.pageLayout) {
|
|
6213
|
+
const layout = this.options.pageLayout;
|
|
6214
|
+
this._root.data.PageLayout = layout.charAt(0).toUpperCase() + layout.slice(1);
|
|
6215
|
+
}
|
|
5859
6216
|
this.page = null;
|
|
5860
6217
|
this.initMetadata();
|
|
6218
|
+
this.initColorSpace();
|
|
5861
6219
|
this.initColor();
|
|
5862
6220
|
this.initVector();
|
|
5863
6221
|
this.initFonts(options.font);
|
|
@@ -5867,6 +6225,7 @@ class PDFDocument extends stream.Readable {
|
|
|
5867
6225
|
this.initMarkings(options);
|
|
5868
6226
|
this.initTables();
|
|
5869
6227
|
this.initSubset(options);
|
|
6228
|
+
this.initOutputIntent();
|
|
5870
6229
|
this.info = {
|
|
5871
6230
|
Producer: 'PDFKit',
|
|
5872
6231
|
Creator: 'PDFKit',
|
|
@@ -6068,6 +6427,7 @@ const mixin = methods => {
|
|
|
6068
6427
|
Object.assign(PDFDocument.prototype, methods);
|
|
6069
6428
|
};
|
|
6070
6429
|
mixin(MetadataMixin);
|
|
6430
|
+
mixin(ColorSpaceMixin);
|
|
6071
6431
|
mixin(ColorMixin);
|
|
6072
6432
|
mixin(VectorMixin);
|
|
6073
6433
|
mixin(FontsMixin);
|
|
@@ -6080,6 +6440,7 @@ mixin(AcroFormMixin);
|
|
|
6080
6440
|
mixin(AttachmentsMixin);
|
|
6081
6441
|
mixin(SubsetMixin);
|
|
6082
6442
|
mixin(TableMixin);
|
|
6443
|
+
mixin(OutputIntent);
|
|
6083
6444
|
PDFDocument.LineWrapper = LineWrapper;
|
|
6084
6445
|
|
|
6085
6446
|
export { PDFDocument as default };
|