feat: anti-alias Ghostscript rasterization to improve OCR quality

Ghostscript 10.x renders aliased glyphs that OCR frequently misreads as
extra word breaks or substituted characters. Enable text and graphics
anti-aliasing (-dTextAlphaBits=4 -dGraphicsAlphaBits=4) for the contone
raster devices, which empirically improves OCR accuracy on the
Ghostscript path, especially for small fonts at moderate DPI. The 1-bit
mono devices are excluded, since older Ghostscript rejects alpha bits on
them and pngmonod performs its own anti-aliased downscaling.

Also log which rasterizer rendered each page at debug verbosity and
clarify the --rasterizer help text, so quality reports are easier to
diagnose. The default rasterizer (auto) already prefers pypdfium2, which
anti-aliases; this primarily benefits --rasterizer ghostscript and
installs without pypdfium2.

Closes #1439.
This commit is contained in:
James R. Barlow
2026-06-10 23:39:03 -07:00
parent 58642d8411
commit 11de13ecfe
6 changed files with 95 additions and 3 deletions
+56
View File
@@ -141,6 +141,62 @@ def test_rasterize_low_dpi_one_axis(francais, outdir):
assert im.info['dpi'] == forced_dpi
def _capture_rasterize_args(resources, outdir, raster_device):
"""Run rasterize_pdf with the gs subprocess mocked; return the gs argv."""
out = outdir / 'out.png'
captured = {}
def fake_run(args, **kwargs):
captured['args'] = list(args)
# Produce a valid PNG so rasterize_pdf's post-processing succeeds.
Image.new('RGB', (2, 2)).save(out)
return subprocess.CompletedProcess(args, returncode=0, stdout=b'', stderr=b'')
with patch('ocrmypdf._exec.ghostscript.run', side_effect=fake_run):
rasterize_pdf(
resources / 'francais.pdf',
out,
raster_device=raster_device,
raster_dpi=Resolution(150.0, 150.0),
)
return captured['args']
@pytest.mark.parametrize(
'raster_device',
[
GhostscriptRasterDevice.PNGGRAY,
GhostscriptRasterDevice.PNG256,
GhostscriptRasterDevice.PNG16M,
],
)
def test_rasterize_antialiases_contone_devices(resources, outdir, raster_device):
"""Contone raster devices receive anti-aliasing flags to aid OCR.
Ghostscript 10.x renders aliased glyphs that OCR misreads as extra word
breaks; -dTextAlphaBits/-dGraphicsAlphaBits markedly improve accuracy,
especially for small fonts at moderate DPI (see issue #1439).
"""
args = _capture_rasterize_args(resources, outdir, raster_device)
assert '-dTextAlphaBits=4' in args
assert '-dGraphicsAlphaBits=4' in args
@pytest.mark.parametrize(
'raster_device',
[GhostscriptRasterDevice.PNGMONO, GhostscriptRasterDevice.PNGMONOD],
)
def test_rasterize_no_antialias_on_mono_devices(resources, outdir, raster_device):
"""1-bit mono devices must not receive alpha-bit flags.
Older Ghostscript versions reject -dTextAlphaBits on 1-bit devices, and
pngmonod performs its own anti-aliased downscaling.
"""
args = _capture_rasterize_args(resources, outdir, raster_device)
assert not any(a.startswith('-dTextAlphaBits') for a in args)
assert not any(a.startswith('-dGraphicsAlphaBits') for a in args)
def test_generate_pdfa_default_jpeg_quality(outdir):
"""When jpeg_quality is None, Ghostscript receives -dJPEGQ=95 (default)."""
with (