OCRmyPDF/tests/test_main.py

885 lines
24 KiB
Python
Raw Normal View History

2019-08-09 01:23:49 -07:00
# © 2015-19 James R. Barlow: github.com/jbarlow83
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
2015-07-22 02:59:25 -07:00
import os
2015-07-22 04:00:59 -07:00
import shutil
2018-12-30 01:28:15 -08:00
from math import isclose
from pathlib import Path
2019-08-09 01:23:49 -07:00
from subprocess import PIPE, run
2019-12-09 15:04:37 -08:00
from unittest.mock import patch
2018-05-01 23:51:35 -07:00
2019-12-09 15:52:38 -08:00
import pikepdf
2018-05-01 23:51:35 -07:00
import PIL
import pytest
2018-12-30 01:28:15 -08:00
from PIL import Image
2018-05-01 23:51:35 -07:00
import ocrmypdf
2020-06-09 14:55:54 -07:00
from ocrmypdf._exec import ghostscript, tesseract
2018-11-10 00:56:22 -08:00
from ocrmypdf.exceptions import ExitCode, MissingDependencyError
2018-12-30 01:28:15 -08:00
from ocrmypdf.pdfa import file_claims_pdfa
from ocrmypdf.pdfinfo import Colorspace, Encoding, PdfInfo
from ocrmypdf.subprocess import get_version
2021-04-07 01:56:51 -07:00
from .conftest import (
check_ocrmypdf,
first_page_dimensions,
have_unpaper,
is_macos,
run_ocrmypdf,
run_ocrmypdf_api,
running_in_docker,
running_in_travis,
)
2015-07-22 11:21:33 -07:00
2021-04-07 01:56:51 -07:00
# pylint: disable=redefined-outer-name
2015-12-17 11:36:47 -08:00
2018-05-01 17:31:34 -07:00
RENDERERS = ['hocr', 'sandwich']
def test_quick(resources, outpdf):
check_ocrmypdf(
resources / 'ccitt.pdf', outpdf, '--plugin', 'tests/plugins/tesseract_cache.py'
)
@pytest.mark.parametrize('renderer', RENDERERS)
def test_oversample(renderer, resources, outpdf):
oversampled_pdf = check_ocrmypdf(
2018-12-30 01:27:49 -08:00
resources / 'skew.pdf',
outpdf,
'--oversample',
'350',
'-f',
2018-12-30 01:27:49 -08:00
'--pdf-renderer',
renderer,
'--plugin',
'tests/plugins/tesseract_cache.py',
2018-12-30 01:27:49 -08:00
)
pdfinfo = PdfInfo(oversampled_pdf)
2020-04-24 04:12:05 -07:00
print(pdfinfo[0].dpi.x)
assert abs(pdfinfo[0].dpi.x - 350) < 1
def test_repeat_ocr(resources, no_outpdf):
result = run_ocrmypdf_api(resources / 'graph_ocred.pdf', no_outpdf)
assert result == ExitCode.already_done_ocr
def test_force_ocr(resources, outpdf):
2018-12-30 01:27:49 -08:00
out = check_ocrmypdf(
resources / 'graph_ocred.pdf',
outpdf,
'-f',
'--plugin',
'tests/plugins/tesseract_cache.py',
2018-12-30 01:27:49 -08:00
)
pdfinfo = PdfInfo(out)
assert pdfinfo[0].has_text
def test_skip_ocr(resources, outpdf):
2018-12-30 01:27:49 -08:00
out = check_ocrmypdf(
resources / 'graph_ocred.pdf',
outpdf,
'-s',
'--plugin',
'tests/plugins/tesseract_cache.py',
2018-12-30 01:27:49 -08:00
)
2018-05-01 23:16:41 -07:00
pdfinfo = PdfInfo(out)
assert pdfinfo[0].has_text
2015-07-28 01:47:30 -07:00
2019-09-20 17:17:11 -07:00
def test_redo_ocr(resources, outpdf):
2018-11-04 15:54:41 -08:00
in_ = resources / 'graph_ocred.pdf'
before = PdfInfo(in_, detailed_analysis=True)
2019-06-01 01:55:51 -07:00
out = outpdf
out = check_ocrmypdf(in_, out, '--redo-ocr')
after = PdfInfo(out, detailed_analysis=True)
2018-11-04 15:54:41 -08:00
assert before[0].has_text and after[0].has_text
2018-12-30 01:27:49 -08:00
assert (
before[0].get_textareas() != after[0].get_textareas()
), "Expected text to be different after re-OCR"
2018-11-04 15:54:41 -08:00
2020-06-01 03:06:40 -07:00
def test_argsfile(resources, outdir):
2017-01-26 17:53:27 -08:00
path_argsfile = outdir / 'test_argsfile.txt'
with open(str(path_argsfile), 'w') as argsfile:
2018-12-30 01:27:49 -08:00
print(
'--title',
'ArgsFile Test',
'--author',
'Test Cases',
2020-06-01 03:06:40 -07:00
'--plugin',
'tests/plugins/tesseract_noop.py',
2018-12-30 01:27:49 -08:00
sep='\n',
end='\n',
file=argsfile,
)
check_ocrmypdf(
2020-06-01 03:06:40 -07:00
resources / 'graph.pdf', path_argsfile, '@' + str(outdir / 'test_argsfile.txt')
2018-12-30 01:27:49 -08:00
)
@pytest.mark.parametrize('renderer', RENDERERS)
def test_ocr_timeout(renderer, resources, outpdf):
2018-12-30 01:27:49 -08:00
out = check_ocrmypdf(
resources / 'skew.pdf',
outpdf,
'--tesseract-timeout',
'0',
'--pdf-renderer',
renderer,
)
pdfinfo = PdfInfo(out)
assert not pdfinfo[0].has_text
2015-07-28 01:47:30 -07:00
def test_skip_big(resources, outpdf):
2018-12-30 01:27:49 -08:00
out = check_ocrmypdf(
resources / 'jbig2.pdf',
outpdf,
'--skip-big',
'1',
'--plugin',
'tests/plugins/tesseract_cache.py',
2018-12-30 01:27:49 -08:00
)
pdfinfo = PdfInfo(out)
assert not pdfinfo[0].has_text
2015-07-28 02:31:18 -07:00
2015-07-28 03:02:35 -07:00
@pytest.mark.parametrize('renderer', RENDERERS)
2016-08-03 02:47:18 -07:00
@pytest.mark.parametrize('output_type', ['pdf', 'pdfa'])
def test_maximum_options(renderer, output_type, resources, outpdf):
2015-07-28 03:02:35 -07:00
check_ocrmypdf(
2018-12-30 01:27:49 -08:00
resources / 'multipage.pdf',
outpdf,
'-d',
2021-04-07 01:56:51 -07:00
'-ci' if have_unpaper() else None,
2018-12-30 01:27:49 -08:00
'-f',
'-k',
'--oversample',
'300',
'--remove-background',
2018-12-30 01:27:49 -08:00
'--skip-big',
'10',
'--title',
'Too Many Weird Files',
'--author',
'py.test',
'--pdf-renderer',
renderer,
'--output-type',
output_type,
'--plugin',
'tests/plugins/tesseract_cache.py',
2018-12-30 01:27:49 -08:00
)
def test_tesseract_missing_tessdata(monkeypatch, resources, no_outpdf, tmpdir):
monkeypatch.setenv("TESSDATA_PREFIX", os.fspath(tmpdir))
with pytest.raises(MissingDependencyError):
run_ocrmypdf_api(resources / 'graph.pdf', no_outpdf, '-v', '1', '--skip-text')
def test_invalid_input_pdf(resources, no_outpdf):
result = run_ocrmypdf_api(resources / 'invalid.pdf', no_outpdf)
assert result == ExitCode.input_file
2015-08-14 00:46:50 -07:00
def test_blank_input_pdf(resources, outpdf):
result = run_ocrmypdf_api(resources / 'blank.pdf', outpdf)
assert result == ExitCode.ok
2015-08-14 00:46:50 -07:00
2020-06-02 02:36:41 -07:00
def test_force_ocr_on_pdf_with_no_images(resources, no_outpdf):
# As a correctness test, make sure that --force-ocr on a PDF with no
# content still triggers tesseract. If tesseract crashes, then it was
# called.
p, _, _ = run_ocrmypdf(
2020-06-02 02:36:41 -07:00
resources / 'blank.pdf',
no_outpdf,
'--force-ocr',
'--plugin',
'tests/plugins/tesseract_crash.py',
2018-12-30 01:27:49 -08:00
)
assert p.returncode == ExitCode.child_process_error
2020-05-06 02:53:47 -07:00
assert not no_outpdf.exists()
@pytest.mark.skipif(
2021-04-07 01:56:51 -07:00
is_macos() and running_in_travis(),
2018-12-30 01:27:49 -08:00
reason="takes too long to install language packs in Travis macOS homebrew",
)
def test_german(resources, outdir):
# Produce a sidecar too - implicit test that system locale is set up
# properly. It is fine that we are testing -l deu on a French file because
# we are exercising the functionality not going for accuracy.
sidecar = outdir / 'francais.txt'
2019-12-04 13:40:48 -08:00
try:
check_ocrmypdf(
resources / 'francais.pdf',
outdir / 'francais.pdf',
'-l',
'deu', # more commonly installed
'--sidecar',
sidecar,
'--plugin',
'tests/plugins/tesseract_cache.py',
2019-12-04 13:40:48 -08:00
)
except MissingDependencyError:
if 'deu' not in tesseract.get_languages():
2019-12-04 13:40:48 -08:00
pytest.xfail(reason="tesseract-deu language pack not installed")
raise
def test_klingon(resources, outpdf):
p, _, _ = run_ocrmypdf(resources / 'francais.pdf', outpdf, '-l', 'klz')
assert p.returncode == ExitCode.missing_dependency
2020-06-01 03:06:40 -07:00
def test_missing_docinfo(resources, outpdf):
result = run_ocrmypdf_api(
2018-12-30 01:27:49 -08:00
resources / 'missing_docinfo.pdf',
outpdf,
'-l',
'eng',
'--skip-text',
2020-06-01 03:06:40 -07:00
'--plugin',
2020-06-30 04:17:30 -07:00
Path('tests/plugins/tesseract_noop.py'),
2018-12-30 01:27:49 -08:00
)
assert result == ExitCode.ok
2020-06-01 03:06:40 -07:00
def test_uppercase_extension(resources, outdir):
2018-12-30 01:27:49 -08:00
shutil.copy(str(resources / "skew.pdf"), str(outdir / "UPPERCASE.PDF"))
2018-12-30 01:27:49 -08:00
check_ocrmypdf(
2020-06-01 03:06:40 -07:00
outdir / "UPPERCASE.PDF",
outdir / "UPPERCASE_OUT.PDF",
'--plugin',
'tests/plugins/tesseract_noop.py',
2018-12-30 01:27:49 -08:00
)
def test_input_file_not_found(caplog, no_outpdf):
input_file = "does not exist.pdf"
result = run_ocrmypdf_api(input_file, no_outpdf)
assert result == ExitCode.input_file
assert input_file in caplog.text
2021-04-07 01:56:51 -07:00
@pytest.mark.skipif(os.name == 'nt' or running_in_docker(), reason="chmod")
def test_input_file_not_readable(caplog, resources, outdir, no_outpdf):
input_file = outdir / 'trivial.pdf'
shutil.copy(resources / 'trivial.pdf', input_file)
input_file.chmod(0o000)
result = run_ocrmypdf_api(input_file, no_outpdf)
assert result == ExitCode.input_file
assert str(input_file) in caplog.text
def test_input_file_not_a_pdf(caplog, no_outpdf):
input_file = __file__ # Try to OCR this file
result = run_ocrmypdf_api(input_file, no_outpdf)
assert result == ExitCode.input_file
if os.name != 'nt': # name will be mangled with \\'s on nt
assert input_file in caplog.text
def test_encrypted(resources, caplog, no_outpdf):
result = run_ocrmypdf_api(resources / 'skew-encrypted.pdf', no_outpdf)
assert result == ExitCode.encrypted_pdf
assert 'encryption must be removed' in caplog.text
2016-01-11 17:19:32 -08:00
@pytest.mark.parametrize('renderer', RENDERERS)
def test_pagesegmode(renderer, resources, outpdf):
2016-01-11 17:19:32 -08:00
check_ocrmypdf(
2018-12-30 01:27:49 -08:00
resources / 'skew.pdf',
outpdf,
'--tesseract-pagesegmode',
'7',
'-v',
'1',
'--pdf-renderer',
renderer,
'--plugin',
'tests/plugins/tesseract_cache.py',
2018-12-30 01:27:49 -08:00
)
2016-01-11 17:19:32 -08:00
2017-07-21 14:10:02 -07:00
@pytest.mark.parametrize('renderer', RENDERERS)
2020-06-02 02:36:41 -07:00
def test_tesseract_crash(renderer, resources, no_outpdf):
p, _, err = run_ocrmypdf(
2018-12-30 01:27:49 -08:00
resources / 'ccitt.pdf',
no_outpdf,
'-v',
'1',
'--pdf-renderer',
renderer,
2020-06-02 02:36:41 -07:00
'--plugin',
'tests/plugins/tesseract_crash.py',
2018-12-30 01:27:49 -08:00
)
assert p.returncode == ExitCode.child_process_error
2020-05-06 02:53:47 -07:00
assert not no_outpdf.exists()
assert "SubprocessOutputError" in err
2020-06-02 02:36:41 -07:00
def test_tesseract_crash_autorotate(resources, no_outpdf):
p, out, err = run_ocrmypdf(
2020-06-02 02:36:41 -07:00
resources / 'ccitt.pdf',
no_outpdf,
'-r',
'--plugin',
'tests/plugins/tesseract_crash.py',
2018-12-30 01:27:49 -08:00
)
assert p.returncode == ExitCode.child_process_error
2020-05-06 02:53:47 -07:00
assert not no_outpdf.exists()
assert "uncaught exception" in err
print(out)
print(err)
@pytest.mark.parametrize('renderer', RENDERERS)
2019-05-17 01:42:27 -07:00
@pytest.mark.slow
def test_tesseract_image_too_big(renderer, resources, outpdf):
check_ocrmypdf(
2018-12-30 01:27:49 -08:00
resources / 'hugemono.pdf',
outpdf,
'-r',
'--pdf-renderer',
renderer,
'--max-image-mpixels',
'0',
'--plugin',
'tests/plugins/tesseract_big_image_error.py',
2018-12-30 01:27:49 -08:00
)
2020-06-01 03:06:40 -07:00
def test_algo4(resources, outpdf):
2018-12-30 01:27:49 -08:00
p, _, _ = run_ocrmypdf(
2020-06-01 03:06:40 -07:00
resources / 'encrypted_algo4.pdf',
outpdf,
'--plugin',
'tests/plugins/tesseract_noop.py',
2018-12-30 01:27:49 -08:00
)
assert p.returncode == ExitCode.encrypted_pdf
def test_jbig2_passthrough(resources, outpdf):
out = check_ocrmypdf(
2018-12-30 01:27:49 -08:00
resources / 'jbig2.pdf',
outpdf,
'--output-type',
'pdf',
'--pdf-renderer',
'hocr',
'--plugin',
'tests/plugins/tesseract_cache.py',
2018-12-30 01:27:49 -08:00
)
out_pageinfo = PdfInfo(out)
assert out_pageinfo[0].images[0].enc == Encoding.jbig2
2016-08-25 14:46:54 -07:00
2020-06-01 03:06:40 -07:00
def test_masks(resources, outpdf):
assert (
2019-07-07 02:11:44 -07:00
ocrmypdf.ocr(
2020-06-01 03:06:40 -07:00
resources / 'masks.pdf', outpdf, plugins=['tests/plugins/tesseract_noop.py']
)
== ExitCode.ok
2018-12-30 01:27:49 -08:00
)
2016-08-31 11:42:21 -07:00
2020-06-01 03:06:40 -07:00
def test_linearized_pdf_and_indirect_object(resources, outpdf):
check_ocrmypdf(
resources / 'epson.pdf', outpdf, '--plugin', 'tests/plugins/tesseract_noop.py'
)
def test_very_high_dpi(resources, outpdf):
"Checks for a Decimal quantize error with high DPI, etc"
check_ocrmypdf(
resources / '2400dpi.pdf',
outpdf,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
pdfinfo = PdfInfo(outpdf)
image = pdfinfo[0].images[0]
2020-04-24 04:12:05 -07:00
assert isclose(image.dpi.x, image.dpi.y)
assert isclose(image.dpi.x, 2400)
2020-06-01 03:06:40 -07:00
def test_overlay(resources, outpdf):
2018-12-30 01:27:49 -08:00
check_ocrmypdf(
2020-06-01 03:06:40 -07:00
resources / 'overlay.pdf',
outpdf,
'--skip-text',
'--plugin',
'tests/plugins/tesseract_noop.py',
2018-12-30 01:27:49 -08:00
)
2020-06-01 03:06:40 -07:00
def test_destination_not_writable(resources, outdir):
2019-11-19 18:07:33 -08:00
if os.name != 'nt' and (os.getuid() == 0 or os.geteuid() == 0):
pytest.xfail(reason="root can write to anything")
protected_file = outdir / 'protected.pdf'
protected_file.touch()
protected_file.chmod(0o400) # Read-only
2020-05-03 00:51:17 -07:00
p, _out, _err = run_ocrmypdf(
2020-06-01 03:06:40 -07:00
resources / 'jbig2.pdf',
protected_file,
'--plugin',
'tests/plugins/tesseract_noop.py',
2018-12-30 01:27:49 -08:00
)
assert p.returncode == ExitCode.file_access_error, "Expected error"
def test_tesseract_config_valid(resources, outdir):
cfg_file = outdir / 'test.cfg'
with cfg_file.open('w') as f:
2018-12-30 01:27:49 -08:00
f.write(
'''\
load_system_dawg 0
language_model_penalty_non_dict_word 0
language_model_penalty_non_freq_dict_word 0
2018-12-30 01:27:49 -08:00
'''
)
check_ocrmypdf(
2019-12-09 15:04:37 -08:00
resources / '3small.pdf',
outdir / 'out.pdf',
'--tesseract-config',
cfg_file,
'--pages',
'1',
2018-12-30 01:27:49 -08:00
)
@pytest.mark.slow # This test sometimes times out in CI
@pytest.mark.parametrize('renderer', RENDERERS)
def test_tesseract_config_invalid(renderer, resources, outdir):
cfg_file = outdir / 'test.cfg'
with cfg_file.open('w') as f:
2018-12-30 01:27:49 -08:00
f.write(
'''\
THIS FILE IS INVALID
2018-12-30 01:27:49 -08:00
'''
)
2020-05-03 00:51:17 -07:00
p, _out, err = run_ocrmypdf(
2018-12-30 01:27:49 -08:00
resources / 'ccitt.pdf',
outdir / 'out.pdf',
'--pdf-renderer',
renderer,
'--tesseract-config',
cfg_file,
)
assert (
"parameter not found" in err.lower()
or "error occurred while parsing" in err.lower()
), "No error message"
assert p.returncode == ExitCode.invalid_config
2017-02-14 12:51:15 -08:00
@pytest.mark.skipif(not tesseract.has_user_words(), reason='not functional until 4.1.0')
def test_user_words_ocr(resources, outdir):
# Does not actually test if --user-words causes output to differ
word_list = outdir / 'wordlist.txt'
sidecar_after = outdir / 'sidecar.txt'
with word_list.open('w') as f:
f.write('cromulent\n') # a perfectly cromulent word
check_ocrmypdf(
2018-12-30 01:27:49 -08:00
resources / 'crom.png',
outdir / 'out.pdf',
'--image-dpi',
150,
'--sidecar',
sidecar_after,
'--user-words',
word_list,
)
2020-06-01 03:06:40 -07:00
def test_form_xobject(resources, outpdf):
2018-12-30 01:27:49 -08:00
check_ocrmypdf(
2020-06-01 03:06:40 -07:00
resources / 'formxobject.pdf',
outpdf,
'--force-ocr',
'--plugin',
'tests/plugins/tesseract_noop.py',
2018-12-30 01:27:49 -08:00
)
@pytest.mark.parametrize('renderer', RENDERERS)
def test_pagesize_consistency(renderer, resources, outpdf):
2019-12-09 15:04:37 -08:00
infile = resources / '3small.pdf'
before_dims = first_page_dimensions(infile)
check_ocrmypdf(
infile,
2018-12-30 01:27:49 -08:00
outpdf,
'--pdf-renderer',
renderer,
2021-04-07 01:56:51 -07:00
'--clean' if have_unpaper() else None,
2018-12-30 01:27:49 -08:00
'--deskew',
'--remove-background',
2021-04-07 01:56:51 -07:00
'--clean-final' if have_unpaper() else None,
2019-12-09 15:04:37 -08:00
'--pages',
'1',
2018-12-30 01:27:49 -08:00
)
after_dims = first_page_dimensions(outpdf)
2019-12-09 15:04:37 -08:00
assert isclose(before_dims[0], after_dims[0], rel_tol=1e-4)
assert isclose(before_dims[1], after_dims[1], rel_tol=1e-4)
2017-04-18 15:20:25 -07:00
2020-06-01 03:06:40 -07:00
def test_skip_big_with_no_images(resources, outpdf):
2018-12-30 01:27:49 -08:00
check_ocrmypdf(
resources / 'blank.pdf',
outpdf,
'--skip-big',
'5',
'--force-ocr',
2020-06-01 03:06:40 -07:00
'--plugin',
'tests/plugins/tesseract_noop.py',
2018-12-30 01:27:49 -08:00
)
2018-12-30 01:27:49 -08:00
@pytest.mark.skipif(
'8.0.0' <= pikepdf.__libqpdf_version__ <= '8.0.1',
reason="libqpdf regression on pages with no contents",
2018-12-30 01:27:49 -08:00
)
2020-06-01 03:06:40 -07:00
def test_no_contents(resources, outpdf):
2018-12-30 01:27:49 -08:00
check_ocrmypdf(
2020-06-01 03:06:40 -07:00
resources / 'no_contents.pdf',
outpdf,
'--force-ocr',
'--plugin',
'tests/plugins/tesseract_noop.py',
2018-12-30 01:27:49 -08:00
)
@pytest.mark.parametrize(
'image', ['baiona.png', 'baiona_gray.png', 'baiona_alpha.png', 'congress.jpg']
)
2020-06-01 03:06:40 -07:00
def test_compression_preserved(ocrmypdf_exec, resources, image, outpdf):
input_file = str(resources / image)
output_file = str(outpdf)
im = Image.open(input_file)
# Runs: ocrmypdf - output.pdf < testfile
with open(input_file, 'rb') as input_stream:
p_args = ocrmypdf_exec + [
2018-12-30 01:27:49 -08:00
'--optimize',
'0',
'--image-dpi',
'150',
'--output-type',
'pdf',
2020-06-01 03:06:40 -07:00
'--plugin',
'tests/plugins/tesseract_noop.py',
2018-12-30 01:27:49 -08:00
'-',
output_file,
]
p = run(
2018-12-30 01:27:49 -08:00
p_args,
stdout=PIPE,
stderr=PIPE,
stdin=input_stream,
universal_newlines=True, # When dropping support for Python 3.6 change to text=
2020-05-03 00:51:17 -07:00
check=False,
2018-12-30 01:27:49 -08:00
)
if im.mode in ('RGBA', 'LA'):
# If alpha image is input, expect an error
assert p.returncode != ExitCode.ok and 'alpha' in p.stderr
return
assert p.returncode == ExitCode.ok, p.stderr
pdfinfo = PdfInfo(output_file)
pdfimage = pdfinfo[0].images[0]
if input_file.endswith('.png'):
2018-12-30 01:27:49 -08:00
assert pdfimage.enc != Encoding.jpeg, "Lossless compression changed to lossy!"
elif input_file.endswith('.jpg'):
2018-12-30 01:27:49 -08:00
assert pdfimage.enc == Encoding.jpeg, "Lossy compression changed to lossless!"
if im.mode.startswith('RGB') or im.mode.startswith('BGR'):
2018-12-30 01:27:49 -08:00
assert pdfimage.color == Colorspace.rgb, "Colorspace changed"
elif im.mode.startswith('L'):
2018-12-30 01:27:49 -08:00
assert pdfimage.color == Colorspace.gray, "Colorspace changed"
im.close()
2018-12-30 01:27:49 -08:00
@pytest.mark.parametrize(
'image,compression',
[
('baiona.png', 'jpeg'),
('baiona_gray.png', 'lossless'),
('congress.jpg', 'lossless'),
],
)
2020-06-01 03:06:40 -07:00
def test_compression_changed(ocrmypdf_exec, resources, image, compression, outpdf):
input_file = str(resources / image)
output_file = str(outpdf)
im = Image.open(input_file)
# Runs: ocrmypdf - output.pdf < testfile
with open(input_file, 'rb') as input_stream:
p_args = ocrmypdf_exec + [
2018-12-30 01:27:49 -08:00
'--image-dpi',
'150',
'--output-type',
'pdfa',
'--optimize',
'0',
'--pdfa-image-compression',
compression,
2020-06-01 03:06:40 -07:00
'--plugin',
'tests/plugins/tesseract_noop.py',
2018-12-30 01:27:49 -08:00
'-',
output_file,
]
p = run(
2018-12-30 01:27:49 -08:00
p_args,
stdout=PIPE,
stderr=PIPE,
stdin=input_stream,
universal_newlines=True, # When dropping support for Python 3.6 change to text=
2020-05-03 00:51:17 -07:00
check=False,
2018-12-30 01:27:49 -08:00
)
assert p.returncode == ExitCode.ok, p.stderr
pdfinfo = PdfInfo(output_file)
pdfimage = pdfinfo[0].images[0]
if compression == "jpeg":
assert pdfimage.enc == Encoding.jpeg
else:
if ghostscript.jpeg_passthrough_available():
# Ghostscript 9.23 adds JPEG passthrough, which allows a JPEG to be
# copied without transcoding - so report
if image.endswith('jpg'):
assert pdfimage.enc == Encoding.jpeg
else:
assert pdfimage.enc not in (Encoding.jpeg, Encoding.jpeg2000)
if im.mode.startswith('RGB') or im.mode.startswith('BGR'):
2018-12-30 01:27:49 -08:00
assert pdfimage.color == Colorspace.rgb, "Colorspace changed"
elif im.mode.startswith('L'):
2018-12-30 01:27:49 -08:00
assert pdfimage.color == Colorspace.gray, "Colorspace changed"
im.close()
def test_sidecar_pagecount(resources, outpdf):
2019-06-01 01:55:51 -07:00
sidecar = outpdf.with_suffix('.txt')
check_ocrmypdf(
2019-12-09 15:04:37 -08:00
resources / '3small.pdf',
2018-12-30 01:27:49 -08:00
outpdf,
'--skip-text',
2018-12-30 01:27:49 -08:00
'--sidecar',
sidecar,
'--plugin',
'tests/plugins/tesseract_cache.py',
2018-12-30 01:27:49 -08:00
)
2019-12-09 15:04:37 -08:00
pdfinfo = PdfInfo(resources / '3small.pdf')
num_pages = len(pdfinfo)
2019-11-19 18:07:33 -08:00
with open(sidecar, 'r', encoding='utf-8') as f:
ocr_text = f.read()
# There should a formfeed between each pair of pages, so the count of
# formfeeds is the page count less one
2018-12-30 01:27:49 -08:00
assert (
ocr_text.count('\f') == num_pages - 1
), "Sidecar page count does not match PDF page count"
2017-10-11 14:32:58 -07:00
def test_sidecar_nonempty(resources, outpdf):
2019-06-01 01:55:51 -07:00
sidecar = outpdf.with_suffix('.txt')
check_ocrmypdf(
resources / 'ccitt.pdf',
outpdf,
'--sidecar',
sidecar,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
2019-11-19 18:07:33 -08:00
with open(sidecar, 'r', encoding='utf-8') as f:
ocr_text = f.read()
assert 'the' in ocr_text
@pytest.mark.parametrize('pdfa_level', ['1', '2', '3'])
def test_pdfa_n(pdfa_level, resources, outpdf):
2018-05-04 15:29:43 -07:00
if pdfa_level == '3' and ghostscript.version() < '9.19':
pytest.xfail(reason='Ghostscript >= 9.19 required')
2017-10-11 14:32:58 -07:00
check_ocrmypdf(
2018-12-30 01:27:49 -08:00
resources / 'ccitt.pdf',
outpdf,
'--output-type',
'pdfa-' + pdfa_level,
'--plugin',
'tests/plugins/tesseract_cache.py',
2017-10-11 14:32:58 -07:00
)
pdfa_info = file_claims_pdfa(outpdf)
assert pdfa_info['conformance'] == f'PDF/A-{pdfa_level}B'
@pytest.mark.skipif(
2018-12-30 01:27:49 -08:00
PIL.__version__ < '5.0.0', reason="Pillow < 5.0.0 doesn't raise the exception"
)
2019-05-17 01:42:27 -07:00
@pytest.mark.slow
def test_decompression_bomb(resources, outpdf):
2020-05-03 00:51:17 -07:00
p, _out, err = run_ocrmypdf(resources / 'hugemono.pdf', outpdf)
assert 'decompression bomb' in err
2020-05-03 00:51:17 -07:00
p, _out, err = run_ocrmypdf(
2018-12-30 01:27:49 -08:00
resources / 'hugemono.pdf', outpdf, '--max-image-mpixels', '2000'
)
2018-02-08 00:15:12 -08:00
assert p.returncode == 0
2020-06-01 03:06:40 -07:00
def test_text_curves(resources, outpdf):
2019-12-09 15:04:37 -08:00
with patch('ocrmypdf._pipeline.VECTOR_PAGE_DPI', 100):
2020-06-01 03:06:40 -07:00
check_ocrmypdf(
resources / 'vector.pdf',
outpdf,
'--plugin',
'tests/plugins/tesseract_noop.py',
)
2018-02-08 00:15:12 -08:00
2019-12-09 15:04:37 -08:00
info = PdfInfo(outpdf)
assert len(info.pages[0].images) == 0, "added images to the vector PDF"
2018-02-08 00:15:12 -08:00
2019-12-09 15:04:37 -08:00
check_ocrmypdf(
2020-06-01 03:06:40 -07:00
resources / 'vector.pdf',
outpdf,
'--force-ocr',
'--plugin',
'tests/plugins/tesseract_noop.py',
2019-12-09 15:04:37 -08:00
)
2019-12-09 15:04:37 -08:00
info = PdfInfo(outpdf)
assert len(info.pages[0].images) != 0, "force did not rasterize"
2020-06-01 03:06:40 -07:00
def test_output_is_dir(resources, outdir):
2020-05-03 00:51:17 -07:00
p, _out, err = run_ocrmypdf(
2020-06-01 03:06:40 -07:00
resources / 'trivial.pdf',
outdir,
'--force-ocr',
'--plugin',
'tests/plugins/tesseract_noop.py',
)
assert p.returncode == ExitCode.file_access_error
assert 'is not a writable file' in err
@pytest.mark.skipif(os.name == 'nt', reason="symlink needs admin permissions")
2020-06-01 03:06:40 -07:00
def test_output_is_symlink(resources, outdir):
sym = Path(outdir / 'this_is_a_symlink')
sym.symlink_to(outdir / 'out.pdf')
2020-05-03 00:51:17 -07:00
p, _out, err = run_ocrmypdf(
2020-06-01 03:06:40 -07:00
resources / 'trivial.pdf',
sym,
'--force-ocr',
'--plugin',
'tests/plugins/tesseract_noop.py',
)
assert p.returncode == ExitCode.ok, err
assert (outdir / 'out.pdf').stat().st_size > 0, 'target file not created'
def test_livecycle(resources, no_outpdf):
2018-12-30 01:27:49 -08:00
p, _, err = run_ocrmypdf(resources / 'livecycle.pdf', no_outpdf)
assert p.returncode == ExitCode.input_file, err
2018-11-10 00:56:22 -08:00
def test_version_check():
with pytest.raises(MissingDependencyError):
get_version('NOT_FOUND_UNLIKELY_ON_PATH')
with pytest.raises(MissingDependencyError):
get_version('sh', version_arg='-c')
with pytest.raises(MissingDependencyError):
get_version('echo')
2019-07-27 16:15:48 -07:00
@pytest.mark.parametrize(
'threshold, optimize, output_type, expected',
[
[1.0, 0, 'pdfa', False],
[1.0, 0, 'pdf', False],
[0.0, 0, 'pdfa', True],
[0.0, 0, 'pdf', True],
[1.0, 1, 'pdfa', False],
[1.0, 1, 'pdf', False],
[0.0, 1, 'pdfa', True],
[0.0, 1, 'pdf', True],
],
)
2020-06-01 03:06:40 -07:00
def test_fast_web_view(resources, outpdf, threshold, optimize, output_type, expected):
2019-07-27 16:15:48 -07:00
check_ocrmypdf(
resources / 'trivial.pdf',
outpdf,
'--fast-web-view',
threshold,
'--optimize',
optimize,
'--output-type',
output_type,
2020-06-01 03:06:40 -07:00
'--plugin',
'tests/plugins/tesseract_noop.py',
2019-07-27 16:15:48 -07:00
)
with pikepdf.open(outpdf) as pdf:
assert pdf.is_linearized == expected
2020-06-01 03:06:40 -07:00
def test_image_dpi_not_image(caplog, resources, outpdf):
check_ocrmypdf(
resources / 'trivial.pdf',
outpdf,
'--image-dpi',
'100',
2020-06-01 03:06:40 -07:00
'--plugin',
'tests/plugins/tesseract_noop.py',
)
assert '--image-dpi is being ignored' in caplog.text
def test_image_dpi_threshold(resources, outpdf):
check_ocrmypdf(
resources / 'typewriter.png',
outpdf,
'--threshold',
'--image-dpi=170',
'--output-type=pdf',
'--optimize=0',
'--plugin',
'tests/plugins/tesseract_noop.py',
)
assert outpdf.exists()