fix(scanner): render the PDF CropBox, not the MediaBox, in the cover fallback

pdftoppm defaults to rasterizing the MediaBox, while PDF viewers (pdf.js
in the reader, and every other viewer) display the CropBox. For PDFs
whose page 1 is the full print cover wrap (back cover + spine + front
cover in one landscape page) with a CropBox covering only the front
cover - e.g. No Starch's XeTeX-built 'Algorithmic Thinking' - the
fallback stored the entire spread as a squashed landscape cover, while
the reader correctly showed just the front cover.

Pass -cropbox so the rendered cover always matches what the reader
displays. Poppler falls back to the MediaBox when a PDF defines no
CropBox, so PDFs with identical boxes (the common case) render exactly
as before.
This commit is contained in:
John O'Keefe
2026-09-12 14:21:24 -04:00
parent da1f689263
commit 9c8337d0a8
+5 -1
View File
@@ -2147,7 +2147,11 @@ func (s *MediaScanner) renderPDFCoverPage(pdfPath string) string {
}() }()
outPrefix := filepath.Join(tmpDir, "cover") outPrefix := filepath.Join(tmpDir, "cover")
cmd := exec.Command("pdftoppm", "-jpeg", "-f", "1", "-l", "1", "-singlefile", "-r", "150", pdfPath, outPrefix) // -cropbox renders the CropBox (the viewer-visible region, matching pdf.js)
// rather than the MediaBox; poppler falls back to the MediaBox when no
// CropBox is defined. This matters for PDFs whose page 1 is a full print
// cover wrap (back + spine + front) with a CropBox covering just the front.
cmd := exec.Command("pdftoppm", "-jpeg", "-f", "1", "-l", "1", "-singlefile", "-cropbox", "-r", "150", pdfPath, outPrefix)
if output, err := cmd.CombinedOutput(); err != nil { if output, err := cmd.CombinedOutput(); err != nil {
fmt.Printf("Warning: failed to render PDF cover from %s: %v, output: %s\n", pdfPath, err, string(output)) fmt.Printf("Warning: failed to render PDF cover from %s: %v, output: %s\n", pdfPath, err, string(output))
return "" return ""