mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix(pdf): flag scanned PDFs in pdf-to-text and serve text as UTF-8 (#603)
When a PDF has no text layer (scanned or image-only), pdf-to-text now returns a 422 that points at the OCR tool instead of a silent empty file, and text downloads carry charset=utf-8 so UTF-8 Arabic renders correctly when the .txt is viewed inline. Fixes #589
This commit is contained in:
@@ -227,7 +227,7 @@ export async function fileRoutes(app: FastifyInstance): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
function getContentType(ext: string): string {
|
||||
export function getContentType(ext: string): string {
|
||||
const map: Record<string, string> = {
|
||||
jpg: "image/jpeg",
|
||||
jpeg: "image/jpeg",
|
||||
@@ -303,5 +303,9 @@ function getContentType(ext: string): string {
|
||||
heic: "image/heic",
|
||||
heif: "image/heif",
|
||||
};
|
||||
return map[ext] ?? "application/octet-stream";
|
||||
const type = map[ext] ?? "application/octet-stream";
|
||||
// Text payloads (extracted text, markdown, CSV, subtitles) are written UTF-8.
|
||||
// Without an explicit charset a browser can sniff a legacy encoding and
|
||||
// mojibake non-Latin scripts like Arabic when the file is viewed inline (#589).
|
||||
return type.startsWith("text/") ? `${type}; charset=utf-8` : type;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
||||
import { pdfTextPy } from "@snapotter/doc-engine";
|
||||
import type { FastifyInstance } from "fastify";
|
||||
import { z } from "zod";
|
||||
import { InputValidationError } from "../../modality/contract.js";
|
||||
import { createToolRoute } from "../tool-factory.js";
|
||||
|
||||
const settingsSchema = z.object({});
|
||||
@@ -23,6 +24,15 @@ export function registerPdfToText(app: FastifyInstance) {
|
||||
const outPath = join(ctx.scratchDir, `${base}.txt`);
|
||||
ctx.report(10, "Extracting text");
|
||||
const result = await pdfTextPy(inPath, outPath);
|
||||
// A scanned or image-only PDF has no text layer, so extraction returns an
|
||||
// empty file. Rather than hand back a silent 0-byte "success", tell the
|
||||
// user to run OCR instead (#589).
|
||||
if (!result.hasText) {
|
||||
throw new InputValidationError(
|
||||
"This PDF has no extractable text layer. It looks scanned or image-only, so use the PDF OCR tool to read its text.",
|
||||
422,
|
||||
);
|
||||
}
|
||||
ctx.report(90, "Done");
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user