fix(dn): detect PDF content for page range extraction
This commit is contained in:
parent
81c8f78602
commit
80fbe15792
3 changed files with 51 additions and 4 deletions
|
|
@ -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
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue