From f549201b7cd9db831c93def215f124ef8115a147 Mon Sep 17 00:00:00 2001 From: stumpylog <797416+stumpylog@users.noreply.github.com> Date: Wed, 7 Oct 2026 14:32:54 -0700 Subject: [PATCH] Chore: Tighten thumbnail supersampling edge cases Pages so large that the computed DPI hit the floor of 1 were still supersampled to 2 DPI, quadrupling the pixel count pdftoppm had to render for an already oversized page. Supersampling is now skipped at that floor. The downsample test also did not prove the factor was applied, because the final 500x5000 clamp hid it in most cases. A 900x1200 render now has to come out at 450x600, which only happens when the downsample runs. The comments on the DPI cap and the clamp are reworded to describe the supersampled pipeline accurately. --- src/documents/parsers.py | 13 ++++++++----- src/documents/tests/test_parsers.py | 10 ++++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/documents/parsers.py b/src/documents/parsers.py index 75256bbed..a669c20ac 100644 --- a/src/documents/parsers.py +++ b/src/documents/parsers.py @@ -79,7 +79,8 @@ _THUMBNAIL_MAX_WIDTH = 500 _THUMBNAIL_MAX_HEIGHT = 5000 # Used only when the page geometry cannot be read _THUMBNAIL_FALLBACK_DPI = 150 -# Matches the density the thumbnail was previously rendered at before scaling +# Cap on the target density, applied before supersampling: a capped tiny page +# is rasterized at twice this and halved to the size a 300 DPI render gave _THUMBNAIL_MAX_DPI = 300 # Pages with known geometry are rendered at this multiple of the computed DPI # and downsampled with Lanczos, which keeps text noticeably crisper than @@ -196,14 +197,16 @@ def _compute_thumbnail_dpi(in_path: Path, logging_group=None) -> tuple[int, int] dpi_for_height = _THUMBNAIL_MAX_HEIGHT * 72 / height_pts # The old pipeline rendered at 300 DPI and then shrank to fit, so only # pages too small to reach the thumbnail size even at 300 DPI end up - # smaller than it. Rounding up keeps the render at or above the target, - # so the shrink-only clamp in encode_thumbnail_webp trims it to exactly - # the thumbnail size instead of leaving it a few pixels short. + # smaller than it. Rounding up keeps the downsampled render at or above + # the target, so the shrink-only clamp in encode_thumbnail_webp trims it + # to exactly the thumbnail size instead of leaving it a few pixels short. dpi = max( 1, math.ceil(min(_THUMBNAIL_MAX_DPI, dpi_for_width, dpi_for_height)), ) - return dpi, _THUMBNAIL_SUPERSAMPLE + # A page so large it hits the 1 DPI floor is already oversized, so + # doubling it would only quadruple the pixel count for no benefit + return dpi, 1 if dpi == 1 else _THUMBNAIL_SUPERSAMPLE def _render_pdf_thumbnail( diff --git a/src/documents/tests/test_parsers.py b/src/documents/tests/test_parsers.py index 3531cec92..23155b60e 100644 --- a/src/documents/tests/test_parsers.py +++ b/src/documents/tests/test_parsers.py @@ -147,7 +147,11 @@ class TestComputeThumbnailDpi: pytest.param((612.0, 100000.0), (4, 2), id="tall-strip-height-bound"), pytest.param((200.0, 300.0), (180, 2), id="small-page-width-bound"), pytest.param((72.0, 72.0), (300, 2), id="tiny-page-capped-at-300"), - pytest.param((1000000.0, 1000000.0), (1, 2), id="huge-page-minimum-one"), + pytest.param( + (1000000.0, 1000000.0), + (1, 1), + id="huge-page-minimum-one-unsupersampled", + ), pytest.param(None, (150, 1), id="unreadable-geometry-fallback"), ], ) @@ -166,7 +170,8 @@ class TestComputeThumbnailDpi: - The thumbnail DPI is computed THEN: - The DPI is rounded up so the render reaches 500x5000, never - exceeds 300, is at least 1, and is supersampled 2x; an unknown + exceeds 300, is at least 1, and is supersampled 2x (except at the + 1 DPI floor); an unknown size gives the plain 150 DPI fallback without supersampling """ mocker.patch( @@ -491,6 +496,7 @@ class TestEncodeThumbnailWebp: pytest.param((1001, 1401), 2, (500, 700), id="2x-odd-rounded"), pytest.param((1000, 1400), 1, (500, 700), id="no-supersample-clamped"), pytest.param((600, 800), 2, (300, 400), id="2x-small-not-enlarged"), + pytest.param((900, 1200), 2, (450, 600), id="2x-below-clamp"), ], ) def test_supersampled_render_downsampled(