@signiphi/page-assembly 0.2.0-beta.8 → 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) {
@@ -2006,6 +2579,7 @@ var PageAssembler = forwardRef(
2006
2579
  const processFiles = useCallback(async (files) => {
2007
2580
  const fileArray = Array.from(files);
2008
2581
  const maxFileSize = (limits?.maxFileSizeMB || 10) * 1024 * 1024;
2582
+ setUploadError(null);
2009
2583
  setIsLoading(true);
2010
2584
  setProcessingStatus(`Processing ${fileArray.length} file${fileArray.length !== 1 ? "s" : ""}...`);
2011
2585
  for (const file of fileArray) {
@@ -2030,7 +2604,7 @@ var PageAssembler = forwardRef(
2030
2604
  if (file.type === "application/pdf") {
2031
2605
  setProcessingStatus(`Processing PDF: ${file.name}`);
2032
2606
  const arrayBuffer = await file.arrayBuffer();
2033
- const pdfBytes = new Uint8Array(arrayBuffer);
2607
+ const pdfBytes = await normalizeUploadedPdfBytes(new Uint8Array(arrayBuffer));
2034
2608
  fileInfo.originalBytes = new Uint8Array(pdfBytes);
2035
2609
  originalBytesCache.current.set(fileInfo.id, fileInfo.originalBytes);
2036
2610
  const defensiveCopy = new Uint8Array(pdfBytes);
@@ -2080,6 +2654,11 @@ var PageAssembler = forwardRef(
2080
2654
  dispatch({ type: "ADD_FILE", payload: fileInfo });
2081
2655
  } catch (error) {
2082
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
+ }
2083
2662
  }
2084
2663
  }
2085
2664
  setIsLoading(false);
@@ -2177,7 +2756,7 @@ var PageAssembler = forwardRef(
2177
2756
  setIsLoading(true);
2178
2757
  setProcessingStatus("Assembling PDF...");
2179
2758
  try {
2180
- const { PDFDocument: PDFDocument4, PDFName: PDFName3, PDFString: PDFString3 } = await import('pdf-lib');
2759
+ const { PDFDocument: PDFDocument5, PDFName: PDFName4, PDFString: PDFString4 } = await import('pdf-lib');
2181
2760
  const loadedPdfs = /* @__PURE__ */ new Map();
2182
2761
  const fileMap = /* @__PURE__ */ new Map();
2183
2762
  const pdfFiles = state.files.filter((f) => f.type === "pdf" && f.originalBytes?.length > 0);
@@ -2187,7 +2766,7 @@ var PageAssembler = forwardRef(
2187
2766
  fileMap.set(file.id, file);
2188
2767
  if (file.type === "pdf" && file.originalBytes && file.originalBytes.length > 0) {
2189
2768
  try {
2190
- const sourcePdf = await PDFDocument4.load(file.originalBytes);
2769
+ const sourcePdf = await PDFDocument5.load(file.originalBytes);
2191
2770
  loadedPdfs.set(file.id, sourcePdf);
2192
2771
  } catch (error) {
2193
2772
  console.error(`Error loading PDF for file ${file.id}:`, error);
@@ -2208,7 +2787,7 @@ var PageAssembler = forwardRef(
2208
2787
  if (cachedBytes && cachedBytes.length > 0) {
2209
2788
  try {
2210
2789
  console.log(`[PageAssembler] Loading PDF from cache for deleted file ${fileId}`);
2211
- const sourcePdf = await PDFDocument4.load(cachedBytes);
2790
+ const sourcePdf = await PDFDocument5.load(cachedBytes);
2212
2791
  loadedPdfs.set(fileId, sourcePdf);
2213
2792
  } catch (error) {
2214
2793
  console.error(`Error loading cached PDF for file ${fileId}:`, error);
@@ -2292,7 +2871,7 @@ var PageAssembler = forwardRef(
2292
2871
  if (sourceInfoRef) {
2293
2872
  const sourceInfoDict = sourcePdf.context.lookup(sourceInfoRef);
2294
2873
  if (sourceInfoDict && typeof sourceInfoDict.get === "function") {
2295
- const metadataObj = sourceInfoDict.get(PDFName3.of("SigniphiMetadata"));
2874
+ const metadataObj = sourceInfoDict.get(PDFName4.of("SigniphiMetadata"));
2296
2875
  if (metadataObj) {
2297
2876
  const metadataStr = metadataObj.toString();
2298
2877
  let jsonStr = metadataStr;
@@ -2375,7 +2954,7 @@ var PageAssembler = forwardRef(
2375
2954
  }
2376
2955
  console.log(`[PageAssembler] Total extracted fields: ${allExtractedFields.length}`);
2377
2956
  console.log(`[PageAssembler] Field names:`, allExtractedFields.map((f) => `${f.name} (page ${f.position.page})`));
2378
- const combinedDoc = await PDFDocument4.create();
2957
+ const combinedDoc = await PDFDocument5.create();
2379
2958
  for (const file of state.files) {
2380
2959
  if (file.pages.length === 0) continue;
2381
2960
  for (const page of file.pages) {
@@ -2397,9 +2976,9 @@ var PageAssembler = forwardRef(
2397
2976
  const copiedPages = await combinedDoc.copyPages(sourcePdf, [page.srcIndex]);
2398
2977
  copiedPages.forEach((copiedPage) => {
2399
2978
  try {
2400
- const annotsRef = copiedPage.node.get(PDFName3.of("Annots"));
2979
+ const annotsRef = copiedPage.node.get(PDFName4.of("Annots"));
2401
2980
  if (annotsRef) {
2402
- copiedPage.node.delete(PDFName3.of("Annots"));
2981
+ copiedPage.node.delete(PDFName4.of("Annots"));
2403
2982
  }
2404
2983
  } catch (annotError) {
2405
2984
  console.warn("Could not remove annotations from copied page:", annotError);
@@ -2414,7 +2993,7 @@ var PageAssembler = forwardRef(
2414
2993
  } else if (sourceFile.type === "image" && sourceFile.originalFile) {
2415
2994
  try {
2416
2995
  const imagePdfBytes = await imageToPdf(sourceFile.originalFile);
2417
- const imagePdf = await PDFDocument4.load(imagePdfBytes);
2996
+ const imagePdf = await PDFDocument5.load(imagePdfBytes);
2418
2997
  const copiedPages = await combinedDoc.copyPages(imagePdf, [0]);
2419
2998
  copiedPages.forEach((copiedPage) => combinedDoc.addPage(copiedPage));
2420
2999
  } catch (imageError) {
@@ -2431,8 +3010,8 @@ var PageAssembler = forwardRef(
2431
3010
  const newInfoDict = combinedDoc.context.lookup(newInfoRef);
2432
3011
  if (newInfoDict && typeof newInfoDict.set === "function") {
2433
3012
  newInfoDict.set(
2434
- PDFName3.of("SigniphiMetadata"),
2435
- PDFString3.of(JSON.stringify(mergedMetadata))
3013
+ PDFName4.of("SigniphiMetadata"),
3014
+ PDFString4.of(JSON.stringify(mergedMetadata))
2436
3015
  );
2437
3016
  }
2438
3017
  }
@@ -2518,6 +3097,10 @@ var PageAssembler = forwardRef(
2518
3097
  /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2 border-primary" }),
2519
3098
  /* @__PURE__ */ jsx("span", { className: "text-sm", children: processingStatus })
2520
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
+ ] }) }),
2521
3104
  /* @__PURE__ */ jsx(
2522
3105
  BulkActionBar,
2523
3106
  {
@@ -2653,6 +3236,6 @@ function debounce(func, wait) {
2653
3236
  });
2654
3237
  }
2655
3238
 
2656
- 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 };
2657
3240
  //# sourceMappingURL=index.mjs.map
2658
3241
  //# sourceMappingURL=index.mjs.map