mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-14 10:34:06 +02:00
smart redaction (#6195)
Co-authored-by: James Brunton <[email protected]>
This commit is contained in:
co-authored by
James Brunton
parent
7f3ca7ea70
commit
35a712a278
@@ -64,15 +64,16 @@ async function extractPageMeasureScales(
|
||||
// Parse a Measure dict into a MeasureScale, or return null if malformed.
|
||||
const parseScale = (measureObj: unknown) => {
|
||||
if (!(measureObj instanceof PDFDict)) return null;
|
||||
const rObj = measureObj.lookup(PDFName.of("R"));
|
||||
// @cantoo/pdf-lib ships without individual .d.ts files so instanceof can't narrow `unknown`
|
||||
const m = measureObj as PDFDict;
|
||||
const rObj = m.lookup(PDFName.of("R"));
|
||||
const ratioLabel =
|
||||
rObj instanceof PDFString || rObj instanceof PDFHexString
|
||||
? rObj.decodeText()
|
||||
: "";
|
||||
// D = distance array, X = x-axis fallback
|
||||
let fmtArray = measureObj.lookup(PDFName.of("D"));
|
||||
if (!(fmtArray instanceof PDFArray))
|
||||
fmtArray = measureObj.lookup(PDFName.of("X"));
|
||||
let fmtArray = m.lookup(PDFName.of("D"));
|
||||
if (!(fmtArray instanceof PDFArray)) fmtArray = m.lookup(PDFName.of("X"));
|
||||
if (!(fmtArray instanceof PDFArray)) return null;
|
||||
const firstFmt = fmtArray.lookup(0);
|
||||
if (!(firstFmt instanceof PDFDict)) return null;
|
||||
|
||||
@@ -22,6 +22,20 @@ import type {
|
||||
ButtonAction,
|
||||
} from "@app/tools/formFill/types";
|
||||
import type { IFormDataProvider } from "@app/tools/formFill/providers/types";
|
||||
import type {
|
||||
PDFDict,
|
||||
PDFString,
|
||||
PDFHexString,
|
||||
PDFName,
|
||||
} from "@cantoo/pdf-lib";
|
||||
|
||||
interface PDFAcroField {
|
||||
dict: PDFDict;
|
||||
getWidgets?: () => Array<{ dict: PDFDict }>;
|
||||
}
|
||||
interface PDFFieldInternal {
|
||||
acroField?: PDFAcroField;
|
||||
}
|
||||
import {
|
||||
closeDocAndFreeBuffer,
|
||||
extractFormFields,
|
||||
@@ -238,7 +252,11 @@ export class PdfiumFormProvider implements IFormDataProvider {
|
||||
);
|
||||
const altName = readUtf16(m, altBuf, altLen);
|
||||
m.pdfium.wasmExports.free(altBuf);
|
||||
(nameToField.get(name) as any)._tooltip = altName || null;
|
||||
(
|
||||
nameToField.get(name) as PdfiumFormField & {
|
||||
_tooltip?: string | null;
|
||||
}
|
||||
)._tooltip = altName || null;
|
||||
}
|
||||
enriched.add(name);
|
||||
}
|
||||
@@ -299,7 +317,7 @@ export class PdfiumFormProvider implements IFormDataProvider {
|
||||
|
||||
const decodeText = (obj: unknown): string => {
|
||||
if (obj instanceof PDFString || obj instanceof PDFHexString)
|
||||
return obj.decodeText();
|
||||
return (obj as PDFString | PDFHexString).decodeText();
|
||||
return String(obj ?? "");
|
||||
};
|
||||
|
||||
@@ -312,7 +330,8 @@ export class PdfiumFormProvider implements IFormDataProvider {
|
||||
)
|
||||
continue;
|
||||
|
||||
const acroDict = (field.acroField as any).dict;
|
||||
const acroDict = (field as unknown as PDFFieldInternal).acroField!
|
||||
.dict;
|
||||
const optRaw = acroDict.lookup(PDFName.of("Opt"));
|
||||
if (!(optRaw instanceof PDFArray)) continue;
|
||||
|
||||
@@ -374,8 +393,14 @@ export class PdfiumFormProvider implements IFormDataProvider {
|
||||
if (buttons.length === 0) return result;
|
||||
|
||||
try {
|
||||
const { PDFDocument, PDFName, PDFString, PDFHexString, PDFDict } =
|
||||
await import("@cantoo/pdf-lib");
|
||||
const {
|
||||
PDFDocument,
|
||||
PDFName,
|
||||
PDFString,
|
||||
PDFHexString,
|
||||
PDFDict,
|
||||
PDFNumber,
|
||||
} = await import("@cantoo/pdf-lib");
|
||||
|
||||
const doc = await PDFDocument.load(data, {
|
||||
ignoreEncryption: true,
|
||||
@@ -385,53 +410,53 @@ export class PdfiumFormProvider implements IFormDataProvider {
|
||||
|
||||
const decodeText = (obj: unknown): string | null => {
|
||||
if (obj instanceof PDFString || obj instanceof PDFHexString)
|
||||
return obj.decodeText();
|
||||
return (obj as PDFString | PDFHexString).decodeText();
|
||||
if (obj instanceof PDFName)
|
||||
return (obj as any).asString?.() ?? obj.toString().replace(/^\//, "");
|
||||
return (obj as PDFName).asString() ?? String(obj).replace(/^\//, "");
|
||||
return null;
|
||||
};
|
||||
|
||||
const parseActionDict = (aObj: unknown): ButtonAction | null => {
|
||||
if (!(aObj instanceof PDFDict)) return null;
|
||||
const sObj = aObj.lookup(PDFName.of("S"));
|
||||
// @cantoo/pdf-lib ships without individual .d.ts files so instanceof can't narrow `unknown`
|
||||
const a = aObj as PDFDict;
|
||||
const sObj = a.lookup(PDFName.of("S"));
|
||||
if (!(sObj instanceof PDFName)) return null;
|
||||
const actionType: string =
|
||||
(sObj as any).asString?.() ?? sObj.toString().replace(/^\//, "");
|
||||
(sObj as PDFName).asString() ?? String(sObj).replace(/^\//, "");
|
||||
|
||||
switch (actionType) {
|
||||
case "Named": {
|
||||
const nObj = aObj.lookup(PDFName.of("N"));
|
||||
const nObj = a.lookup(PDFName.of("N"));
|
||||
const name =
|
||||
nObj instanceof PDFName
|
||||
? ((nObj as any).asString?.() ??
|
||||
nObj.toString().replace(/^\//, ""))
|
||||
? ((nObj as PDFName).asString() ??
|
||||
String(nObj).replace(/^\//, ""))
|
||||
: "";
|
||||
return { type: "named", namedAction: name };
|
||||
}
|
||||
case "JavaScript": {
|
||||
const jsObj = aObj.lookup(PDFName.of("JS"));
|
||||
const jsObj = a.lookup(PDFName.of("JS"));
|
||||
const js = decodeText(jsObj) ?? jsObj?.toString() ?? "";
|
||||
return { type: "javascript", javascript: js };
|
||||
}
|
||||
case "SubmitForm": {
|
||||
const fObj = aObj.lookup(PDFName.of("F"));
|
||||
const fObj = a.lookup(PDFName.of("F"));
|
||||
let url = "";
|
||||
if (fObj instanceof PDFDict) {
|
||||
url = decodeText(fObj.lookup(PDFName.of("F"))) ?? "";
|
||||
} else if (fObj) {
|
||||
url = decodeText(fObj) ?? fObj.toString();
|
||||
url = decodeText(fObj) ?? String(fObj);
|
||||
}
|
||||
const flagsObj = aObj.lookup(PDFName.of("Flags"));
|
||||
const flagsObj = a.lookup(PDFName.of("Flags"));
|
||||
const flags =
|
||||
typeof (flagsObj as any)?.asNumber === "function"
|
||||
? (flagsObj as any).asNumber()
|
||||
: 0;
|
||||
flagsObj instanceof PDFNumber ? flagsObj.asNumber() : 0;
|
||||
return { type: "submitForm", url, submitFlags: flags };
|
||||
}
|
||||
case "ResetForm":
|
||||
return { type: "resetForm" };
|
||||
case "URI": {
|
||||
const uriObj = aObj.lookup(PDFName.of("URI"));
|
||||
const uriObj = a.lookup(PDFName.of("URI"));
|
||||
return { type: "uri", url: decodeText(uriObj) ?? "" };
|
||||
}
|
||||
default:
|
||||
@@ -439,7 +464,7 @@ export class PdfiumFormProvider implements IFormDataProvider {
|
||||
}
|
||||
};
|
||||
|
||||
const getMkCaption = (dict: any): string | null => {
|
||||
const getMkCaption = (dict: PDFDict): string | null => {
|
||||
try {
|
||||
const mkObj = dict.lookup(PDFName.of("MK"));
|
||||
if (!(mkObj instanceof PDFDict)) return null;
|
||||
@@ -450,7 +475,7 @@ export class PdfiumFormProvider implements IFormDataProvider {
|
||||
}
|
||||
};
|
||||
|
||||
const getActionFromDict = (dict: any): ButtonAction | null => {
|
||||
const getActionFromDict = (dict: PDFDict): ButtonAction | null => {
|
||||
try {
|
||||
return parseActionDict(dict.lookup(PDFName.of("A")));
|
||||
} catch {
|
||||
@@ -465,13 +490,13 @@ export class PdfiumFormProvider implements IFormDataProvider {
|
||||
if (!buttonNames.has(name)) continue;
|
||||
|
||||
try {
|
||||
const acroField = (field as any).acroField;
|
||||
const acroField = (field as unknown as PDFFieldInternal).acroField;
|
||||
if (!acroField?.dict) continue;
|
||||
|
||||
const info: { label?: string; action?: ButtonAction } = {};
|
||||
|
||||
// Try widget dicts first (each widget can have its own /MK and /A)
|
||||
const widgets: any[] = (acroField as any).getWidgets?.() ?? [];
|
||||
const widgets = acroField.getWidgets?.() ?? [];
|
||||
for (const widget of widgets) {
|
||||
if (!info.label) {
|
||||
const label = getMkCaption(widget.dict);
|
||||
|
||||
@@ -25,7 +25,13 @@ export function usePrototypeToolRegistry(): PrototypeToolRegistry {
|
||||
() =>
|
||||
({
|
||||
pdfCommentAgent: {
|
||||
icon: <LocalIcon icon="add-comment" width="1.5rem" height="1.5rem" />,
|
||||
icon: (
|
||||
<LocalIcon
|
||||
icon="add-comment-outline-rounded"
|
||||
width="1.5rem"
|
||||
height="1.5rem"
|
||||
/>
|
||||
),
|
||||
name: t("home.pdfCommentAgent.title", "Add AI comments"),
|
||||
component: PdfCommentAgent,
|
||||
description: t(
|
||||
|
||||
Reference in New Issue
Block a user