fix(compress-pdf): land close to the target size, honestly (#522)

Target-size compression had only a coarse DPI lever, so it undershot badly (a 350KB target could land at 216KB) and silently missed unreachable targets. Adds JPEG quality as a second lever (forced re-encode so it bites on JPEG scans), folds both into one monotonic quality axis that target-size binary-searches, reports targetMet honestly in the panel across 21 locales, and flips the tool to async for the extra passes. Quality-mode output sizes shift intentionally (slider now drives JPEG quality at full resolution in its top half).
This commit is contained in:
SnapOtter
2026-07-16 15:08:21 +08:00
committed by GitHub
parent f858c4cea0
commit 7d938af1f9
30 changed files with 344 additions and 65 deletions
@@ -0,0 +1,30 @@
import { describe, expect, it } from "vitest";
import { paramsForQuality } from "../../../apps/api/src/routes/tools/compress-pdf.js";
describe("paramsForQuality", () => {
it("endpoints: q=100 preserves resolution at best quality; q=1 is smallest", () => {
const hi = paramsForQuality(100);
expect(hi.dpi).toBe(300);
expect(hi.qFactor).toBeCloseTo(0.1, 2);
const lo = paramsForQuality(1);
expect(lo.dpi).toBeLessThanOrEqual(30);
expect(lo.qFactor).toBeGreaterThan(2.0);
});
it("is monotonic in size: dpi non-decreasing and qFactor non-increasing as q rises", () => {
let prevDpi = 0;
let prevQf = Number.POSITIVE_INFINITY;
for (let q = 1; q <= 100; q++) {
const { dpi, qFactor } = paramsForQuality(q);
expect(dpi).toBeGreaterThanOrEqual(prevDpi);
expect(qFactor).toBeLessThanOrEqual(prevQf + 1e-9);
prevDpi = dpi;
prevQf = qFactor;
}
});
it("clamps out-of-range input", () => {
expect(paramsForQuality(0)).toEqual(paramsForQuality(1));
expect(paramsForQuality(200)).toEqual(paramsForQuality(100));
});
});