fix(dn): detect PDF content for page range extraction

This commit is contained in:
DEls 2026-09-18 19:10:35 +02:00
parent 81c8f78602
commit 80fbe15792
3 changed files with 51 additions and 4 deletions

View file

@ -68,3 +68,40 @@ Exit status: 0.
- `npm install sharp` reports existing audit vulnerabilities and an unrelated
`isolated-vm` engine warning on Node 22; neither blocks this task's tests or
typecheck.
## Fix Round 1
Addressed review findings:
- `extractPageRange()` now detects PDF content using `%PDF-` magic bytes,
independent of declared MIME, matching the normalizer and split adapter.
- Updated `splitPages()` documentation to describe decoding and canonical PNG
output instead of pass-through behavior.
- Added a regression test proving a multi-page PDF declared as `image/jpeg`
extracts only the requested range.
RED evidence:
```text
npx vitest run src/lib/dn-pipeline/split.test.ts
...
× split (requires poppler + the fixture) > extracts a page range from a PDF despite an incorrect declared MIME
→ expected 4 to be 2
Test Files 1 failed (1)
Tests 1 failed | 5 passed (6)
```
GREEN and verification evidence:
```text
npx vitest run src/lib/dn-pipeline/normalise-source.test.ts src/lib/dn-pipeline/split.test.ts
Test Files 2 passed (2)
Tests 12 passed (12)
npm run typecheck
> mega-infrastructure-dashboard@1.0.0 typecheck
> tsc --noEmit
git diff --check
exit status: 0
```

View file

@ -31,6 +31,11 @@ describe.skipIf(!existsSync(FIXTURE))("split (requires poppler + the fixture)",
expect(await pageCount(out, PDF)).toBe(2);
});
it("extracts a page range from a PDF despite an incorrect declared MIME", async () => {
const out = await extractPageRange(buf, "image/jpeg", 2, 3);
expect(await pageCount(out, PDF)).toBe(2);
});
it("rejects a range outside the document", async () => {
await expect(extractPageRange(buf, PDF, 3, 9)).rejects.toThrow("out of range");
});

View file

@ -15,6 +15,11 @@ import { promisify } from "node:util";
import { normaliseSource } from "./normalise-source";
const run = promisify(execFile);
const PDF_MAGIC = Buffer.from("%PDF-");
function isPdf(buffer: Buffer): boolean {
return buffer.subarray(0, PDF_MAGIC.length).equals(PDF_MAGIC);
}
export interface PageImage {
page: number;
@ -23,7 +28,7 @@ export interface PageImage {
/** Page count of a PDF. Images are always one page. */
export async function pageCount(buffer: Buffer, mime: string): Promise<number> {
if (!buffer.subarray(0, 5).equals(Buffer.from("%PDF-"))) return 1;
if (!isPdf(buffer)) return 1;
const dir = await mkdtemp(join(tmpdir(), "dn-count-"));
try {
const path = join(dir, "in.pdf");
@ -38,8 +43,8 @@ export async function pageCount(buffer: Buffer, mime: string): Promise<number> {
}
/**
* Render every page to PNG. An image upload passes through untouched as
* a single page.
* Decode and render every page to canonical PNG. An image upload becomes one
* decoded, orientation-corrected PNG page.
*/
export async function splitPages(buffer: Buffer, mime: string): Promise<PageImage[]> {
const pages = await normaliseSource(buffer, mime);
@ -57,7 +62,7 @@ export async function extractPageRange(
pageFrom: number,
pageTo: number,
): Promise<Buffer> {
if (mime !== "application/pdf") return buffer;
if (!isPdf(buffer)) return buffer;
const src = await PDFDocument.load(buffer);
const out = await PDFDocument.create();