mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(tools): 2.0 phase 5 wave 2 - pdf depth (21 tools) (#220)
This commit is contained in:
@@ -3,38 +3,20 @@ import { resolveGs } from "./binaries.js";
|
||||
|
||||
export type PdfCompressionPreset = "screen" | "ebook" | "printer";
|
||||
|
||||
/** Ghostscript re-distillation with a quality preset; 120s hard kill. */
|
||||
export async function gsCompressPdf(
|
||||
inputPath: string,
|
||||
outPath: string,
|
||||
preset: PdfCompressionPreset,
|
||||
): Promise<void> {
|
||||
/** @internal Shared gs CLI runner; not part of the public package API. */
|
||||
function runGs(args: string[], timeoutMs = 120_000): Promise<void> {
|
||||
const bin = resolveGs();
|
||||
if (!bin) throw new Error("gs binary not found (set GS_PATH or install ghostscript)");
|
||||
await new Promise<void>((resolvePromise, reject) => {
|
||||
const child = spawn(
|
||||
bin,
|
||||
[
|
||||
"-dSAFER",
|
||||
"-dBATCH",
|
||||
"-dNOPAUSE",
|
||||
"-dQUIET",
|
||||
"-sDEVICE=pdfwrite",
|
||||
`-dPDFSETTINGS=/${preset}`,
|
||||
"-dCompatibilityLevel=1.6",
|
||||
`-sOutputFile=${outPath}`,
|
||||
inputPath,
|
||||
],
|
||||
{ stdio: ["ignore", "ignore", "pipe"] },
|
||||
);
|
||||
return new Promise<void>((resolvePromise, reject) => {
|
||||
const child = spawn(bin, args, { stdio: ["ignore", "ignore", "pipe"] });
|
||||
let err = "";
|
||||
let settled = false;
|
||||
const timer = setTimeout(() => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
child.kill("SIGKILL");
|
||||
reject(new Error("ghostscript timed out after 120s"));
|
||||
}, 120_000);
|
||||
reject(new Error(`ghostscript timed out after ${Math.round(timeoutMs / 1000)}s`));
|
||||
}, timeoutMs);
|
||||
child.stderr.on("data", (c: Buffer) => {
|
||||
err = (err + c.toString("utf8")).slice(-4096);
|
||||
});
|
||||
@@ -53,3 +35,53 @@ export async function gsCompressPdf(
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Ghostscript re-distillation with a quality preset. */
|
||||
export async function gsCompressPdf(
|
||||
inputPath: string,
|
||||
outPath: string,
|
||||
preset: PdfCompressionPreset,
|
||||
): Promise<void> {
|
||||
await runGs([
|
||||
"-dSAFER",
|
||||
"-dBATCH",
|
||||
"-dNOPAUSE",
|
||||
"-dQUIET",
|
||||
"-sDEVICE=pdfwrite",
|
||||
`-dPDFSETTINGS=/${preset}`,
|
||||
"-dCompatibilityLevel=1.6",
|
||||
`-sOutputFile=${outPath}`,
|
||||
inputPath,
|
||||
]);
|
||||
}
|
||||
|
||||
/** Grayscale re-distillation via DeviceGray color conversion. */
|
||||
export async function gsGrayscalePdf(inputPath: string, outPath: string): Promise<void> {
|
||||
await runGs([
|
||||
"-dSAFER",
|
||||
"-dBATCH",
|
||||
"-dNOPAUSE",
|
||||
"-dQUIET",
|
||||
"-sDEVICE=pdfwrite",
|
||||
"-sColorConversionStrategy=Gray",
|
||||
"-dProcessColorModel=/DeviceGray",
|
||||
`-sOutputFile=${outPath}`,
|
||||
inputPath,
|
||||
]);
|
||||
}
|
||||
|
||||
/** PDF/A-2b candidate via ghostscript's PDFA switch (no veraPDF validation this wave). */
|
||||
export async function gsPdfaConvert(inputPath: string, outPath: string): Promise<void> {
|
||||
await runGs([
|
||||
"-dSAFER",
|
||||
"-dBATCH",
|
||||
"-dNOPAUSE",
|
||||
"-dQUIET",
|
||||
"-dPDFA=2",
|
||||
"-dPDFACompatibilityPolicy=1",
|
||||
"-sColorConversionStrategy=RGB",
|
||||
"-sDEVICE=pdfwrite",
|
||||
`-sOutputFile=${outPath}`,
|
||||
inputPath,
|
||||
]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user