@signiphi/page-assembly 0.2.0-beta.7 → 0.2.0-beta.9

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/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@ import * as React7 from 'react';
2
2
  import { forwardRef, useReducer, useState, useRef, useMemo, useEffect, useCallback, useImperativeHandle } from 'react';
3
3
  import { DndProvider, useDrop, useDrag } from 'react-dnd';
4
4
  import { HTML5Backend } from 'react-dnd-html5-backend';
5
- import { ChevronDown, ChevronUp, Check, ChevronRight, Circle, Upload, FolderOpen, FileText, Image as Image$1, Trash2, X, MoreHorizontal, GripVertical } from 'lucide-react';
5
+ import { ChevronDown, ChevronUp, Check, ChevronRight, Circle, Upload, X, FolderOpen, FileText, Image as Image$1, Trash2, MoreHorizontal, GripVertical } from 'lucide-react';
6
6
  import { Slot } from '@radix-ui/react-slot';
7
7
  import { cva } from 'class-variance-authority';
8
8
  import { clsx } from 'clsx';
@@ -14,9 +14,11 @@ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
14
14
  import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
15
15
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
16
16
  import * as pdfjsLib from 'pdfjs-dist';
17
- import { PDFDocument, PDFTextField, PDFCheckBox, PDFDropdown, PDFRadioGroup, StandardFonts, rgb, PDFName, PDFString } from 'pdf-lib';
17
+ import { PDFDocument, PDFTextField, PDFCheckBox, PDFDropdown, PDFRadioGroup, StandardFonts, rgb, PDFName, PDFString, PDFDict, PDFArray, PDFHexString, PDFRawStream, PDFRef, PDFBool, PDFNumber, EncryptedPDFError } from 'pdf-lib';
18
18
 
19
- // src/components/PageAssembler.tsx
19
+ var __defProp = Object.defineProperty;
20
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
21
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
20
22
  function cn(...inputs) {
21
23
  return twMerge(clsx(inputs));
22
24
  }
@@ -547,6 +549,528 @@ var TooltipContent = React7.forwardRef(({ className, sideOffset = 4, ...props },
547
549
  }
548
550
  ));
549
551
  TooltipContent.displayName = TooltipPrimitive.Content.displayName;
552
+ var PdfPasswordRequiredError = class extends Error {
553
+ constructor() {
554
+ const message = "This PDF is password-protected. Please remove the password and upload it again.";
555
+ super(message);
556
+ __publicField(this, "code", "PDF_PASSWORD_REQUIRED");
557
+ __publicField(this, "recoverable", true);
558
+ __publicField(this, "userMessage");
559
+ this.name = "PdfPasswordRequiredError";
560
+ this.userMessage = message;
561
+ }
562
+ };
563
+ var UnsupportedPdfEncryptionError = class extends Error {
564
+ constructor(detail) {
565
+ super(`This PDF uses an unsupported encryption scheme (${detail}).`);
566
+ this.name = "UnsupportedPdfEncryptionError";
567
+ }
568
+ };
569
+ function concatBytes(...parts) {
570
+ let total = 0;
571
+ for (const p of parts) total += p.length;
572
+ const out = new Uint8Array(total);
573
+ let offset = 0;
574
+ for (const p of parts) {
575
+ out.set(p, offset);
576
+ offset += p.length;
577
+ }
578
+ return out;
579
+ }
580
+ var MD5_S = [
581
+ 7,
582
+ 12,
583
+ 17,
584
+ 22,
585
+ 7,
586
+ 12,
587
+ 17,
588
+ 22,
589
+ 7,
590
+ 12,
591
+ 17,
592
+ 22,
593
+ 7,
594
+ 12,
595
+ 17,
596
+ 22,
597
+ 5,
598
+ 9,
599
+ 14,
600
+ 20,
601
+ 5,
602
+ 9,
603
+ 14,
604
+ 20,
605
+ 5,
606
+ 9,
607
+ 14,
608
+ 20,
609
+ 5,
610
+ 9,
611
+ 14,
612
+ 20,
613
+ 4,
614
+ 11,
615
+ 16,
616
+ 23,
617
+ 4,
618
+ 11,
619
+ 16,
620
+ 23,
621
+ 4,
622
+ 11,
623
+ 16,
624
+ 23,
625
+ 4,
626
+ 11,
627
+ 16,
628
+ 23,
629
+ 6,
630
+ 10,
631
+ 15,
632
+ 21,
633
+ 6,
634
+ 10,
635
+ 15,
636
+ 21,
637
+ 6,
638
+ 10,
639
+ 15,
640
+ 21,
641
+ 6,
642
+ 10,
643
+ 15,
644
+ 21
645
+ ];
646
+ var MD5_K = (() => {
647
+ const k = new Uint32Array(64);
648
+ for (let i = 0; i < 64; i++) k[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 4294967296);
649
+ return k;
650
+ })();
651
+ function md5(input) {
652
+ const bitLen = input.length * 8;
653
+ const paddedLen = (input.length + 8 >> 6) + 1 << 6;
654
+ const buf = new Uint8Array(paddedLen);
655
+ buf.set(input);
656
+ buf[input.length] = 128;
657
+ const dv = new DataView(buf.buffer);
658
+ dv.setUint32(paddedLen - 8, bitLen >>> 0, true);
659
+ dv.setUint32(paddedLen - 4, Math.floor(bitLen / 4294967296), true);
660
+ let a0 = 1732584193;
661
+ let b0 = 4023233417;
662
+ let c0 = 2562383102;
663
+ let d0 = 271733878;
664
+ const M = new Uint32Array(16);
665
+ for (let offset = 0; offset < paddedLen; offset += 64) {
666
+ for (let i = 0; i < 16; i++) M[i] = dv.getUint32(offset + i * 4, true);
667
+ let A = a0;
668
+ let B = b0;
669
+ let C = c0;
670
+ let D = d0;
671
+ for (let i = 0; i < 64; i++) {
672
+ let F;
673
+ let g;
674
+ if (i < 16) {
675
+ F = B & C | ~B & D;
676
+ g = i;
677
+ } else if (i < 32) {
678
+ F = D & B | ~D & C;
679
+ g = 5 * i + 1 & 15;
680
+ } else if (i < 48) {
681
+ F = B ^ C ^ D;
682
+ g = 3 * i + 5 & 15;
683
+ } else {
684
+ F = C ^ (B | ~D);
685
+ g = 7 * i & 15;
686
+ }
687
+ const tmp = D;
688
+ D = C;
689
+ C = B;
690
+ const x = A + F + MD5_K[i] + M[g] | 0;
691
+ const s = MD5_S[i];
692
+ B = B + (x << s | x >>> 32 - s) | 0;
693
+ A = tmp;
694
+ }
695
+ a0 = a0 + A | 0;
696
+ b0 = b0 + B | 0;
697
+ c0 = c0 + C | 0;
698
+ d0 = d0 + D | 0;
699
+ }
700
+ const out = new Uint8Array(16);
701
+ const ov = new DataView(out.buffer);
702
+ ov.setUint32(0, a0 >>> 0, true);
703
+ ov.setUint32(4, b0 >>> 0, true);
704
+ ov.setUint32(8, c0 >>> 0, true);
705
+ ov.setUint32(12, d0 >>> 0, true);
706
+ return out;
707
+ }
708
+ function rc4(key, data) {
709
+ const S = new Uint8Array(256);
710
+ for (let i2 = 0; i2 < 256; i2++) S[i2] = i2;
711
+ let j = 0;
712
+ for (let i2 = 0; i2 < 256; i2++) {
713
+ j = j + S[i2] + key[i2 % key.length] & 255;
714
+ const t = S[i2];
715
+ S[i2] = S[j];
716
+ S[j] = t;
717
+ }
718
+ const out = new Uint8Array(data.length);
719
+ let i = 0;
720
+ j = 0;
721
+ for (let k = 0; k < data.length; k++) {
722
+ i = i + 1 & 255;
723
+ j = j + S[i] & 255;
724
+ const t = S[i];
725
+ S[i] = S[j];
726
+ S[j] = t;
727
+ out[k] = data[k] ^ S[S[i] + S[j] & 255];
728
+ }
729
+ return out;
730
+ }
731
+ function getSubtle() {
732
+ const subtle = globalThis.crypto?.subtle;
733
+ if (!subtle) throw new UnsupportedPdfEncryptionError("WebCrypto is unavailable in this environment");
734
+ return subtle;
735
+ }
736
+ async function sha2(algorithm, data) {
737
+ return new Uint8Array(await getSubtle().digest(algorithm, data));
738
+ }
739
+ async function importAesKey(rawKey, usages) {
740
+ return getSubtle().importKey("raw", rawKey, "AES-CBC", false, usages);
741
+ }
742
+ async function aesCbcEncryptNoPad(rawKey, iv, data) {
743
+ const key = await importAesKey(rawKey, ["encrypt"]);
744
+ const out = new Uint8Array(await getSubtle().encrypt({ name: "AES-CBC", iv }, key, data));
745
+ return out.slice(0, data.length);
746
+ }
747
+ async function aesCbcDecryptNoPad(rawKey, iv, data) {
748
+ const key = await importAesKey(rawKey, ["encrypt", "decrypt"]);
749
+ const lastBlock = data.slice(data.length - 16);
750
+ const padBlock = new Uint8Array(
751
+ await getSubtle().encrypt({ name: "AES-CBC", iv: lastBlock }, key, new Uint8Array(0))
752
+ );
753
+ const joined = concatBytes(data, padBlock);
754
+ return new Uint8Array(await getSubtle().decrypt({ name: "AES-CBC", iv }, key, joined));
755
+ }
756
+ async function aesDecryptPdfData(rawKey, data) {
757
+ if (data.length < 32) return new Uint8Array(0);
758
+ const iv = data.slice(0, 16);
759
+ let ct = data.slice(16);
760
+ if (ct.length % 16 !== 0) ct = ct.slice(0, ct.length - ct.length % 16);
761
+ try {
762
+ const key = await importAesKey(rawKey, ["decrypt"]);
763
+ return new Uint8Array(await getSubtle().decrypt({ name: "AES-CBC", iv }, key, ct));
764
+ } catch {
765
+ const plain = await aesCbcDecryptNoPad(rawKey, iv, ct);
766
+ const padLen = plain[plain.length - 1];
767
+ return padLen >= 1 && padLen <= 16 ? plain.slice(0, plain.length - padLen) : plain;
768
+ }
769
+ }
770
+ var PAD = new Uint8Array([
771
+ 40,
772
+ 191,
773
+ 78,
774
+ 94,
775
+ 78,
776
+ 117,
777
+ 138,
778
+ 65,
779
+ 100,
780
+ 0,
781
+ 78,
782
+ 86,
783
+ 255,
784
+ 250,
785
+ 1,
786
+ 8,
787
+ 46,
788
+ 46,
789
+ 0,
790
+ 182,
791
+ 208,
792
+ 104,
793
+ 62,
794
+ 128,
795
+ 47,
796
+ 12,
797
+ 169,
798
+ 254,
799
+ 100,
800
+ 83,
801
+ 105,
802
+ 122
803
+ ]);
804
+ var bytesEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
805
+ function legacyFileKey(paddedPwd, params) {
806
+ const { revision, keyLen, O, P, id0, encryptMetadata } = params;
807
+ const pBytes = new Uint8Array(4);
808
+ new DataView(pBytes.buffer).setInt32(0, P | 0, true);
809
+ const parts = [paddedPwd, O.slice(0, 32), pBytes, id0];
810
+ if (revision >= 4 && !encryptMetadata) parts.push(new Uint8Array([255, 255, 255, 255]));
811
+ let h = md5(concatBytes(...parts));
812
+ if (revision >= 3) for (let i = 0; i < 50; i++) h = md5(h.slice(0, keyLen));
813
+ return h.slice(0, keyLen);
814
+ }
815
+ function legacyComputeU(fileKey, revision, id0) {
816
+ if (revision === 2) return rc4(fileKey, PAD);
817
+ let d = md5(concatBytes(PAD, id0));
818
+ d = rc4(fileKey, d);
819
+ for (let i = 1; i <= 19; i++) d = rc4(fileKey.map((b) => b ^ i), d);
820
+ return d;
821
+ }
822
+ function legacyUMatches(fileKey, params) {
823
+ const computed = legacyComputeU(fileKey, params.revision, params.id0);
824
+ return params.revision === 2 ? bytesEqual(computed, params.U.slice(0, 32)) : bytesEqual(computed.slice(0, 16), params.U.slice(0, 16));
825
+ }
826
+ function legacyAuthenticateEmptyPassword(params) {
827
+ const userKey = legacyFileKey(PAD, params);
828
+ if (legacyUMatches(userKey, params)) return userKey;
829
+ const { revision, keyLen } = params;
830
+ let h = md5(PAD);
831
+ if (revision >= 3) for (let i = 0; i < 50; i++) h = md5(h);
832
+ const ownerKey = h.slice(0, keyLen);
833
+ let recovered = params.O.slice(0, 32);
834
+ if (revision === 2) {
835
+ recovered = rc4(ownerKey, recovered);
836
+ } else {
837
+ for (let i = 19; i >= 0; i--) recovered = rc4(ownerKey.map((b) => b ^ i), recovered);
838
+ }
839
+ const viaOwner = legacyFileKey(recovered, params);
840
+ if (legacyUMatches(viaOwner, params)) return viaOwner;
841
+ throw new PdfPasswordRequiredError();
842
+ }
843
+ async function hash2B(password, salt, udata) {
844
+ let k = await sha2("SHA-256", concatBytes(password, salt, udata));
845
+ let e = new Uint8Array(0);
846
+ let i = 0;
847
+ do {
848
+ const block = concatBytes(password, k, udata);
849
+ const k1 = new Uint8Array(block.length * 64);
850
+ for (let rep = 0; rep < 64; rep++) k1.set(block, rep * block.length);
851
+ e = await aesCbcEncryptNoPad(k.slice(0, 16), k.slice(16, 32), k1);
852
+ let sum = 0;
853
+ for (let b = 0; b < 16; b++) sum += e[b];
854
+ const mod = sum % 3;
855
+ k = await sha2(mod === 0 ? "SHA-256" : mod === 1 ? "SHA-384" : "SHA-512", e);
856
+ i++;
857
+ } while (i < 64 || e[e.length - 1] > i - 32);
858
+ return k.slice(0, 32);
859
+ }
860
+ async function v5AuthenticateEmptyPassword(revision, U, O, UE, OE) {
861
+ const pwd = new Uint8Array(0);
862
+ const zeroIv = new Uint8Array(16);
863
+ const hash = async (salt, udata) => revision === 5 ? sha2("SHA-256", concatBytes(pwd, salt, udata)) : hash2B(pwd, salt, udata);
864
+ const userHash = await hash(U.slice(32, 40), new Uint8Array(0));
865
+ if (bytesEqual(userHash, U.slice(0, 32))) {
866
+ const intermediate = await hash(U.slice(40, 48), new Uint8Array(0));
867
+ return aesCbcDecryptNoPad(intermediate, zeroIv, UE.slice(0, 32));
868
+ }
869
+ const u48 = U.slice(0, 48);
870
+ const ownerHash = await hash(O.slice(32, 40), u48);
871
+ if (bytesEqual(ownerHash, O.slice(0, 32))) {
872
+ const intermediate = await hash(O.slice(40, 48), u48);
873
+ return aesCbcDecryptNoPad(intermediate, zeroIv, OE.slice(0, 32));
874
+ }
875
+ throw new PdfPasswordRequiredError();
876
+ }
877
+ var identityCipher = async (data) => data;
878
+ function rc4ObjectCipher(fileKey) {
879
+ return async (data, ref) => {
880
+ const ext = new Uint8Array([
881
+ ref.objectNumber & 255,
882
+ ref.objectNumber >> 8 & 255,
883
+ ref.objectNumber >> 16 & 255,
884
+ ref.generationNumber & 255,
885
+ ref.generationNumber >> 8 & 255
886
+ ]);
887
+ const key = md5(concatBytes(fileKey, ext)).slice(0, Math.min(fileKey.length + 5, 16));
888
+ return rc4(key, data);
889
+ };
890
+ }
891
+ var AES_SALT = new Uint8Array([115, 65, 108, 84]);
892
+ function aes128ObjectCipher(fileKey) {
893
+ return async (data, ref) => {
894
+ const ext = new Uint8Array([
895
+ ref.objectNumber & 255,
896
+ ref.objectNumber >> 8 & 255,
897
+ ref.objectNumber >> 16 & 255,
898
+ ref.generationNumber & 255,
899
+ ref.generationNumber >> 8 & 255
900
+ ]);
901
+ const key = md5(concatBytes(fileKey, ext, AES_SALT)).slice(0, 16);
902
+ return aesDecryptPdfData(key, data);
903
+ };
904
+ }
905
+ function aes256ObjectCipher(fileKey) {
906
+ return async (data) => aesDecryptPdfData(fileKey, data);
907
+ }
908
+ function dictNumber(dict, key) {
909
+ const value = dict.lookup(PDFName.of(key));
910
+ return value instanceof PDFNumber ? value.asNumber() : void 0;
911
+ }
912
+ function dictBytes(dict, key) {
913
+ const value = dict.lookup(PDFName.of(key));
914
+ return value instanceof PDFString || value instanceof PDFHexString ? value.asBytes() : void 0;
915
+ }
916
+ async function buildDocumentCiphers(encDict, id0) {
917
+ const filter = encDict.lookup(PDFName.of("Filter"));
918
+ if (filter !== PDFName.of("Standard")) {
919
+ throw new UnsupportedPdfEncryptionError(`security handler ${String(filter)}`);
920
+ }
921
+ const V = dictNumber(encDict, "V") ?? 0;
922
+ const R = dictNumber(encDict, "R") ?? 0;
923
+ const O = dictBytes(encDict, "O");
924
+ const U = dictBytes(encDict, "U");
925
+ const P = dictNumber(encDict, "P") ?? -1;
926
+ const metadataValue = encDict.lookup(PDFName.of("EncryptMetadata"));
927
+ const encryptMetadata = metadataValue instanceof PDFBool ? metadataValue.asBoolean() : true;
928
+ if (!O || !U) throw new UnsupportedPdfEncryptionError("missing /O or /U");
929
+ if (V === 1 || V === 2) {
930
+ const keyLen = V === 1 ? 5 : (dictNumber(encDict, "Length") ?? 40) / 8;
931
+ const fileKey = legacyAuthenticateEmptyPassword({ revision: R, keyLen, O, U, P, id0, encryptMetadata });
932
+ const cipher = rc4ObjectCipher(fileKey);
933
+ return { stringCipher: cipher, streamCipher: cipher, encryptMetadata };
934
+ }
935
+ if (V === 4 || V === 5) {
936
+ const cf = encDict.lookup(PDFName.of("CF"));
937
+ const cfmOf = (filterKey) => {
938
+ const streamFilter = encDict.lookup(PDFName.of(filterKey));
939
+ if (!(streamFilter instanceof PDFName) || streamFilter === PDFName.of("Identity")) return void 0;
940
+ if (!(cf instanceof PDFDict)) throw new UnsupportedPdfEncryptionError("missing /CF");
941
+ const cfDict = cf.lookup(streamFilter);
942
+ if (!(cfDict instanceof PDFDict)) throw new UnsupportedPdfEncryptionError(`missing crypt filter ${streamFilter.toString()}`);
943
+ const cfm = cfDict.lookup(PDFName.of("CFM"));
944
+ return cfm instanceof PDFName ? cfm : void 0;
945
+ };
946
+ let fileKey;
947
+ if (V === 5) {
948
+ const UE = dictBytes(encDict, "UE");
949
+ const OE = dictBytes(encDict, "OE");
950
+ if (!UE || !OE) throw new UnsupportedPdfEncryptionError("missing /UE or /OE");
951
+ fileKey = await v5AuthenticateEmptyPassword(R, U, O, UE, OE);
952
+ } else {
953
+ const keyLen = (dictNumber(encDict, "Length") ?? 40) / 8;
954
+ fileKey = legacyAuthenticateEmptyPassword({ revision: R, keyLen, O, U, P, id0, encryptMetadata });
955
+ }
956
+ const cipherFor = (cfm) => {
957
+ if (!cfm || cfm === PDFName.of("Identity")) return identityCipher;
958
+ if (cfm === PDFName.of("V2")) return rc4ObjectCipher(fileKey);
959
+ if (cfm === PDFName.of("AESV2")) return aes128ObjectCipher(fileKey);
960
+ if (cfm === PDFName.of("AESV3")) return aes256ObjectCipher(fileKey);
961
+ throw new UnsupportedPdfEncryptionError(`crypt filter method ${cfm.toString()}`);
962
+ };
963
+ return {
964
+ streamCipher: cipherFor(cfmOf("StmF")),
965
+ stringCipher: cipherFor(cfmOf("StrF")),
966
+ encryptMetadata
967
+ };
968
+ }
969
+ throw new UnsupportedPdfEncryptionError(`/V ${V}`);
970
+ }
971
+ var toHexString = (bytes) => {
972
+ let hex = "";
973
+ for (let i = 0; i < bytes.length; i++) hex += bytes[i].toString(16).padStart(2, "0");
974
+ return new PDFHexString(hex.toUpperCase());
975
+ };
976
+ function filterIncludesCrypt(dict) {
977
+ const filter = dict.lookup(PDFName.of("Filter"));
978
+ if (filter === PDFName.of("Crypt")) return true;
979
+ if (filter instanceof PDFArray) {
980
+ for (let i = 0; i < filter.size(); i++) {
981
+ if (filter.get(i) === PDFName.of("Crypt")) return true;
982
+ }
983
+ }
984
+ return false;
985
+ }
986
+ async function decryptStringsWithin(obj, ref, stringCipher) {
987
+ if (obj instanceof PDFDict) {
988
+ const isSignature = obj.lookup(PDFName.of("Type")) === PDFName.of("Sig");
989
+ for (const [key, value] of obj.entries()) {
990
+ if (value instanceof PDFString || value instanceof PDFHexString) {
991
+ if (isSignature && key === PDFName.of("Contents")) continue;
992
+ obj.set(key, toHexString(await stringCipher(value.asBytes(), ref)));
993
+ } else {
994
+ await decryptStringsWithin(value, ref, stringCipher);
995
+ }
996
+ }
997
+ } else if (obj instanceof PDFArray) {
998
+ for (let i = 0; i < obj.size(); i++) {
999
+ const value = obj.get(i);
1000
+ if (value instanceof PDFString || value instanceof PDFHexString) {
1001
+ obj.set(i, toHexString(await stringCipher(value.asBytes(), ref)));
1002
+ } else {
1003
+ await decryptStringsWithin(value, ref, stringCipher);
1004
+ }
1005
+ }
1006
+ }
1007
+ }
1008
+ async function decryptLoadedDocument(doc) {
1009
+ const context = doc.context;
1010
+ const encryptRef = context.trailerInfo.Encrypt;
1011
+ const encDict = context.lookup(encryptRef);
1012
+ if (!(encDict instanceof PDFDict)) {
1013
+ throw new UnsupportedPdfEncryptionError("malformed /Encrypt dictionary");
1014
+ }
1015
+ const idArray = context.lookup(context.trailerInfo.ID);
1016
+ const idFirst = idArray instanceof PDFArray ? idArray.get(0) : void 0;
1017
+ const id0 = idFirst instanceof PDFString || idFirst instanceof PDFHexString ? idFirst.asBytes() : new Uint8Array(0);
1018
+ const { stringCipher, streamCipher, encryptMetadata } = await buildDocumentCiphers(encDict, id0);
1019
+ for (const [ref, obj] of context.enumerateIndirectObjects()) {
1020
+ if (ref === encryptRef || obj === encDict) continue;
1021
+ if (obj instanceof PDFRawStream) {
1022
+ const type = obj.dict.lookup(PDFName.of("Type"));
1023
+ const isXrefStream = type === PDFName.of("XRef");
1024
+ const isSkippedMetadata = !encryptMetadata && type === PDFName.of("Metadata");
1025
+ if (!isXrefStream && !isSkippedMetadata && !filterIncludesCrypt(obj.dict)) {
1026
+ const decrypted = await streamCipher(obj.contents, ref);
1027
+ context.assign(ref, PDFRawStream.of(obj.dict, decrypted));
1028
+ }
1029
+ await decryptStringsWithin(obj.dict, ref, stringCipher);
1030
+ } else if (obj instanceof PDFString || obj instanceof PDFHexString) {
1031
+ context.assign(ref, toHexString(await stringCipher(obj.asBytes(), ref)));
1032
+ } else {
1033
+ await decryptStringsWithin(obj, ref, stringCipher);
1034
+ }
1035
+ }
1036
+ delete context.trailerInfo.Encrypt;
1037
+ if (encryptRef instanceof PDFRef) context.delete(encryptRef);
1038
+ return doc.save({ useObjectStreams: false });
1039
+ }
1040
+ async function decryptPdf(pdfBytes) {
1041
+ const doc = await PDFDocument.load(pdfBytes.slice(), {
1042
+ ignoreEncryption: true,
1043
+ updateMetadata: false
1044
+ });
1045
+ if (!doc.isEncrypted) return pdfBytes;
1046
+ return decryptLoadedDocument(doc);
1047
+ }
1048
+ var ENCRYPT_MARKER = new Uint8Array([47, 69, 110, 99, 114, 121, 112, 116]);
1049
+ function containsEncryptMarker(bytes) {
1050
+ const last = bytes.length - ENCRYPT_MARKER.length;
1051
+ outer: for (let i = 0; i <= last; i++) {
1052
+ for (let j = 0; j < ENCRYPT_MARKER.length; j++) {
1053
+ if (bytes[i + j] !== ENCRYPT_MARKER[j]) continue outer;
1054
+ }
1055
+ return true;
1056
+ }
1057
+ return false;
1058
+ }
1059
+ function isEncryptedPdfError(error) {
1060
+ return error instanceof EncryptedPDFError || error instanceof Error && error.message.includes("is encrypted");
1061
+ }
1062
+ async function normalizePdfEncryption(pdfBytes) {
1063
+ if (!containsEncryptMarker(pdfBytes)) return pdfBytes;
1064
+ const doc = await PDFDocument.load(pdfBytes.slice(), {
1065
+ ignoreEncryption: true,
1066
+ updateMetadata: false
1067
+ });
1068
+ if (!doc.isEncrypted) return pdfBytes;
1069
+ console.info("[PageAssembler] Encrypted PDF detected during ingestion; decrypting");
1070
+ return decryptLoadedDocument(doc);
1071
+ }
1072
+
1073
+ // src/utils/pdf-helpers.ts
550
1074
  if (typeof window !== "undefined") {
551
1075
  pdfjsLib.GlobalWorkerOptions.workerSrc = "/pdfjs/build/pdf.worker.mjs";
552
1076
  }
@@ -618,6 +1142,53 @@ async function pdfToImages(pdfBytes, options) {
618
1142
  throw error;
619
1143
  }
620
1144
  }
1145
+ async function rasterizePdfToPdf(pdfBytes) {
1146
+ if (typeof window !== "undefined") {
1147
+ pdfjsLib.GlobalWorkerOptions.workerSrc = "/pdfjs/build/pdf.worker.mjs";
1148
+ }
1149
+ let sourceDoc;
1150
+ try {
1151
+ sourceDoc = await pdfjsLib.getDocument({ data: pdfBytes.slice(0), verbosity: 0 }).promise;
1152
+ } catch (error) {
1153
+ if (error?.name === "PasswordException") {
1154
+ throw new PdfPasswordRequiredError();
1155
+ }
1156
+ throw error;
1157
+ }
1158
+ try {
1159
+ const outDoc = await PDFDocument.create();
1160
+ for (let pageNum = 1; pageNum <= sourceDoc.numPages; pageNum++) {
1161
+ const page = await sourceDoc.getPage(pageNum);
1162
+ const viewport = page.getViewport({ scale: 2 });
1163
+ const canvas = document.createElement("canvas");
1164
+ canvas.width = viewport.width;
1165
+ canvas.height = viewport.height;
1166
+ const context = canvas.getContext("2d");
1167
+ await page.render({ canvasContext: context, viewport }).promise;
1168
+ const png = await outDoc.embedPng(canvas.toDataURL("image/png"));
1169
+ const pageWidth = viewport.width / 2;
1170
+ const pageHeight = viewport.height / 2;
1171
+ const outPage = outDoc.addPage([pageWidth, pageHeight]);
1172
+ outPage.drawImage(png, { x: 0, y: 0, width: pageWidth, height: pageHeight });
1173
+ }
1174
+ return new Uint8Array(await outDoc.save({ useObjectStreams: false }));
1175
+ } finally {
1176
+ await sourceDoc.destroy();
1177
+ }
1178
+ }
1179
+ async function normalizeUploadedPdfBytes(pdfBytes) {
1180
+ try {
1181
+ const normalized = await normalizePdfEncryption(pdfBytes);
1182
+ return normalized === pdfBytes ? pdfBytes : new Uint8Array(normalized);
1183
+ } catch (error) {
1184
+ if (error instanceof PdfPasswordRequiredError) throw error;
1185
+ console.warn(
1186
+ "[PageAssembler] Structural decryption failed; rebuilding document from rendered pages:",
1187
+ error
1188
+ );
1189
+ return rasterizePdfToPdf(pdfBytes);
1190
+ }
1191
+ }
621
1192
  async function imageToPdf(imageFile) {
622
1193
  return new Promise((resolve, reject) => {
623
1194
  const img = new Image();
@@ -1901,6 +2472,7 @@ var PageAssembler = forwardRef(
1901
2472
  );
1902
2473
  const [isLoading, setIsLoading] = useState(false);
1903
2474
  const [processingStatus, setProcessingStatus] = useState("");
2475
+ const [uploadError, setUploadError] = useState(null);
1904
2476
  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
1905
2477
  const [showRemoveFileConfirm, setShowRemoveFileConfirm] = useState(false);
1906
2478
  const [fileToRemove, setFileToRemove] = useState(null);
@@ -1942,6 +2514,7 @@ var PageAssembler = forwardRef(
1942
2514
  } else {
1943
2515
  pdfBytes = new Uint8Array(pdf);
1944
2516
  }
2517
+ pdfBytes = await normalizeUploadedPdfBytes(pdfBytes);
1945
2518
  const defensiveCopy = new Uint8Array(pdfBytes);
1946
2519
  const pages = await pdfToImages(defensiveCopy, { hideFormFields: true });
1947
2520
  if (pages.length === 0) {
@@ -1987,8 +2560,9 @@ var PageAssembler = forwardRef(
1987
2560
  setProcessingStatus("");
1988
2561
  }
1989
2562
  }, []);
2563
+ const hasRestorableState = !!initialState && initialState.files.length > 0;
1990
2564
  useEffect(() => {
1991
- if (initialPdf && !initialState) {
2565
+ if (initialPdf && state.files.length === 0 && !hasRestorableState) {
1992
2566
  const pdfIdentifier = initialPdf instanceof Uint8Array ? `uint8array-${initialPdf.length}-${initialPdf[0]}-${initialPdf[initialPdf.length - 1]}` : `${initialPdf.constructor.name}-${initialPdf.size || initialPdf.byteLength}`;
1993
2567
  if (processedInitialPdfRef.current === pdfIdentifier) {
1994
2568
  return;
@@ -1996,15 +2570,16 @@ var PageAssembler = forwardRef(
1996
2570
  processedInitialPdfRef.current = pdfIdentifier;
1997
2571
  processInitialPdf(initialPdf);
1998
2572
  }
1999
- }, [initialPdf, initialState, processInitialPdf]);
2573
+ }, [initialPdf, state.files.length, hasRestorableState, processInitialPdf]);
2000
2574
  useEffect(() => {
2001
- if (initialState) {
2575
+ if (initialState && initialState.files.length > 0) {
2002
2576
  dispatch({ type: "RESET", payload: initialState });
2003
2577
  }
2004
2578
  }, [initialState]);
2005
2579
  const processFiles = useCallback(async (files) => {
2006
2580
  const fileArray = Array.from(files);
2007
2581
  const maxFileSize = (limits?.maxFileSizeMB || 10) * 1024 * 1024;
2582
+ setUploadError(null);
2008
2583
  setIsLoading(true);
2009
2584
  setProcessingStatus(`Processing ${fileArray.length} file${fileArray.length !== 1 ? "s" : ""}...`);
2010
2585
  for (const file of fileArray) {
@@ -2029,7 +2604,7 @@ var PageAssembler = forwardRef(
2029
2604
  if (file.type === "application/pdf") {
2030
2605
  setProcessingStatus(`Processing PDF: ${file.name}`);
2031
2606
  const arrayBuffer = await file.arrayBuffer();
2032
- const pdfBytes = new Uint8Array(arrayBuffer);
2607
+ const pdfBytes = await normalizeUploadedPdfBytes(new Uint8Array(arrayBuffer));
2033
2608
  fileInfo.originalBytes = new Uint8Array(pdfBytes);
2034
2609
  originalBytesCache.current.set(fileInfo.id, fileInfo.originalBytes);
2035
2610
  const defensiveCopy = new Uint8Array(pdfBytes);
@@ -2079,6 +2654,11 @@ var PageAssembler = forwardRef(
2079
2654
  dispatch({ type: "ADD_FILE", payload: fileInfo });
2080
2655
  } catch (error) {
2081
2656
  console.error(`Error processing file ${file.name}:`, error);
2657
+ if (error instanceof PdfPasswordRequiredError) {
2658
+ setUploadError(`${file.name}: ${error.userMessage}`);
2659
+ } else {
2660
+ setUploadError(`Failed to process "${file.name}". The file may be corrupted or unsupported.`);
2661
+ }
2082
2662
  }
2083
2663
  }
2084
2664
  setIsLoading(false);
@@ -2176,7 +2756,7 @@ var PageAssembler = forwardRef(
2176
2756
  setIsLoading(true);
2177
2757
  setProcessingStatus("Assembling PDF...");
2178
2758
  try {
2179
- const { PDFDocument: PDFDocument4, PDFName: PDFName3, PDFString: PDFString3 } = await import('pdf-lib');
2759
+ const { PDFDocument: PDFDocument5, PDFName: PDFName4, PDFString: PDFString4 } = await import('pdf-lib');
2180
2760
  const loadedPdfs = /* @__PURE__ */ new Map();
2181
2761
  const fileMap = /* @__PURE__ */ new Map();
2182
2762
  const pdfFiles = state.files.filter((f) => f.type === "pdf" && f.originalBytes?.length > 0);
@@ -2186,7 +2766,7 @@ var PageAssembler = forwardRef(
2186
2766
  fileMap.set(file.id, file);
2187
2767
  if (file.type === "pdf" && file.originalBytes && file.originalBytes.length > 0) {
2188
2768
  try {
2189
- const sourcePdf = await PDFDocument4.load(file.originalBytes);
2769
+ const sourcePdf = await PDFDocument5.load(file.originalBytes);
2190
2770
  loadedPdfs.set(file.id, sourcePdf);
2191
2771
  } catch (error) {
2192
2772
  console.error(`Error loading PDF for file ${file.id}:`, error);
@@ -2207,7 +2787,7 @@ var PageAssembler = forwardRef(
2207
2787
  if (cachedBytes && cachedBytes.length > 0) {
2208
2788
  try {
2209
2789
  console.log(`[PageAssembler] Loading PDF from cache for deleted file ${fileId}`);
2210
- const sourcePdf = await PDFDocument4.load(cachedBytes);
2790
+ const sourcePdf = await PDFDocument5.load(cachedBytes);
2211
2791
  loadedPdfs.set(fileId, sourcePdf);
2212
2792
  } catch (error) {
2213
2793
  console.error(`Error loading cached PDF for file ${fileId}:`, error);
@@ -2291,7 +2871,7 @@ var PageAssembler = forwardRef(
2291
2871
  if (sourceInfoRef) {
2292
2872
  const sourceInfoDict = sourcePdf.context.lookup(sourceInfoRef);
2293
2873
  if (sourceInfoDict && typeof sourceInfoDict.get === "function") {
2294
- const metadataObj = sourceInfoDict.get(PDFName3.of("SigniphiMetadata"));
2874
+ const metadataObj = sourceInfoDict.get(PDFName4.of("SigniphiMetadata"));
2295
2875
  if (metadataObj) {
2296
2876
  const metadataStr = metadataObj.toString();
2297
2877
  let jsonStr = metadataStr;
@@ -2374,7 +2954,7 @@ var PageAssembler = forwardRef(
2374
2954
  }
2375
2955
  console.log(`[PageAssembler] Total extracted fields: ${allExtractedFields.length}`);
2376
2956
  console.log(`[PageAssembler] Field names:`, allExtractedFields.map((f) => `${f.name} (page ${f.position.page})`));
2377
- const combinedDoc = await PDFDocument4.create();
2957
+ const combinedDoc = await PDFDocument5.create();
2378
2958
  for (const file of state.files) {
2379
2959
  if (file.pages.length === 0) continue;
2380
2960
  for (const page of file.pages) {
@@ -2396,9 +2976,9 @@ var PageAssembler = forwardRef(
2396
2976
  const copiedPages = await combinedDoc.copyPages(sourcePdf, [page.srcIndex]);
2397
2977
  copiedPages.forEach((copiedPage) => {
2398
2978
  try {
2399
- const annotsRef = copiedPage.node.get(PDFName3.of("Annots"));
2979
+ const annotsRef = copiedPage.node.get(PDFName4.of("Annots"));
2400
2980
  if (annotsRef) {
2401
- copiedPage.node.delete(PDFName3.of("Annots"));
2981
+ copiedPage.node.delete(PDFName4.of("Annots"));
2402
2982
  }
2403
2983
  } catch (annotError) {
2404
2984
  console.warn("Could not remove annotations from copied page:", annotError);
@@ -2413,7 +2993,7 @@ var PageAssembler = forwardRef(
2413
2993
  } else if (sourceFile.type === "image" && sourceFile.originalFile) {
2414
2994
  try {
2415
2995
  const imagePdfBytes = await imageToPdf(sourceFile.originalFile);
2416
- const imagePdf = await PDFDocument4.load(imagePdfBytes);
2996
+ const imagePdf = await PDFDocument5.load(imagePdfBytes);
2417
2997
  const copiedPages = await combinedDoc.copyPages(imagePdf, [0]);
2418
2998
  copiedPages.forEach((copiedPage) => combinedDoc.addPage(copiedPage));
2419
2999
  } catch (imageError) {
@@ -2430,8 +3010,8 @@ var PageAssembler = forwardRef(
2430
3010
  const newInfoDict = combinedDoc.context.lookup(newInfoRef);
2431
3011
  if (newInfoDict && typeof newInfoDict.set === "function") {
2432
3012
  newInfoDict.set(
2433
- PDFName3.of("SigniphiMetadata"),
2434
- PDFString3.of(JSON.stringify(mergedMetadata))
3013
+ PDFName4.of("SigniphiMetadata"),
3014
+ PDFString4.of(JSON.stringify(mergedMetadata))
2435
3015
  );
2436
3016
  }
2437
3017
  }
@@ -2517,6 +3097,10 @@ var PageAssembler = forwardRef(
2517
3097
  /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2 border-primary" }),
2518
3098
  /* @__PURE__ */ jsx("span", { className: "text-sm", children: processingStatus })
2519
3099
  ] }) }),
3100
+ uploadError && !isLoading && /* @__PURE__ */ jsx(Card, { className: "p-4 border-destructive/50 bg-destructive/5", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3101
+ /* @__PURE__ */ jsx(X, { className: "h-4 w-4 text-destructive flex-shrink-0" }),
3102
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-destructive", children: uploadError })
3103
+ ] }) }),
2520
3104
  /* @__PURE__ */ jsx(
2521
3105
  BulkActionBar,
2522
3106
  {
@@ -2652,6 +3236,6 @@ function debounce(func, wait) {
2652
3236
  });
2653
3237
  }
2654
3238
 
2655
- export { Button, FormFieldType, PageAssembler, addFormFieldsToPdf, createPdfBlobUrl, downloadPdf, extractFieldsFromPdf, getSigniphiMetadata, imageToPdf, mapFieldPositionsAfterAssembly, pdfToImages, setSigniphiMetadata };
3239
+ export { Button, FormFieldType, PageAssembler, PdfPasswordRequiredError, UnsupportedPdfEncryptionError, addFormFieldsToPdf, createPdfBlobUrl, decryptPdf, downloadPdf, extractFieldsFromPdf, getSigniphiMetadata, imageToPdf, isEncryptedPdfError, mapFieldPositionsAfterAssembly, normalizePdfEncryption, normalizeUploadedPdfBytes, pdfToImages, rasterizePdfToPdf, setSigniphiMetadata };
2656
3240
  //# sourceMappingURL=index.mjs.map
2657
3241
  //# sourceMappingURL=index.mjs.map