diff --git a/docs/API.md b/docs/API.md
index 44793dd8..1fc61eaa 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -1005,10 +1005,10 @@ only which field names were given.
The answer is `{"filename", "pdf", "report"}`: the file name to save as, the PDF in base64, and the
tagger's report. `report.warnings` lists what the tagger could not do cleanly, such as a form field
-it could not find in the HTML. The demo page turns each warning into a plain sentence.
-
-A PDF that is already tagged gets `422 already_tagged`. Send `"retag": true` to replace its tags
-with tags from the HTML. The report then has a `retagged` warning.
+it could not find in the HTML. The demo page turns each warning into a plain sentence. A PDF that
+was already tagged has its old tags replaced, with a `retagged` warning. This needs iris-pdf with
+[equalify-iris-pdf#8](https://github.com/EqualifyEverything/equalify-iris-pdf/pull/8). An older one
+answers `422 already_tagged`.
The session must have finished (`ready_for_review` or `closed`), or you get `409 invalid_state`. The
PDF is tagged from the first pass's HTML for each page, before review. A page that failed extraction is
@@ -4368,10 +4368,9 @@ pages reported blank, which an empty document could not.
### `tagged_pdf` / `tagged_pdf_failed`
A [tagged PDF](#get-a-tagged-pdf-optional) was made, or the tagger refused. `ms` is how long it took.
-`tagged_pdf` lists `fields_given`, the names of the fields that were filled in, and `retag`. The
-values are never logged. `tagged_pdf_failed` has the tagger's `code` and `error`. An `internal_error`'s
-message is left out, because it may quote a value. The demo asks before it retags, so a demo retag
-logs `tagged_pdf_failed` with `already_tagged` first, then `tagged_pdf` with `retag: true`.
+`tagged_pdf` lists `fields_given`, the names of the fields that were filled in. The values are
+never logged. `tagged_pdf_failed` has the tagger's `code` and `error`. An `internal_error`'s
+message is left out, because it may quote a value.
### `form_fields` / `page_fields` / `page_fields_missing`
diff --git a/public/demo.html b/public/demo.html
index d200ae16..441822cd 100644
--- a/public/demo.html
+++ b/public/demo.html
@@ -577,24 +577,10 @@
About
$('pdf-btn').disabled = true;
live('Tagging your PDF. This can take a minute.');
try {
- const values = fieldValues($('pdf-field-list'));
- const post = (retag) => api('/sessions/' + sessionId + '/pdf', {
+ const res = await api('/sessions/' + sessionId + '/pdf', {
method: 'POST', headers: { 'content-type': 'application/json' },
- body: JSON.stringify({ values, retag }),
+ body: JSON.stringify({ values: fieldValues($('pdf-field-list')) }),
});
- let res = await post(false);
- // A PDF that is already tagged is only retagged if the reader says so.
- if (res.status === 422) {
- const code = await res.clone().json().then((j) => j.error && j.error.code, () => null);
- if (code === 'already_tagged') {
- if (!confirm('This PDF is already tagged. Retagging replaces its existing tags with tags from Iris\'s HTML. Retag it?')) {
- live('The PDF was not retagged.');
- return;
- }
- live('Retagging your PDF.');
- res = await post(true);
- }
- }
if (!res.ok) throw new Error(await errMessage(res));
const out = await res.json();
const bytes = Uint8Array.from(atob(out.pdf), (c) => c.charCodeAt(0));
diff --git a/src/routes/sessions.ts b/src/routes/sessions.ts
index 2996133c..527db36f 100644
--- a/src/routes/sessions.ts
+++ b/src/routes/sessions.ts
@@ -634,11 +634,6 @@ export function sessionsRouter(cfg: IrisConfig, store: Store): Router {
sendError(res, 400, "invalid_request", "`values` must be an object of field name to value.");
return;
}
- const retag: unknown = (req.body ?? {}).retag ?? false;
- if (typeof retag !== "boolean") {
- sendError(res, 400, "invalid_request", "`retag` must be true or false.");
- return;
- }
if (tooBusy(res)) return;
const log = new RunLog(paths.sessionLog(s.session_id));
const started = Date.now();
@@ -665,12 +660,11 @@ export function sessionsRouter(cfg: IrisConfig, store: Store): Router {
pdfPath: src.pdf,
input,
values: values as Record,
- retag,
scratchRoot: paths.pdfScratchRoot(),
timeoutSeconds: tagTimeoutSeconds(cfg),
});
// Field names only. A value is never logged.
- log.event("tagged_pdf", { ms: Date.now() - started, fields_given: Object.keys(values), retag });
+ log.event("tagged_pdf", { ms: Date.now() - started, fields_given: Object.keys(values) });
res.json({ filename: `${base}_tagged.pdf`, pdf: pdf.toString("base64"), report });
} catch (e) {
// iris-pdf's own messages name fields and options, never values. Its
diff --git a/src/util/taggedPdf.ts b/src/util/taggedPdf.ts
index 76138089..c17c8a96 100644
--- a/src/util/taggedPdf.ts
+++ b/src/util/taggedPdf.ts
@@ -121,7 +121,7 @@ export type TagInput = { lang?: string; title?: string; pages: { sourcePage: num
// neither them nor the tagged PDF.
export async function tagPdf(
command: string,
- args: { pdfPath: string; input: TagInput; values: Record; retag?: boolean; scratchRoot: string; timeoutSeconds: number },
+ args: { pdfPath: string; input: TagInput; values: Record; scratchRoot: string; timeoutSeconds: number },
): Promise<{ pdf: Buffer; report: unknown }> {
mkdirSync(args.scratchRoot, { recursive: true });
const dir = mkdtempSync(join(args.scratchRoot, "pdf-"));
@@ -130,8 +130,6 @@ export async function tagPdf(
writeFileSync(f("pages.json"), JSON.stringify(args.input));
writeFileSync(f("values.json"), JSON.stringify(args.values), { mode: 0o600 });
const argv = ["tag", "--pdf", args.pdfPath, "--pages", f("pages.json"), "--values", f("values.json"), "--out", f("out.pdf"), "--report", f("report.json")];
- // An already-tagged PDF is refused (`already_tagged`) unless the user asked to replace its tags.
- if (args.retag) argv.push("--retag");
try {
await execFileP(command, argv, { maxBuffer: MAX_BUFFER, timeout: args.timeoutSeconds * 1000 });
} catch (e) {
diff --git a/test/demo-pdf-form.test.ts b/test/demo-pdf-form.test.ts
index 4d4c1e31..5153d005 100644
--- a/test/demo-pdf-form.test.ts
+++ b/test/demo-pdf-form.test.ts
@@ -95,12 +95,14 @@ test("the report's warnings come back as one sentence per kind", () => {
{ code: "missing_alt", page: 2 },
{ code: "field_not_in_html", page: 1 },
{ code: "no_title" },
+ { code: "retagged" },
{ code: "something_new" },
],
});
assert.deepEqual(notes, [
"2 images have no description.",
"One form field was not found in the HTML, so a screen reader reaches it at the end of its page.",
+ "The PDF's old tags were replaced with tags from the HTML.",
"Note from the tagger: something_new.",
]);
assert.deepEqual(pdfNotes({ warnings: [] }), []);
@@ -130,40 +132,19 @@ test("a late /fields reply is dropped after a new document, a re-run, or a newer
});
// The submit handler, run against stand-ins for the page and the server.
-function submitHandler(replies: { status: number; body: unknown }[], answer: boolean) {
+test("the demo sends only the values, and shows a refusal as an error", async () => {
const start = demoHtml.indexOf("async (ev) =>", demoHtml.indexOf("$('pdf-form').addEventListener('submit'"));
const end = demoHtml.lastIndexOf("});", demoHtml.indexOf("$('restart-btn')"));
- const sent: unknown[] = [], said: string[] = [], asked: string[] = [], errors: string[] = [];
+ const sent: unknown[] = [], errors: string[] = [];
const api = async (_path: string, opts: { body: string }) => {
sent.push(JSON.parse(opts.body));
- const r = replies.shift()!;
- const res = { ok: r.status < 400, status: r.status, json: async () => r.body, clone: () => res };
- return res;
+ return { ok: false, status: 422, json: async () => ({ error: { code: "encrypted", message: "The PDF is encrypted." } }) };
};
- const $ = () => ({ disabled: false });
const handler = new Function(
- "$", "sessionId", "api", "confirm", "live", "setError", "fieldValues", "errMessage",
+ "$", "sessionId", "api", "live", "setError", "fieldValues", "errMessage",
`return ${demoHtml.slice(start, end)}};`,
- )($, "ses_1", api, (q: string) => (asked.push(q), answer), (m: string) => said.push(m), (m: string) => m && errors.push(m), () => ({}), async (res: { json: () => Promise<{ error: { message: string } }> }) => (await res.json()).error.message);
- return { run: () => handler({ preventDefault() {} }), sent, said, asked, errors };
-}
-
-test("an already-tagged PDF is retagged only after the reader says yes", async () => {
- const already = { status: 422, body: { error: { code: "already_tagged", message: "The PDF is already tagged." } } };
- const no = submitHandler([already], false);
- await no.run();
- assert.deepEqual(no.sent, [{ values: {}, retag: false }]);
- assert.equal(no.asked.length, 1);
- assert.deepEqual(no.errors, []);
- assert.equal(no.said.at(-1), "The PDF was not retagged.");
-
- const yes = submitHandler([already, { status: 500, body: { error: { message: "stop here" } } }], true);
- await yes.run();
- assert.deepEqual(yes.sent, [{ values: {}, retag: false }, { values: {}, retag: true }]);
- assert.deepEqual(yes.errors, ["Could not tag the PDF: stop here"]);
-
- // Any other refusal is shown, not asked about.
- const other = submitHandler([{ status: 422, body: { error: { code: "encrypted", message: "The PDF is encrypted." } } }], true);
- await other.run();
- assert.deepEqual([other.asked, other.sent.length, other.errors], [[], 1, ["Could not tag the PDF: The PDF is encrypted."]]);
+ )(() => ({ disabled: false }), "ses_1", api, () => {}, (m: string) => m && errors.push(m), () => ({ name: "Ada" }), async (res: { json: () => Promise<{ error: { message: string } }> }) => (await res.json()).error.message);
+ await handler({ preventDefault() {} });
+ assert.deepEqual(sent, [{ values: { name: "Ada" } }]);
+ assert.deepEqual(errors, ["Could not tag the PDF: The PDF is encrypted."]);
});
diff --git a/test/fixtures/fake-iris-pdf.mjs b/test/fixtures/fake-iris-pdf.mjs
index 50a06142..bc02045b 100755
--- a/test/fixtures/fake-iris-pdf.mjs
+++ b/test/fixtures/fake-iris-pdf.mjs
@@ -8,7 +8,7 @@ const [command, ...rest] = process.argv.slice(2);
const { values: a } = parseArgs({
args: rest,
strict: false,
- options: { pdf: { type: "string" }, pages: { type: "string" }, values: { type: "string" }, out: { type: "string" }, report: { type: "string" }, json: { type: "boolean" }, retag: { type: "boolean" }, help: { type: "boolean" } },
+ options: { pdf: { type: "string" }, pages: { type: "string" }, values: { type: "string" }, out: { type: "string" }, report: { type: "string" }, json: { type: "boolean" }, help: { type: "boolean" } },
});
const fail = (code, message, exit) => {
process.stderr.write(`iris-pdf: ${code}: ${message}\n`);
@@ -31,7 +31,6 @@ if (command === "fields") {
: []),
]));
} else if (command === "tag") {
- if (pdf.includes("TAGGED") && !a.retag) fail("already_tagged", "The PDF is already tagged. Pass --retag to replace its tags.", 1);
const values = JSON.parse(readFileSync(a.values, "utf8"));
if ("unknown.field" in values) fail("bad_value", "No field is named unknown.field.", 3);
if ("crash" in values) fail("internal_error", `Cannot set ${values.crash}.`, 3);
@@ -39,7 +38,7 @@ if (command === "fields") {
const input = JSON.parse(readFileSync(a.pages, "utf8"));
const mode = (statSync(a.values).mode & 0o777).toString(8);
writeFileSync(a.out, "%PDF-fake " + JSON.stringify({ source: pdf, input, values, mode, scratch: a.values }));
- writeFileSync(a.report, JSON.stringify({ tool: "iris-pdf 0.0.0-fake", warnings: [{ code: "duplicate_text_layer", page: 1 }, ...(a.retag ? [{ code: "retagged" }] : [])] }));
+ writeFileSync(a.report, JSON.stringify({ tool: "iris-pdf 0.0.0-fake", warnings: [{ code: "duplicate_text_layer", page: 1 }] }));
} else {
fail("bad_arguments", `Unknown command ${command}.`, 3);
}
diff --git a/test/tagged-pdf.test.ts b/test/tagged-pdf.test.ts
index 5607630a..df884f43 100644
--- a/test/tagged-pdf.test.ts
+++ b/test/tagged-pdf.test.ts
@@ -248,22 +248,6 @@ test("POST /pdf passes the tagger's refusals on with its code", async () => {
}
});
-test("POST /pdf refuses an already-tagged PDF unless asked to retag it", async () => {
- const s = await serve({ source: "%PDF TAGGED" });
- try {
- const refused = await s.tag({ values: {} });
- assert.equal(refused.status, 422);
- assert.equal((await refused.json()).error.code, "already_tagged");
- assert.equal((await s.tag({ retag: "yes" })).status, 400);
- const res = await s.tag({ values: {}, retag: true });
- assert.equal(res.status, 200);
- assert.deepEqual((await res.json()).report.warnings.map((w: { code: string }) => w.code), ["duplicate_text_layer", "retagged"]);
- assert.match(readFileSync(s.paths.sessionLog(s.id), "utf8"), /"tagged_pdf".*"retag":true/);
- } finally {
- s.close();
- }
-});
-
test("POST /pdf waits for the finished document and stops a run that takes too long", async () => {
const running = await serve({ status: "running" });
try {