@signiphi/pdf-signer 0.2.0-beta.30 → 0.2.0-beta.31
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/components/SubmissionForm.d.ts.map +1 -1
- package/dist/components/index.js +310 -52
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +310 -52
- package/dist/components/index.mjs.map +1 -1
- package/dist/hooks/index.js +82 -29
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +82 -29
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/hooks/useAnnotationChat.d.ts.map +1 -1
- package/dist/hooks/usePdfViewer.d.ts +2 -1
- package/dist/hooks/usePdfViewer.d.ts.map +1 -1
- package/dist/index.js +311 -51
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +310 -52
- package/dist/index.mjs.map +1 -1
- package/dist/utils/annotation-bridge.d.ts +15 -0
- package/dist/utils/annotation-bridge.d.ts.map +1 -1
- package/dist/utils/annotation-pdf-save.d.ts +86 -7
- package/dist/utils/annotation-pdf-save.d.ts.map +1 -1
- package/dist/utils/index.js +277 -42
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +276 -43
- package/dist/utils/index.mjs.map +1 -1
- package/dist/utils/review-pdf-derivation.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubmissionForm.d.ts","sourceRoot":"","sources":["../../src/components/SubmissionForm.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"SubmissionForm.d.ts","sourceRoot":"","sources":["../../src/components/SubmissionForm.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,oBAAoB,EAAwC,MAAM,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AA6B3K,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;;OAcG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAG1B,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,uGAAuG;IACvG,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yFAAyF;IACzF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,0DAA0D;IAC1D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,QAAQ,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,cAAc,EAAE,CAAC;IACpC,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,qEAAqE;IACrE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,6FAA6F;IAC7F,qBAAqB,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClD,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,oDAAoD;IACpD,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qFAAqF;IACrF,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,mDAAmD;IACnD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC;QAAC,MAAM,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAChG,gHAAgH;IAChH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE;;;;;;;OAOG;IACH,0BAA0B,CAAC,EAAE,CAAC,IAAI,EAAE;QAClC,WAAW,EAAE,gBAAgB,EAAE,CAAC;QAChC,QAAQ,EAAE,UAAU,CAAC;KACtB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B;;;;;;;;;;;;;OAaG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAE7B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAGrB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAAE,yBAAyB;AAC7C,aAAa,EACb,mBAAmB,EACnB,aAAoB,EACpB,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,QAAgB,EAChB,SAAc,EACd,aAAoB,EACpB,gBAAgB,EAChB,kBAA0B,EAC1B,qBAA6B,EAC7B,qBAA6B,EAC7B,iBAAyB,EACzB,cAAmB,EACnB,oBAA4B,EAC5B,oBAAoB,EACpB,iBAAiB,EACjB,gBAAwB,EACxB,eAAe,EACf,0BAA0B,EAC1B,wBAA8C,EAC9C,IAAI,EACJ,UAAU,EAAE,cAAc,EAC1B,MAAM,EACN,gBAAgB,EAChB,UAAU,GACX,EAAE,mBAAmB,2CA+zGrB"}
|
package/dist/components/index.js
CHANGED
|
@@ -2827,12 +2827,43 @@ function assertSigningSourceIsClean(params) {
|
|
|
2827
2827
|
}
|
|
2828
2828
|
}
|
|
2829
2829
|
var REVIEW_MARKUP_SUBTYPES = /* @__PURE__ */ new Set(["Highlight", "FreeText", "Ink", "Stamp", "Text"]);
|
|
2830
|
-
|
|
2831
|
-
|
|
2830
|
+
var REVIEW_MARKER_KEY = "SigniphiReview";
|
|
2831
|
+
var RECT_MATCH_EPSILON = 1;
|
|
2832
|
+
function rectsMatch(a, b) {
|
|
2833
|
+
const norm = (r) => [
|
|
2834
|
+
Math.min(r[0], r[2]),
|
|
2835
|
+
Math.min(r[1], r[3]),
|
|
2836
|
+
Math.max(r[0], r[2]),
|
|
2837
|
+
Math.max(r[1], r[3])
|
|
2838
|
+
];
|
|
2839
|
+
const na = norm(a);
|
|
2840
|
+
const nb = norm(b);
|
|
2841
|
+
return na.every((v, i) => Math.abs(v - nb[i]) <= RECT_MATCH_EPSILON);
|
|
2842
|
+
}
|
|
2843
|
+
function sourceNativeMarkDescriptors(threads) {
|
|
2844
|
+
const out = [];
|
|
2845
|
+
for (const t of threads) {
|
|
2846
|
+
const a = t.annotation;
|
|
2847
|
+
if (!a.sourceAnnotationId) continue;
|
|
2848
|
+
const rect = a.position?.rect;
|
|
2849
|
+
if (!rect || rect.length < 4) continue;
|
|
2850
|
+
out.push({
|
|
2851
|
+
subtype: ANNOTATION_SUBTYPE_MAP[a.type] || "Text",
|
|
2852
|
+
pageIndex: (a.position.pageNumber ?? 1) - 1,
|
|
2853
|
+
rect: rect.slice(0, 4)
|
|
2854
|
+
});
|
|
2855
|
+
}
|
|
2856
|
+
return out;
|
|
2857
|
+
}
|
|
2858
|
+
async function hasBakedReviewMarkup(pdfBytes, allowedSourceMarks) {
|
|
2859
|
+
const { PDFDocument, PDFName: PDFName3, PDFArray, PDFDict, PDFRef, PDFNumber } = await loadPdfLib();
|
|
2832
2860
|
const pdfDoc = await PDFDocument.load(pdfBytes);
|
|
2833
2861
|
const markup = [];
|
|
2834
2862
|
const byTag = /* @__PURE__ */ new Map();
|
|
2835
|
-
|
|
2863
|
+
const pages = pdfDoc.getPages();
|
|
2864
|
+
for (let pageIndex = 0; pageIndex < pages.length; pageIndex++) {
|
|
2865
|
+
const page = pages[pageIndex];
|
|
2866
|
+
if (!page) continue;
|
|
2836
2867
|
const annots = page.node.lookupMaybe(PDFName3.of("Annots"), PDFArray);
|
|
2837
2868
|
if (!annots) continue;
|
|
2838
2869
|
for (let i = 0; i < annots.size(); i++) {
|
|
@@ -2844,47 +2875,64 @@ async function hasBakedReviewMarkup(pdfBytes, ignoreIds) {
|
|
|
2844
2875
|
continue;
|
|
2845
2876
|
}
|
|
2846
2877
|
const irt = dict.get(PDFName3.of("IRT"));
|
|
2878
|
+
const rt = dict.get(PDFName3.of("RT"));
|
|
2879
|
+
const rectArr = dict.lookup(PDFName3.of("Rect"));
|
|
2880
|
+
let rect = null;
|
|
2881
|
+
if (rectArr instanceof PDFArray && rectArr.size() >= 4) {
|
|
2882
|
+
const values = [];
|
|
2883
|
+
for (let j = 0; j < 4; j++) {
|
|
2884
|
+
const n = rectArr.lookup(j);
|
|
2885
|
+
if (n instanceof PDFNumber) values.push(n.asNumber());
|
|
2886
|
+
}
|
|
2887
|
+
if (values.length === 4) rect = values;
|
|
2888
|
+
}
|
|
2889
|
+
const subtypeText = subtype.decodeText();
|
|
2847
2890
|
const record = {
|
|
2848
|
-
|
|
2891
|
+
subtype: subtypeText,
|
|
2892
|
+
pageIndex,
|
|
2893
|
+
rect,
|
|
2849
2894
|
irtTag: irt instanceof PDFRef ? irt.toString() : null,
|
|
2850
|
-
tag: entry instanceof PDFRef ? entry.toString() : null
|
|
2895
|
+
tag: entry instanceof PDFRef ? entry.toString() : null,
|
|
2896
|
+
marker: dict.get(PDFName3.of(REVIEW_MARKER_KEY)) !== void 0,
|
|
2897
|
+
replyShaped: subtypeText === "Text" || rt instanceof PDFName3 && rt.decodeText() === "Group"
|
|
2851
2898
|
};
|
|
2852
2899
|
markup.push(record);
|
|
2853
2900
|
if (record.tag) byTag.set(record.tag, record);
|
|
2854
2901
|
}
|
|
2855
2902
|
}
|
|
2856
2903
|
if (markup.length === 0) return false;
|
|
2857
|
-
if (!
|
|
2858
|
-
const
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2904
|
+
if (!allowedSourceMarks?.length) return true;
|
|
2905
|
+
const matchesDescriptor = (m) => m.rect !== null && allowedSourceMarks.some(
|
|
2906
|
+
(d) => d.subtype === m.subtype && d.pageIndex === m.pageIndex && rectsMatch(d.rect, m.rect)
|
|
2907
|
+
);
|
|
2908
|
+
const isExempt = (m, hops = 0) => {
|
|
2909
|
+
if (m.marker) return false;
|
|
2910
|
+
if (matchesDescriptor(m)) return true;
|
|
2911
|
+
if (!m.replyShaped || !m.irtTag || hops >= 64) return false;
|
|
2912
|
+
const parent = byTag.get(m.irtTag);
|
|
2913
|
+
return parent !== void 0 && parent !== m && isExempt(parent, hops + 1);
|
|
2866
2914
|
};
|
|
2867
|
-
|
|
2868
|
-
const id = rootId(m);
|
|
2869
|
-
if (!id || !ignoreIds.has(id)) return true;
|
|
2870
|
-
}
|
|
2871
|
-
return false;
|
|
2915
|
+
return markup.some((m) => !isExempt(m));
|
|
2872
2916
|
}
|
|
2873
2917
|
function pdfJsRefId(ref) {
|
|
2874
2918
|
return `${ref.objectNumber}R${ref.generationNumber === 0 ? "" : ref.generationNumber}`;
|
|
2875
2919
|
}
|
|
2876
2920
|
async function removeMarkupAnnotations(pdfBytes, options) {
|
|
2877
|
-
const { PDFDocument, PDFName: PDFName3, PDFArray, PDFDict, PDFRef } = await loadPdfLib();
|
|
2921
|
+
const { PDFDocument, PDFName: PDFName3, PDFArray, PDFDict, PDFRef, PDFNumber } = await loadPdfLib();
|
|
2878
2922
|
const pdfDoc = await PDFDocument.load(pdfBytes);
|
|
2879
2923
|
const keepIds = options?.keepIds ?? /* @__PURE__ */ new Set();
|
|
2924
|
+
const keepDescriptors = options?.keepDescriptors ?? [];
|
|
2880
2925
|
const subtypeName = PDFName3.of("Subtype");
|
|
2881
2926
|
const irtName = PDFName3.of("IRT");
|
|
2927
|
+
const rtName = PDFName3.of("RT");
|
|
2882
2928
|
const popupName = PDFName3.of("Popup");
|
|
2883
2929
|
const parentName = PDFName3.of("Parent");
|
|
2884
2930
|
const annotsName = PDFName3.of("Annots");
|
|
2885
2931
|
const markupByTag = /* @__PURE__ */ new Map();
|
|
2886
2932
|
const pages = pdfDoc.getPages();
|
|
2887
|
-
for (
|
|
2933
|
+
for (let pageIndex = 0; pageIndex < pages.length; pageIndex++) {
|
|
2934
|
+
const page = pages[pageIndex];
|
|
2935
|
+
if (!page) continue;
|
|
2888
2936
|
const annots = page.node.lookupMaybe(annotsName, PDFArray);
|
|
2889
2937
|
if (!annots) continue;
|
|
2890
2938
|
for (let i = 0; i < annots.size(); i++) {
|
|
@@ -2894,9 +2942,59 @@ async function removeMarkupAnnotations(pdfBytes, options) {
|
|
|
2894
2942
|
if (!(dict instanceof PDFDict)) continue;
|
|
2895
2943
|
const subtype = dict.get(subtypeName);
|
|
2896
2944
|
if (subtype instanceof PDFName3 && REVIEW_MARKUP_SUBTYPES.has(subtype.decodeText())) {
|
|
2897
|
-
|
|
2945
|
+
const rectArr = dict.lookup(PDFName3.of("Rect"));
|
|
2946
|
+
let rect = null;
|
|
2947
|
+
if (rectArr instanceof PDFArray && rectArr.size() >= 4) {
|
|
2948
|
+
const values = [];
|
|
2949
|
+
for (let j = 0; j < 4; j++) {
|
|
2950
|
+
const n = rectArr.lookup(j);
|
|
2951
|
+
if (n instanceof PDFNumber) values.push(n.asNumber());
|
|
2952
|
+
}
|
|
2953
|
+
if (values.length === 4) rect = values;
|
|
2954
|
+
}
|
|
2955
|
+
markupByTag.set(entry.toString(), {
|
|
2956
|
+
ref: entry,
|
|
2957
|
+
dict,
|
|
2958
|
+
subtype: subtype.decodeText(),
|
|
2959
|
+
pageIndex,
|
|
2960
|
+
rect
|
|
2961
|
+
});
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
const isChainChild = (entry) => {
|
|
2966
|
+
const irt = entry.dict.get(irtName);
|
|
2967
|
+
return irt instanceof PDFRef && markupByTag.has(irt.toString());
|
|
2968
|
+
};
|
|
2969
|
+
const matchingDescriptorIndices = (entry) => {
|
|
2970
|
+
if (!entry.rect) return [];
|
|
2971
|
+
const indices = [];
|
|
2972
|
+
for (let i = 0; i < keepDescriptors.length; i++) {
|
|
2973
|
+
const d = keepDescriptors[i];
|
|
2974
|
+
if (d.subtype === entry.subtype && d.pageIndex === entry.pageIndex && rectsMatch(d.rect, entry.rect)) {
|
|
2975
|
+
indices.push(i);
|
|
2898
2976
|
}
|
|
2899
2977
|
}
|
|
2978
|
+
return indices;
|
|
2979
|
+
};
|
|
2980
|
+
const rootKeep = /* @__PURE__ */ new Set();
|
|
2981
|
+
const descriptorClaimed = /* @__PURE__ */ new Set();
|
|
2982
|
+
const keptDescriptors = [];
|
|
2983
|
+
for (const [tag, entry] of markupByTag) {
|
|
2984
|
+
if (isChainChild(entry)) continue;
|
|
2985
|
+
if (keepIds.has(pdfJsRefId(entry.ref))) {
|
|
2986
|
+
rootKeep.add(tag);
|
|
2987
|
+
for (const i of matchingDescriptorIndices(entry)) descriptorClaimed.add(i);
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
for (const [tag, entry] of markupByTag) {
|
|
2991
|
+
if (isChainChild(entry) || rootKeep.has(tag)) continue;
|
|
2992
|
+
const index = matchingDescriptorIndices(entry).find((i) => !descriptorClaimed.has(i));
|
|
2993
|
+
if (index !== void 0) {
|
|
2994
|
+
descriptorClaimed.add(index);
|
|
2995
|
+
rootKeep.add(tag);
|
|
2996
|
+
keptDescriptors.push({ index, id: pdfJsRefId(entry.ref) });
|
|
2997
|
+
}
|
|
2900
2998
|
}
|
|
2901
2999
|
const keepDecision = /* @__PURE__ */ new Map();
|
|
2902
3000
|
const isKept = (tag) => {
|
|
@@ -2906,7 +3004,10 @@ async function removeMarkupAnnotations(pdfBytes, options) {
|
|
|
2906
3004
|
const entry = markupByTag.get(tag);
|
|
2907
3005
|
if (!entry) return false;
|
|
2908
3006
|
const irt = entry.dict.get(irtName);
|
|
2909
|
-
const
|
|
3007
|
+
const subtype = entry.dict.get(subtypeName);
|
|
3008
|
+
const rt = entry.dict.get(rtName);
|
|
3009
|
+
const replyShaped = subtype instanceof PDFName3 && subtype.decodeText() === "Text" || rt instanceof PDFName3 && rt.decodeText() === "Group";
|
|
3010
|
+
const decision = irt instanceof PDFRef && markupByTag.has(irt.toString()) ? replyShaped && isKept(irt.toString()) : rootKeep.has(tag);
|
|
2910
3011
|
keepDecision.set(tag, decision);
|
|
2911
3012
|
return decision;
|
|
2912
3013
|
};
|
|
@@ -2923,32 +3024,140 @@ async function removeMarkupAnnotations(pdfBytes, options) {
|
|
|
2923
3024
|
if (popup instanceof PDFRef) removedTags.add(popup.toString());
|
|
2924
3025
|
}
|
|
2925
3026
|
}
|
|
3027
|
+
let removedInline = 0;
|
|
2926
3028
|
for (const page of pages) {
|
|
2927
3029
|
const annots = page.node.lookupMaybe(annotsName, PDFArray);
|
|
2928
3030
|
if (!annots) continue;
|
|
2929
3031
|
for (let i = annots.size() - 1; i >= 0; i--) {
|
|
2930
3032
|
const entry = annots.get(i);
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
3033
|
+
let remove = false;
|
|
3034
|
+
if (entry instanceof PDFRef) {
|
|
3035
|
+
remove = removedTags.has(entry.toString());
|
|
3036
|
+
if (!remove) {
|
|
3037
|
+
const dict = page.node.context.lookup(entry);
|
|
3038
|
+
if (dict instanceof PDFDict) {
|
|
3039
|
+
const subtype = dict.get(subtypeName);
|
|
3040
|
+
const parent = dict.get(parentName);
|
|
3041
|
+
remove = subtype instanceof PDFName3 && subtype.decodeText() === "Popup" && parent instanceof PDFRef && removedTags.has(parent.toString());
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
} else if (entry instanceof PDFDict) {
|
|
3045
|
+
const subtype = entry.get(subtypeName);
|
|
3046
|
+
const parent = entry.get(parentName);
|
|
3047
|
+
remove = subtype instanceof PDFName3 && REVIEW_MARKUP_SUBTYPES.has(subtype.decodeText()) || subtype instanceof PDFName3 && subtype.decodeText() === "Popup" && parent instanceof PDFRef && removedTags.has(parent.toString());
|
|
3048
|
+
if (remove) removedInline++;
|
|
2940
3049
|
}
|
|
2941
3050
|
if (remove) annots.remove(i);
|
|
2942
3051
|
}
|
|
2943
3052
|
}
|
|
2944
|
-
if (removedTags.size === 0) {
|
|
2945
|
-
return { pdfBytes, removedIds, keptIds };
|
|
3053
|
+
if (removedTags.size === 0 && removedInline === 0) {
|
|
3054
|
+
return { pdfBytes, removedIds, keptIds, keptDescriptors };
|
|
2946
3055
|
}
|
|
2947
3056
|
const saved = await pdfDoc.save();
|
|
2948
|
-
return { pdfBytes: saved, removedIds, keptIds };
|
|
3057
|
+
return { pdfBytes: saved, removedIds, keptIds, keptDescriptors };
|
|
3058
|
+
}
|
|
3059
|
+
async function appendRepliesToExistingAnnotations(pdfBytes, threadsWithKeptRoots) {
|
|
3060
|
+
if (!threadsWithKeptRoots.some(({ thread }) => thread.replies.length > 0)) {
|
|
3061
|
+
return pdfBytes;
|
|
3062
|
+
}
|
|
3063
|
+
const { PDFDocument, PDFName: PDFName3, PDFArray, PDFDict, PDFRef, PDFNumber, PDFString: PDFString2, PDFHexString: PDFHexString2 } = await loadPdfLib();
|
|
3064
|
+
const pdfDoc = await PDFDocument.load(pdfBytes);
|
|
3065
|
+
const context = pdfDoc.context;
|
|
3066
|
+
const subtypeName = PDFName3.of("Subtype");
|
|
3067
|
+
const irtName = PDFName3.of("IRT");
|
|
3068
|
+
const annotsName = PDFName3.of("Annots");
|
|
3069
|
+
const entriesByTag = /* @__PURE__ */ new Map();
|
|
3070
|
+
const tagById = /* @__PURE__ */ new Map();
|
|
3071
|
+
for (const page of pdfDoc.getPages()) {
|
|
3072
|
+
const annots = page.node.lookupMaybe(annotsName, PDFArray);
|
|
3073
|
+
if (!annots) continue;
|
|
3074
|
+
for (let i = 0; i < annots.size(); i++) {
|
|
3075
|
+
const entry = annots.get(i);
|
|
3076
|
+
if (!(entry instanceof PDFRef)) continue;
|
|
3077
|
+
const dict = page.node.context.lookup(entry);
|
|
3078
|
+
if (!(dict instanceof PDFDict)) continue;
|
|
3079
|
+
const subtype = dict.get(subtypeName);
|
|
3080
|
+
if (subtype instanceof PDFName3 && REVIEW_MARKUP_SUBTYPES.has(subtype.decodeText())) {
|
|
3081
|
+
entriesByTag.set(entry.toString(), { ref: entry, dict, annotsArray: annots });
|
|
3082
|
+
tagById.set(pdfJsRefId(entry), entry.toString());
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
const decodeText = (value) => {
|
|
3087
|
+
if (value instanceof PDFHexString2 || value instanceof PDFString2) return value.decodeText();
|
|
3088
|
+
return null;
|
|
3089
|
+
};
|
|
3090
|
+
const rootTagOf = (tag) => {
|
|
3091
|
+
let current = tag;
|
|
3092
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3093
|
+
while (!seen.has(current)) {
|
|
3094
|
+
seen.add(current);
|
|
3095
|
+
const irt = entriesByTag.get(current)?.dict.get(irtName);
|
|
3096
|
+
if (!(irt instanceof PDFRef) || !entriesByTag.has(irt.toString())) break;
|
|
3097
|
+
current = irt.toString();
|
|
3098
|
+
}
|
|
3099
|
+
return current;
|
|
3100
|
+
};
|
|
3101
|
+
const dedupKey = (author, contents) => `${author ?? "Document"}
|
|
3102
|
+
${contents ?? ""}`;
|
|
3103
|
+
const existingByRootTag = /* @__PURE__ */ new Map();
|
|
3104
|
+
for (const [tag, entry] of entriesByTag) {
|
|
3105
|
+
if (!(entry.dict.get(irtName) instanceof PDFRef)) continue;
|
|
3106
|
+
const rootTag = rootTagOf(tag);
|
|
3107
|
+
if (rootTag === tag) continue;
|
|
3108
|
+
let keys = existingByRootTag.get(rootTag);
|
|
3109
|
+
if (!keys) existingByRootTag.set(rootTag, keys = /* @__PURE__ */ new Set());
|
|
3110
|
+
keys.add(
|
|
3111
|
+
dedupKey(decodeText(entry.dict.get(PDFName3.of("T"))), decodeText(entry.dict.get(PDFName3.of("Contents"))))
|
|
3112
|
+
);
|
|
3113
|
+
}
|
|
3114
|
+
let appended = 0;
|
|
3115
|
+
for (const { thread, rootId } of threadsWithKeptRoots) {
|
|
3116
|
+
const rootTag = tagById.get(rootId);
|
|
3117
|
+
const root = rootTag ? entriesByTag.get(rootTag) : void 0;
|
|
3118
|
+
if (!root || !rootTag) {
|
|
3119
|
+
logger.debug(
|
|
3120
|
+
`appendRepliesToExistingAnnotations: no markup dict with id ${rootId} in these bytes; skipping ${thread.replies.length} reply(ies).`
|
|
3121
|
+
);
|
|
3122
|
+
continue;
|
|
3123
|
+
}
|
|
3124
|
+
const rectArr = root.dict.get(PDFName3.of("Rect"));
|
|
3125
|
+
const rect = [0, 0, 0, 0];
|
|
3126
|
+
if (rectArr instanceof PDFArray && rectArr.size() >= 4) {
|
|
3127
|
+
for (let j = 0; j < 4; j++) {
|
|
3128
|
+
const n = rectArr.lookup(j);
|
|
3129
|
+
if (n instanceof PDFNumber) rect[j] = n.asNumber();
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
let keys = existingByRootTag.get(rootTag);
|
|
3133
|
+
if (!keys) existingByRootTag.set(rootTag, keys = /* @__PURE__ */ new Set());
|
|
3134
|
+
for (const reply of thread.replies) {
|
|
3135
|
+
const key = dedupKey(reply.author.name || reply.author.email || "Unknown", reply.content);
|
|
3136
|
+
if (keys.has(key)) continue;
|
|
3137
|
+
keys.add(key);
|
|
3138
|
+
addReplyAnnotation(
|
|
3139
|
+
context,
|
|
3140
|
+
root.annotsArray,
|
|
3141
|
+
root.ref,
|
|
3142
|
+
reply,
|
|
3143
|
+
PDFName3,
|
|
3144
|
+
PDFArray,
|
|
3145
|
+
PDFString2,
|
|
3146
|
+
PDFNumber,
|
|
3147
|
+
PDFHexString2,
|
|
3148
|
+
rect[0],
|
|
3149
|
+
rect[1],
|
|
3150
|
+
rect[2],
|
|
3151
|
+
rect[3]
|
|
3152
|
+
);
|
|
3153
|
+
appended++;
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
if (appended === 0) return pdfBytes;
|
|
3157
|
+
return pdfDoc.save();
|
|
2949
3158
|
}
|
|
2950
|
-
async function assertSigningBytesFreeOfReviewMarkup(pdfBytes,
|
|
2951
|
-
if (await hasBakedReviewMarkup(pdfBytes,
|
|
3159
|
+
async function assertSigningBytesFreeOfReviewMarkup(pdfBytes, allowedSourceMarks) {
|
|
3160
|
+
if (await hasBakedReviewMarkup(pdfBytes, allowedSourceMarks)) {
|
|
2952
3161
|
throw new ReviewAnnotationSigningError(
|
|
2953
3162
|
"SubmissionForm: the PDF selected as the signing source still carries baked review annotations (markup annotation dicts are present in its pages). Signing it would embed those review marks in the signed copy. Pass `pdfUrlForSigning` pointing at the clean original/prepared PDF (distinct from the baked review PDF) so the deliverable is filled from un-annotated source. This check is content-based, so it holds even when the annotation tree was not hydrated in the viewer."
|
|
2954
3163
|
);
|
|
@@ -3107,6 +3316,7 @@ async function embedAnnotationsInPdf(pdfBytes, annotations, options) {
|
|
|
3107
3316
|
const annotDict = context.obj({});
|
|
3108
3317
|
annotDict.set(PDFName3.of("Type"), PDFName3.of("Annot"));
|
|
3109
3318
|
annotDict.set(PDFName3.of("Subtype"), PDFName3.of(subtype));
|
|
3319
|
+
annotDict.set(PDFName3.of(REVIEW_MARKER_KEY), PDFName3.of("v1"));
|
|
3110
3320
|
const [x1, y1, x2, y2] = serializedRect;
|
|
3111
3321
|
const rectArray = PDFArray.withContext(context);
|
|
3112
3322
|
rectArray.push(PDFNumber.of(x1));
|
|
@@ -3260,6 +3470,7 @@ function addReplyAnnotation(context, annotsArray, parentRef, reply, PDFName3, PD
|
|
|
3260
3470
|
const replyDict = context.obj({});
|
|
3261
3471
|
replyDict.set(PDFName3.of("Type"), PDFName3.of("Annot"));
|
|
3262
3472
|
replyDict.set(PDFName3.of("Subtype"), PDFName3.of("Text"));
|
|
3473
|
+
replyDict.set(PDFName3.of(REVIEW_MARKER_KEY), PDFName3.of("v1"));
|
|
3263
3474
|
const rectArray = PDFArray.withContext(context);
|
|
3264
3475
|
rectArray.push(PDFNumber.of(x1));
|
|
3265
3476
|
rectArray.push(PDFNumber.of(y1));
|
|
@@ -3300,20 +3511,42 @@ async function deriveReviewPdf(preparedPdfBytes, annotationTree, options) {
|
|
|
3300
3511
|
for (const t of annotationTree) {
|
|
3301
3512
|
if (isUnchangedSourceThread(t)) wantKeep.set(t.annotation.sourceAnnotationId, t);
|
|
3302
3513
|
}
|
|
3303
|
-
const
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3514
|
+
const descriptorOwners = [];
|
|
3515
|
+
const descriptors = [];
|
|
3516
|
+
for (const thread of wantKeep.values()) {
|
|
3517
|
+
const [descriptor] = sourceNativeMarkDescriptors([thread]);
|
|
3518
|
+
if (descriptor) {
|
|
3519
|
+
descriptors.push(descriptor);
|
|
3520
|
+
descriptorOwners.push(thread);
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
const { pdfBytes: cleaned, keptIds, removedIds, keptDescriptors } = await removeMarkupAnnotations(preparedPdfBytes, {
|
|
3524
|
+
keepIds: new Set(wantKeep.keys()),
|
|
3525
|
+
keepDescriptors: descriptors
|
|
3526
|
+
});
|
|
3307
3527
|
if (removedIds.length > 0 || keptIds.length > 0) {
|
|
3308
3528
|
logger.debug(
|
|
3309
3529
|
`deriveReviewPdf(sanitizeSource): kept ${keptIds.length} source mark(s), removed ${removedIds.length} from the source bytes before embedding.`
|
|
3310
3530
|
);
|
|
3311
3531
|
}
|
|
3312
3532
|
const found = new Set(keptIds);
|
|
3533
|
+
const contentMatched = /* @__PURE__ */ new Map();
|
|
3534
|
+
for (const { index, id } of keptDescriptors) {
|
|
3535
|
+
const owner = descriptorOwners[index];
|
|
3536
|
+
if (owner) contentMatched.set(owner, id);
|
|
3537
|
+
}
|
|
3313
3538
|
const toEmbed = annotationTree.filter(
|
|
3314
|
-
(t) => !(t.annotation.sourceAnnotationId && found.has(t.annotation.sourceAnnotationId))
|
|
3539
|
+
(t) => !(t.annotation.sourceAnnotationId && found.has(t.annotation.sourceAnnotationId)) && !contentMatched.has(t)
|
|
3315
3540
|
);
|
|
3316
|
-
|
|
3541
|
+
const keptRootThreads = [];
|
|
3542
|
+
for (const [sourceId, thread] of wantKeep) {
|
|
3543
|
+
const rootId = found.has(sourceId) ? sourceId : contentMatched.get(thread);
|
|
3544
|
+
if (rootId && thread.replies.length > 0) {
|
|
3545
|
+
keptRootThreads.push({ thread, rootId });
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
const withReplies = keptRootThreads.length ? await appendRepliesToExistingAnnotations(cleaned, keptRootThreads) : cleaned;
|
|
3549
|
+
return embedAnnotationsInPdf(withReplies, toEmbed, embedOptions);
|
|
3317
3550
|
}
|
|
3318
3551
|
if (!allowMarkupInSource && await hasBakedReviewMarkup(preparedPdfBytes)) {
|
|
3319
3552
|
throw new ReviewAnnotationSigningError(
|
|
@@ -5608,13 +5841,13 @@ function usePdfViewer(multiSignerContext) {
|
|
|
5608
5841
|
[extractedFields]
|
|
5609
5842
|
);
|
|
5610
5843
|
const fillPdf = React9.useCallback(
|
|
5611
|
-
async (fieldValues, signatures, currentSignerEmail, metadata, auditTrail, pdfBytesOverride, guardReviewMarkup = false,
|
|
5844
|
+
async (fieldValues, signatures, currentSignerEmail, metadata, auditTrail, pdfBytesOverride, guardReviewMarkup = false, allowedSourceMarks) => {
|
|
5612
5845
|
const sourceBytes = pdfBytesOverride ?? originalPdfBytes;
|
|
5613
5846
|
if (!sourceBytes) {
|
|
5614
5847
|
throw new PdfProcessingError("No PDF loaded");
|
|
5615
5848
|
}
|
|
5616
5849
|
if (guardReviewMarkup) {
|
|
5617
|
-
await assertSigningBytesFreeOfReviewMarkup(sourceBytes,
|
|
5850
|
+
await assertSigningBytesFreeOfReviewMarkup(sourceBytes, allowedSourceMarks);
|
|
5618
5851
|
}
|
|
5619
5852
|
try {
|
|
5620
5853
|
const valuesValidation = validateFieldValues(fieldValues);
|
|
@@ -6536,6 +6769,31 @@ var AnnotationBridge = class {
|
|
|
6536
6769
|
logger.error("AnnotationBridge: Failed to navigate to page:", e);
|
|
6537
6770
|
}
|
|
6538
6771
|
}
|
|
6772
|
+
/**
|
|
6773
|
+
* Origin check for inbound viewer messages (defence-in-depth alongside the
|
|
6774
|
+
* event.source check). The viewer iframe is always embedded same-origin
|
|
6775
|
+
* (`public/pdfjs/web/...` served by the host app), so the legitimate sender
|
|
6776
|
+
* origin is our own — but derive the expectation from the iframe's actual
|
|
6777
|
+
* `src` so a host that serves the viewer from another origin keeps working.
|
|
6778
|
+
* blob: URLs inherit their creator's origin, which `new URL(...).origin`
|
|
6779
|
+
* resolves correctly.
|
|
6780
|
+
*
|
|
6781
|
+
* Residual gap: when the iframe's src resolves to an opaque origin
|
|
6782
|
+
* (`'null'`, e.g. a sandboxed data: URL) origins are indistinguishable and
|
|
6783
|
+
* we fall back to the event.source check alone rather than dropping all
|
|
6784
|
+
* messages.
|
|
6785
|
+
*/
|
|
6786
|
+
isExpectedOrigin(origin) {
|
|
6787
|
+
const src = this.iframeRef.current?.src;
|
|
6788
|
+
if (!src) return origin === window.location.origin;
|
|
6789
|
+
try {
|
|
6790
|
+
const srcOrigin = new URL(src, window.location.href).origin;
|
|
6791
|
+
if (srcOrigin === "null") return true;
|
|
6792
|
+
return origin === srcOrigin || origin === window.location.origin;
|
|
6793
|
+
} catch {
|
|
6794
|
+
return origin === window.location.origin;
|
|
6795
|
+
}
|
|
6796
|
+
}
|
|
6539
6797
|
/**
|
|
6540
6798
|
* Start listening for annotation events from the iframe
|
|
6541
6799
|
*/
|
|
@@ -6544,6 +6802,7 @@ var AnnotationBridge = class {
|
|
|
6544
6802
|
this.messageHandler = (event) => {
|
|
6545
6803
|
const expectedSource = this.iframeRef.current?.contentWindow;
|
|
6546
6804
|
if (!expectedSource || event.source !== expectedSource) return;
|
|
6805
|
+
if (!this.isExpectedOrigin(event.origin)) return;
|
|
6547
6806
|
if (!event.data || typeof event.data.type !== "string") return;
|
|
6548
6807
|
if (!event.data.type.startsWith("signiphi:annotation:")) return;
|
|
6549
6808
|
const { type, payload } = event.data;
|
|
@@ -6561,7 +6820,7 @@ var AnnotationBridge = class {
|
|
|
6561
6820
|
this.callbacks.onAnnotationsInjected?.(payload?.count ?? 0);
|
|
6562
6821
|
break;
|
|
6563
6822
|
case "signiphi:annotation:clicked":
|
|
6564
|
-
if (payload?.editorId) {
|
|
6823
|
+
if (typeof payload?.editorId === "string" && payload.editorId) {
|
|
6565
6824
|
this.callbacks.onAnnotationClicked?.(payload.editorId);
|
|
6566
6825
|
}
|
|
6567
6826
|
break;
|
|
@@ -7109,11 +7368,12 @@ function useAnnotationChat({
|
|
|
7109
7368
|
if (annotationsRef.current.length > 0) return;
|
|
7110
7369
|
const bridge = bridgeRef.current;
|
|
7111
7370
|
if (!bridge) return;
|
|
7112
|
-
sourceIngestDone.current = true;
|
|
7113
7371
|
let cancelled = false;
|
|
7114
7372
|
void (async () => {
|
|
7115
7373
|
const source = await bridge.getSourceMarkupAnnotations();
|
|
7116
|
-
if (cancelled
|
|
7374
|
+
if (cancelled) return;
|
|
7375
|
+
sourceIngestDone.current = true;
|
|
7376
|
+
if (source.length === 0) return;
|
|
7117
7377
|
const threads = sourceMarkupToThreads(source);
|
|
7118
7378
|
if (threads.length === 0) return;
|
|
7119
7379
|
logger.debug(
|
|
@@ -14501,9 +14761,7 @@ function SubmissionForm({
|
|
|
14501
14761
|
pdfUrl,
|
|
14502
14762
|
pdfUrlForSigning
|
|
14503
14763
|
});
|
|
14504
|
-
const
|
|
14505
|
-
annotationChat.annotations.map((t) => t.annotation.sourceAnnotationId).filter((id) => !!id)
|
|
14506
|
-
);
|
|
14764
|
+
const sourceNativeMarks = sourceNativeMarkDescriptors(annotationChat.annotations);
|
|
14507
14765
|
let pdfBytesOverride = null;
|
|
14508
14766
|
const signingUrl = pdfUrlForSigning?.trim();
|
|
14509
14767
|
if (signingUrl && signingUrl !== pdfUrl) {
|
|
@@ -14523,7 +14781,7 @@ function SubmissionForm({
|
|
|
14523
14781
|
auditTrail,
|
|
14524
14782
|
pdfBytesOverride,
|
|
14525
14783
|
enableAnnotationChat,
|
|
14526
|
-
|
|
14784
|
+
sourceNativeMarks
|
|
14527
14785
|
);
|
|
14528
14786
|
const finalPdfBytes = filledPdfBytes;
|
|
14529
14787
|
const annotationThreads = enableAnnotationChat ? annotationChat.getAnnotationsForPdfSave() : void 0;
|