2015-07-22 02:59:25 -07:00
|
|
|
#!/usr/bin/env python3
|
2015-07-28 04:36:58 -07:00
|
|
|
# © 2015 James R. Barlow: github.com/jbarlow83
|
2015-07-22 02:59:25 -07:00
|
|
|
|
2015-07-25 00:57:07 -07:00
|
|
|
from __future__ import print_function
|
2016-08-25 14:46:54 -07:00
|
|
|
from subprocess import Popen, PIPE, check_output, check_call, DEVNULL
|
2015-07-22 02:59:25 -07:00
|
|
|
import os
|
2015-07-22 04:00:59 -07:00
|
|
|
import shutil
|
|
|
|
from contextlib import suppress
|
2015-07-22 11:21:33 -07:00
|
|
|
import sys
|
2015-07-28 00:43:22 -07:00
|
|
|
import pytest
|
2015-07-28 02:31:18 -07:00
|
|
|
from ocrmypdf.pageinfo import pdf_get_all_pageinfo
|
2015-08-05 17:09:38 -07:00
|
|
|
import PyPDF2 as pypdf
|
2015-08-11 00:17:02 -07:00
|
|
|
from ocrmypdf import ExitCode
|
2016-02-08 12:55:50 -08:00
|
|
|
from ocrmypdf import leptonica
|
2016-08-03 02:47:18 -07:00
|
|
|
from ocrmypdf.pdfa import file_claims_pdfa
|
2016-08-26 15:08:56 -07:00
|
|
|
import platform
|
2015-07-27 22:07:04 -07:00
|
|
|
|
2015-07-22 11:21:33 -07:00
|
|
|
|
|
|
|
if sys.version_info.major < 3:
|
|
|
|
print("Requires Python 3.4+")
|
|
|
|
sys.exit(1)
|
2015-07-22 02:59:25 -07:00
|
|
|
|
|
|
|
TESTS_ROOT = os.path.abspath(os.path.dirname(__file__))
|
2015-12-17 11:36:47 -08:00
|
|
|
SPOOF_PATH = os.path.join(TESTS_ROOT, 'spoof')
|
2015-07-22 02:59:25 -07:00
|
|
|
PROJECT_ROOT = os.path.dirname(TESTS_ROOT)
|
|
|
|
TEST_RESOURCES = os.path.join(PROJECT_ROOT, 'tests', 'resources')
|
2015-08-20 01:25:31 -07:00
|
|
|
TEST_OUTPUT = os.environ.get(
|
|
|
|
'OCRMYPDF_TEST_OUTPUT',
|
2016-01-11 17:40:44 -08:00
|
|
|
default=os.path.join(PROJECT_ROOT, 'tests', 'output', 'main'))
|
2016-08-31 17:01:42 -07:00
|
|
|
OCRMYPDF = [sys.executable, '-m', 'ocrmypdf']
|
2015-07-22 02:59:25 -07:00
|
|
|
|
|
|
|
|
2016-02-20 23:47:37 -08:00
|
|
|
def running_in_docker():
|
|
|
|
# Docker creates a file named /.dockerinit
|
|
|
|
return os.path.exists('/.dockerinit')
|
|
|
|
|
|
|
|
|
2016-08-26 15:08:56 -07:00
|
|
|
def is_linux():
|
|
|
|
return platform.system() == 'Linux'
|
|
|
|
|
|
|
|
|
2015-07-22 03:16:19 -07:00
|
|
|
def setup_module():
|
2015-07-22 04:00:59 -07:00
|
|
|
with suppress(FileNotFoundError):
|
|
|
|
shutil.rmtree(TEST_OUTPUT)
|
|
|
|
with suppress(FileExistsError):
|
2016-01-16 02:47:56 -08:00
|
|
|
os.makedirs(TEST_OUTPUT)
|
2015-07-22 03:16:19 -07:00
|
|
|
|
|
|
|
|
2016-02-08 12:57:26 -08:00
|
|
|
def _infile(input_basename):
|
2015-08-05 17:09:38 -07:00
|
|
|
return os.path.join(TEST_RESOURCES, input_basename)
|
|
|
|
|
|
|
|
|
2016-02-08 12:57:26 -08:00
|
|
|
def _outfile(output_basename):
|
2016-02-15 17:17:43 -08:00
|
|
|
return os.path.join(TEST_OUTPUT, os.path.basename(output_basename))
|
2015-08-05 17:09:38 -07:00
|
|
|
|
|
|
|
|
2015-12-17 11:36:47 -08:00
|
|
|
def check_ocrmypdf(input_basename, output_basename, *args, env=None):
|
2016-08-26 15:23:26 -07:00
|
|
|
"Run ocrmypdf and confirmed that a valid file was created"
|
2016-02-08 12:57:26 -08:00
|
|
|
input_file = _infile(input_basename)
|
|
|
|
output_file = _outfile(output_basename)
|
2015-07-22 04:00:59 -07:00
|
|
|
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(input_basename, output_basename, *args, env=env)
|
|
|
|
if p.returncode != 0:
|
2016-02-16 05:17:18 -08:00
|
|
|
print('stdout\n======')
|
|
|
|
print(out)
|
|
|
|
print('stderr\n======')
|
|
|
|
print(err)
|
2016-08-26 15:18:38 -07:00
|
|
|
assert p.returncode == 0
|
2015-07-22 04:00:59 -07:00
|
|
|
assert os.path.exists(output_file), "Output file not created"
|
2015-07-27 15:39:54 -07:00
|
|
|
assert os.stat(output_file).st_size > 100, "PDF too small or empty"
|
|
|
|
return output_file
|
2015-07-22 02:59:25 -07:00
|
|
|
|
|
|
|
|
2016-08-26 15:18:38 -07:00
|
|
|
def run_ocrmypdf(input_basename, output_basename, *args, env=None):
|
2016-08-26 15:23:26 -07:00
|
|
|
"Run ocrmypdf and let caller deal with results"
|
2016-02-08 12:57:26 -08:00
|
|
|
input_file = _infile(input_basename)
|
|
|
|
output_file = _outfile(output_basename)
|
2015-08-10 13:57:28 -07:00
|
|
|
|
2015-08-11 00:23:48 -07:00
|
|
|
if env is None:
|
|
|
|
env = os.environ
|
|
|
|
|
2016-08-26 15:23:26 -07:00
|
|
|
p_args = OCRMYPDF + list(args) + [input_file, output_file]
|
2015-08-10 13:57:28 -07:00
|
|
|
p = Popen(
|
|
|
|
p_args, close_fds=True, stdout=PIPE, stderr=PIPE,
|
|
|
|
universal_newlines=True, env=env)
|
|
|
|
out, err = p.communicate()
|
|
|
|
return p, out, err
|
|
|
|
|
|
|
|
|
2016-02-19 03:48:49 -08:00
|
|
|
def spoof(replace_program, with_spoof):
|
|
|
|
"""Modify environment variables to override subprocess executables
|
|
|
|
|
|
|
|
Before running any executable, ocrmypdf checks the environment variable
|
|
|
|
OCRMYPDF_PROGRAMNAME to override default program name/location, e.g.
|
|
|
|
OCRMYPDF_GS redirects from the system path Ghostscript ("gs") to elsewhere.
|
|
|
|
"""
|
2015-12-17 11:36:47 -08:00
|
|
|
env = os.environ.copy()
|
2016-02-19 03:48:49 -08:00
|
|
|
spoofer = os.path.join(SPOOF_PATH, with_spoof)
|
2016-02-19 14:09:56 -08:00
|
|
|
if not os.access(spoofer, os.X_OK):
|
|
|
|
os.chmod(spoofer, 0o755)
|
2016-02-19 03:48:49 -08:00
|
|
|
env['OCRMYPDF_' + replace_program.upper()] = spoofer
|
2015-12-17 11:36:47 -08:00
|
|
|
return env
|
|
|
|
|
|
|
|
|
2016-02-19 03:48:49 -08:00
|
|
|
@pytest.fixture
|
|
|
|
def spoof_tesseract_noop():
|
|
|
|
return spoof('tesseract', 'tesseract_noop.py')
|
|
|
|
|
|
|
|
|
2015-12-17 12:52:12 -08:00
|
|
|
@pytest.fixture
|
|
|
|
def spoof_tesseract_cache():
|
2016-02-20 23:47:37 -08:00
|
|
|
if running_in_docker():
|
|
|
|
return os.environ.copy()
|
2016-02-19 03:48:49 -08:00
|
|
|
return spoof('tesseract', "tesseract_cache.py")
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def spoof_tesseract_crash():
|
|
|
|
return spoof('tesseract', 'tesseract_crash.py')
|
2015-12-17 12:52:12 -08:00
|
|
|
|
|
|
|
|
2016-02-20 04:53:02 -08:00
|
|
|
@pytest.fixture
|
|
|
|
def spoof_tesseract_big_image_error():
|
|
|
|
return spoof('tesseract', 'tesseract_big_image_error.py')
|
|
|
|
|
|
|
|
|
2015-12-17 11:56:09 -08:00
|
|
|
def test_quick(spoof_tesseract_noop):
|
|
|
|
check_ocrmypdf('c02-22.pdf', 'test_quick.pdf', env=spoof_tesseract_noop)
|
|
|
|
|
|
|
|
|
2015-12-17 11:41:54 -08:00
|
|
|
def test_deskew(spoof_tesseract_noop):
|
2015-07-27 16:11:51 -07:00
|
|
|
# Run with deskew
|
2015-12-17 11:36:47 -08:00
|
|
|
deskewed_pdf = check_ocrmypdf(
|
2016-02-16 02:12:14 -08:00
|
|
|
'skew.pdf', 'test_deskew.pdf', '-d', '-v', '1', env=spoof_tesseract_noop)
|
2015-07-27 16:11:51 -07:00
|
|
|
|
|
|
|
# Now render as an image again and use Leptonica to find the skew angle
|
|
|
|
# to confirm that it was deskewed
|
2015-07-27 15:39:54 -07:00
|
|
|
from ocrmypdf.ghostscript import rasterize_pdf
|
|
|
|
import logging
|
|
|
|
log = logging.getLogger()
|
|
|
|
|
2016-02-08 12:57:26 -08:00
|
|
|
deskewed_png = _outfile('deskewed.png')
|
2015-07-27 15:39:54 -07:00
|
|
|
|
|
|
|
rasterize_pdf(
|
|
|
|
deskewed_pdf,
|
|
|
|
deskewed_png,
|
|
|
|
xres=150,
|
|
|
|
yres=150,
|
|
|
|
raster_device='pngmono',
|
|
|
|
log=log)
|
|
|
|
|
2016-02-08 15:20:01 -08:00
|
|
|
from ocrmypdf.leptonica import Pix
|
|
|
|
pix = Pix.read(deskewed_png)
|
|
|
|
skew_angle, skew_confidence = pix.find_skew()
|
2015-07-27 15:39:54 -07:00
|
|
|
|
|
|
|
print(skew_angle)
|
|
|
|
assert -0.5 < skew_angle < 0.5, "Deskewing failed"
|
2015-07-22 04:00:59 -07:00
|
|
|
|
|
|
|
|
2015-12-17 11:56:09 -08:00
|
|
|
def test_clean(spoof_tesseract_noop):
|
2016-08-03 02:47:18 -07:00
|
|
|
check_ocrmypdf('skew.pdf', 'test_clean.pdf', '-c',
|
|
|
|
env=spoof_tesseract_noop)
|
|
|
|
|
|
|
|
|
|
|
|
# This will run 5 * 2 * 2 = 20 test cases
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"pdf",
|
|
|
|
['palette.pdf', 'cmyk.pdf', 'ccitt.pdf', 'jbig2.pdf', 'lichtenstein.pdf'])
|
|
|
|
@pytest.mark.parametrize("renderer", ['hocr', 'tesseract'])
|
|
|
|
@pytest.mark.parametrize("output_type", ['pdf', 'pdfa'])
|
|
|
|
def test_exotic_image(spoof_tesseract_cache, pdf, renderer, output_type):
|
2015-08-28 04:48:51 -07:00
|
|
|
check_ocrmypdf(
|
|
|
|
pdf,
|
|
|
|
'test_{0}_{1}.pdf'.format(pdf, renderer),
|
|
|
|
'-dc',
|
2015-12-17 14:00:17 -08:00
|
|
|
'-v', '1',
|
2016-08-03 02:47:18 -07:00
|
|
|
'--output-type', output_type,
|
2015-12-17 14:00:17 -08:00
|
|
|
'--pdf-renderer', renderer, env=spoof_tesseract_cache)
|
2015-08-28 04:48:51 -07:00
|
|
|
|
|
|
|
|
2016-08-03 02:47:18 -07:00
|
|
|
@pytest.mark.parametrize("output_type", [
|
|
|
|
'pdfa', 'pdf'
|
|
|
|
])
|
|
|
|
def test_preserve_metadata(spoof_tesseract_noop, output_type):
|
2016-02-08 12:57:26 -08:00
|
|
|
pdf_before = pypdf.PdfFileReader(_infile('graph.pdf'))
|
2015-08-05 17:09:38 -07:00
|
|
|
|
2015-12-17 12:52:12 -08:00
|
|
|
output = check_ocrmypdf('graph.pdf', 'test_metadata_preserve.pdf',
|
2016-08-03 02:47:18 -07:00
|
|
|
'--output-type', output_type,
|
2015-12-17 12:52:12 -08:00
|
|
|
env=spoof_tesseract_noop)
|
2015-08-05 17:09:38 -07:00
|
|
|
|
|
|
|
pdf_after = pypdf.PdfFileReader(output)
|
|
|
|
|
|
|
|
for key in ('/Title', '/Author'):
|
|
|
|
assert pdf_before.documentInfo[key] == pdf_after.documentInfo[key]
|
|
|
|
|
2016-08-03 02:47:18 -07:00
|
|
|
pdfa_info = file_claims_pdfa(output)
|
|
|
|
assert pdfa_info['output'] == output_type
|
|
|
|
|
2015-08-05 17:09:38 -07:00
|
|
|
|
2016-08-26 15:08:56 -07:00
|
|
|
@pytest.mark.skipif(
|
|
|
|
is_linux() and not running_in_docker(),
|
|
|
|
reason="likely to fail if Linux locale is not configured correctly")
|
2016-08-03 02:47:18 -07:00
|
|
|
@pytest.mark.parametrize("output_type", [
|
|
|
|
'pdfa', 'pdf'
|
|
|
|
])
|
|
|
|
def test_override_metadata(spoof_tesseract_noop, output_type):
|
2016-02-08 12:57:26 -08:00
|
|
|
input_file = _infile('c02-22.pdf')
|
|
|
|
output_file = _outfile('test_override_metadata.pdf')
|
2015-08-11 00:23:48 -07:00
|
|
|
|
2015-08-05 16:57:04 -07:00
|
|
|
german = 'Du siehst den Wald vor lauter Bäumen nicht.'
|
|
|
|
chinese = '孔子'
|
|
|
|
high_unicode = 'U+1030C is: 𐌌'
|
|
|
|
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2015-08-11 00:23:48 -07:00
|
|
|
input_file, output_file,
|
2015-08-05 16:57:04 -07:00
|
|
|
'--title', german,
|
|
|
|
'--author', chinese,
|
2015-12-17 11:56:09 -08:00
|
|
|
'--subject', high_unicode,
|
2016-08-03 02:47:18 -07:00
|
|
|
'--output-type', output_type,
|
2015-12-17 11:56:09 -08:00
|
|
|
env=spoof_tesseract_noop)
|
2015-07-27 16:11:51 -07:00
|
|
|
|
2015-09-05 01:12:33 -07:00
|
|
|
assert p.returncode == ExitCode.ok
|
2015-08-11 00:23:48 -07:00
|
|
|
|
|
|
|
pdf = output_file
|
|
|
|
|
2015-07-27 16:11:51 -07:00
|
|
|
out_pdfinfo = check_output(['pdfinfo', pdf], universal_newlines=True)
|
|
|
|
lines_pdfinfo = out_pdfinfo.splitlines()
|
|
|
|
pdfinfo = {}
|
|
|
|
for line in lines_pdfinfo:
|
|
|
|
k, v = line.strip().split(':', maxsplit=1)
|
|
|
|
pdfinfo[k.strip()] = v.strip()
|
|
|
|
|
2015-08-05 16:57:04 -07:00
|
|
|
assert pdfinfo['Title'] == german
|
|
|
|
assert pdfinfo['Author'] == chinese
|
|
|
|
assert pdfinfo['Subject'] == high_unicode
|
2015-07-27 16:11:51 -07:00
|
|
|
assert pdfinfo.get('Keywords', '') == ''
|
2015-07-27 17:18:02 -07:00
|
|
|
|
2016-08-03 02:47:18 -07:00
|
|
|
pdfa_info = file_claims_pdfa(output_file)
|
|
|
|
assert pdfa_info['output'] == output_type
|
|
|
|
|
2015-07-27 17:18:02 -07:00
|
|
|
|
2015-12-17 14:00:17 -08:00
|
|
|
@pytest.mark.parametrize('renderer', [
|
|
|
|
'hocr',
|
|
|
|
'tesseract',
|
|
|
|
])
|
|
|
|
def test_oversample(spoof_tesseract_cache, renderer):
|
2015-07-28 00:43:22 -07:00
|
|
|
oversampled_pdf = check_ocrmypdf(
|
2016-02-09 02:18:54 -08:00
|
|
|
'skew.pdf', 'test_oversample_%s.pdf' % renderer, '--oversample', '350',
|
2016-01-15 15:55:23 -08:00
|
|
|
'-f',
|
2015-12-17 14:00:17 -08:00
|
|
|
'--pdf-renderer', renderer, env=spoof_tesseract_cache)
|
2015-07-27 17:18:02 -07:00
|
|
|
|
|
|
|
pdfinfo = pdf_get_all_pageinfo(oversampled_pdf)
|
|
|
|
|
|
|
|
print(pdfinfo[0]['xres'])
|
2016-02-09 02:18:54 -08:00
|
|
|
assert abs(pdfinfo[0]['xres'] - 350) < 1
|
2015-07-27 22:07:04 -07:00
|
|
|
|
|
|
|
|
|
|
|
def test_repeat_ocr():
|
2016-08-26 15:18:38 -07:00
|
|
|
p, _, _ = run_ocrmypdf('graph_ocred.pdf', 'wontwork.pdf')
|
|
|
|
assert p.returncode != 0
|
2015-07-27 22:07:04 -07:00
|
|
|
|
|
|
|
|
2015-12-17 12:52:12 -08:00
|
|
|
def test_force_ocr(spoof_tesseract_cache):
|
|
|
|
out = check_ocrmypdf('graph_ocred.pdf', 'test_force.pdf', '-f',
|
|
|
|
env=spoof_tesseract_cache)
|
2015-07-28 02:31:18 -07:00
|
|
|
pdfinfo = pdf_get_all_pageinfo(out)
|
|
|
|
assert pdfinfo[0]['has_text']
|
2015-07-27 22:07:04 -07:00
|
|
|
|
|
|
|
|
2015-12-17 12:52:12 -08:00
|
|
|
def test_skip_ocr(spoof_tesseract_cache):
|
|
|
|
check_ocrmypdf('graph_ocred.pdf', 'test_skip.pdf', '-s',
|
|
|
|
env=spoof_tesseract_cache)
|
2015-07-28 01:47:30 -07:00
|
|
|
|
|
|
|
|
2015-12-17 11:56:09 -08:00
|
|
|
def test_argsfile(spoof_tesseract_noop):
|
2016-02-08 12:57:26 -08:00
|
|
|
with open(_outfile('test_argsfile.txt'), 'w') as argsfile:
|
2015-08-05 23:17:38 -07:00
|
|
|
print('--title', 'ArgsFile Test', '--author', 'Test Cases',
|
|
|
|
sep='\n', end='\n', file=argsfile)
|
|
|
|
check_ocrmypdf('graph.pdf', 'test_argsfile.pdf',
|
2016-02-08 12:57:26 -08:00
|
|
|
'@' + _outfile('test_argsfile.txt'),
|
2015-12-17 11:56:09 -08:00
|
|
|
env=spoof_tesseract_noop)
|
2015-08-05 23:17:38 -07:00
|
|
|
|
|
|
|
|
2016-02-15 17:17:43 -08:00
|
|
|
def check_monochrome_correlation(
|
|
|
|
reference_pdf, reference_pageno, test_pdf, test_pageno):
|
|
|
|
|
|
|
|
import ocrmypdf.ghostscript as ghostscript
|
|
|
|
import logging
|
|
|
|
|
|
|
|
gslog = logging.getLogger()
|
|
|
|
|
|
|
|
reference_png = _outfile('{}.ref{:04d}.png'.format(
|
|
|
|
reference_pdf, reference_pageno))
|
|
|
|
test_png = _outfile('{}.test{:04d}.png'.format(
|
|
|
|
test_pdf, test_pageno))
|
|
|
|
|
|
|
|
def rasterize(pdf, pageno, png):
|
|
|
|
if os.path.exists(png):
|
|
|
|
print(png)
|
|
|
|
return
|
|
|
|
ghostscript.rasterize_pdf(
|
|
|
|
pdf,
|
|
|
|
png,
|
|
|
|
xres=100, yres=100,
|
|
|
|
raster_device='pngmono', log=gslog, pageno=pageno)
|
|
|
|
|
|
|
|
rasterize(reference_pdf, reference_pageno, reference_png)
|
|
|
|
rasterize(test_pdf, test_pageno, test_png)
|
|
|
|
|
|
|
|
pix_ref = leptonica.Pix.read(reference_png)
|
|
|
|
pix_test = leptonica.Pix.read(test_png)
|
|
|
|
|
|
|
|
return leptonica.Pix.correlation_binary(pix_ref, pix_test)
|
|
|
|
|
|
|
|
|
|
|
|
def test_monochrome_correlation():
|
|
|
|
# Verify leptonica: check that an incorrect rotated image has poor
|
|
|
|
# correlation with reference
|
|
|
|
corr = check_monochrome_correlation(
|
|
|
|
reference_pdf=_infile('cardinal.pdf'),
|
|
|
|
reference_pageno=1, # north facing page
|
|
|
|
test_pdf=_infile('cardinal.pdf'),
|
|
|
|
test_pageno=3, # south facing page
|
|
|
|
)
|
|
|
|
assert corr < 0.10
|
2016-02-19 14:35:31 -08:00
|
|
|
corr = check_monochrome_correlation(
|
|
|
|
reference_pdf=_infile('cardinal.pdf'),
|
|
|
|
reference_pageno=2,
|
|
|
|
test_pdf=_infile('cardinal.pdf'),
|
|
|
|
test_pageno=2,
|
|
|
|
)
|
|
|
|
assert corr > 0.90
|
2016-02-15 17:17:43 -08:00
|
|
|
|
|
|
|
|
2016-02-08 12:32:39 -08:00
|
|
|
@pytest.mark.parametrize('renderer', [
|
|
|
|
'hocr',
|
|
|
|
'tesseract',
|
|
|
|
])
|
|
|
|
def test_autorotate(spoof_tesseract_cache, renderer):
|
2016-02-08 15:20:01 -08:00
|
|
|
# cardinal.pdf contains four copies of an image rotated in each cardinal
|
|
|
|
# direction - these ones are "burned in" not tagged with /Rotate
|
2016-02-08 12:32:39 -08:00
|
|
|
out = check_ocrmypdf('cardinal.pdf', 'test_autorotate_%s.pdf' % renderer,
|
2016-02-08 12:55:28 -08:00
|
|
|
'-r', '-v', '1', env=spoof_tesseract_cache)
|
2016-02-08 12:32:39 -08:00
|
|
|
for n in range(1, 4+1):
|
2016-02-15 17:17:43 -08:00
|
|
|
correlation = check_monochrome_correlation(
|
|
|
|
reference_pdf=_infile('cardinal.pdf'),
|
|
|
|
reference_pageno=1,
|
|
|
|
test_pdf=out,
|
|
|
|
test_pageno=n)
|
2016-02-08 13:10:11 -08:00
|
|
|
assert correlation > 0.80
|
2016-02-08 12:32:39 -08:00
|
|
|
|
2016-02-08 15:20:01 -08:00
|
|
|
|
2016-04-14 13:49:44 -07:00
|
|
|
def test_autorotate_threshold_low(spoof_tesseract_cache):
|
2016-04-28 00:39:15 -07:00
|
|
|
out = check_ocrmypdf('cardinal.pdf', 'test_autorotate_threshold_low.pdf',
|
2016-04-14 13:49:44 -07:00
|
|
|
'--rotate-pages-threshold', '1',
|
|
|
|
'-r', '-v', '1', env=spoof_tesseract_cache)
|
|
|
|
|
|
|
|
# Low threshold -> always rotate -> expect high correlation between
|
|
|
|
# reference page and test page
|
|
|
|
correlation = check_monochrome_correlation(
|
|
|
|
reference_pdf=_infile('cardinal.pdf'),
|
|
|
|
reference_pageno=1,
|
|
|
|
test_pdf=out,
|
|
|
|
test_pageno=3)
|
|
|
|
assert correlation > 0.80
|
|
|
|
|
|
|
|
|
|
|
|
def test_autorotate_threshold_high(spoof_tesseract_cache):
|
2016-04-28 00:39:15 -07:00
|
|
|
out = check_ocrmypdf('cardinal.pdf', 'test_autorotate_threshold_high.pdf',
|
2016-04-14 13:49:44 -07:00
|
|
|
'--rotate-pages-threshold', '99',
|
|
|
|
'-r', '-v', '1', env=spoof_tesseract_cache)
|
|
|
|
|
|
|
|
# High threshold -> never rotate -> expect low correlation since
|
|
|
|
# test page will not be rotated
|
|
|
|
correlation = check_monochrome_correlation(
|
|
|
|
reference_pdf=_infile('cardinal.pdf'),
|
|
|
|
reference_pageno=1,
|
|
|
|
test_pdf=out,
|
|
|
|
test_pageno=3)
|
|
|
|
assert correlation < 0.10
|
|
|
|
|
|
|
|
|
2015-12-17 14:00:17 -08:00
|
|
|
@pytest.mark.parametrize('renderer', [
|
|
|
|
'hocr',
|
|
|
|
'tesseract',
|
|
|
|
])
|
|
|
|
def test_ocr_timeout(renderer):
|
2015-07-28 02:31:18 -07:00
|
|
|
out = check_ocrmypdf('skew.pdf', 'test_timeout_%s.pdf' % renderer,
|
|
|
|
'--tesseract-timeout', '1.0')
|
|
|
|
pdfinfo = pdf_get_all_pageinfo(out)
|
2016-02-08 15:20:01 -08:00
|
|
|
assert not pdfinfo[0]['has_text']
|
2015-07-28 01:47:30 -07:00
|
|
|
|
|
|
|
|
2015-12-17 12:52:12 -08:00
|
|
|
def test_skip_big(spoof_tesseract_cache):
|
2015-07-28 02:31:18 -07:00
|
|
|
out = check_ocrmypdf('enormous.pdf', 'test_enormous.pdf',
|
2015-12-17 12:52:12 -08:00
|
|
|
'--skip-big', '10', env=spoof_tesseract_cache)
|
2015-07-28 02:31:18 -07:00
|
|
|
pdfinfo = pdf_get_all_pageinfo(out)
|
2016-02-08 15:20:01 -08:00
|
|
|
assert not pdfinfo[0]['has_text']
|
2015-07-28 02:31:18 -07:00
|
|
|
|
2015-07-28 03:02:35 -07:00
|
|
|
|
2016-08-03 02:47:18 -07:00
|
|
|
@pytest.mark.parametrize('renderer', ['hocr', 'tesseract'])
|
|
|
|
@pytest.mark.parametrize('output_type', ['pdf', 'pdfa'])
|
|
|
|
def test_maximum_options(spoof_tesseract_cache, renderer, output_type):
|
2015-07-28 03:02:35 -07:00
|
|
|
check_ocrmypdf(
|
|
|
|
'multipage.pdf', 'test_multipage%s.pdf' % renderer,
|
|
|
|
'-d', '-c', '-i', '-g', '-f', '-k', '--oversample', '300',
|
|
|
|
'--skip-big', '10', '--title', 'Too Many Weird Files',
|
2015-12-17 14:00:17 -08:00
|
|
|
'--author', 'py.test', '--pdf-renderer', renderer,
|
2016-08-03 02:47:18 -07:00
|
|
|
'--output-type', output_type,
|
2015-12-17 14:00:17 -08:00
|
|
|
env=spoof_tesseract_cache)
|
2015-08-10 13:57:28 -07:00
|
|
|
|
|
|
|
|
2015-08-11 00:17:02 -07:00
|
|
|
def test_tesseract_missing_tessdata():
|
2015-08-16 01:57:35 -07:00
|
|
|
env = os.environ.copy()
|
2015-08-11 00:17:02 -07:00
|
|
|
env['TESSDATA_PREFIX'] = '/tmp'
|
|
|
|
|
2016-08-26 15:18:38 -07:00
|
|
|
p, _, err = run_ocrmypdf(
|
2015-08-11 00:23:48 -07:00
|
|
|
'graph_ocred.pdf', 'not_a_pdfa.pdf', '-v', '1', '--skip-text', env=env)
|
2015-08-11 00:17:02 -07:00
|
|
|
assert p.returncode == ExitCode.missing_dependency, err
|
|
|
|
|
2015-08-11 02:19:46 -07:00
|
|
|
|
|
|
|
def test_invalid_input_pdf():
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2015-08-11 02:19:46 -07:00
|
|
|
'invalid.pdf', 'wont_be_created.pdf')
|
|
|
|
assert p.returncode == ExitCode.input_file, err
|
|
|
|
|
2015-08-14 00:46:50 -07:00
|
|
|
|
|
|
|
def test_blank_input_pdf():
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2015-08-14 00:46:50 -07:00
|
|
|
'blank.pdf', 'still_blank.pdf')
|
|
|
|
assert p.returncode == ExitCode.ok
|
|
|
|
|
2015-08-18 23:27:50 -07:00
|
|
|
|
2016-07-27 15:06:49 -07:00
|
|
|
def test_force_ocr_on_pdf_with_no_images(spoof_tesseract_crash):
|
|
|
|
# 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.
|
2016-08-26 15:18:38 -07:00
|
|
|
p, _, err = run_ocrmypdf(
|
2016-07-27 15:06:49 -07:00
|
|
|
'blank.pdf', 'wont_be_created.pdf', '--force-ocr',
|
|
|
|
env=spoof_tesseract_crash)
|
|
|
|
assert p.returncode == ExitCode.child_process_error, err
|
|
|
|
assert not os.path.exists(_outfile('wontwork.pdf'))
|
|
|
|
|
|
|
|
|
2015-12-17 14:00:17 -08:00
|
|
|
def test_french(spoof_tesseract_cache):
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2015-12-17 14:00:17 -08:00
|
|
|
'francais.pdf', 'francais.pdf', '-l', 'fra', env=spoof_tesseract_cache)
|
2015-08-18 23:27:50 -07:00
|
|
|
assert p.returncode == ExitCode.ok, \
|
|
|
|
"This test may fail if Tesseract language packs are missing"
|
|
|
|
|
|
|
|
|
|
|
|
def test_klingon():
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2015-08-18 23:56:30 -07:00
|
|
|
'francais.pdf', 'francais.pdf', '-l', 'klz')
|
2015-08-18 23:27:50 -07:00
|
|
|
assert p.returncode == ExitCode.bad_args
|
2015-08-24 01:23:30 -07:00
|
|
|
|
|
|
|
|
2015-12-17 14:00:17 -08:00
|
|
|
def test_missing_docinfo(spoof_tesseract_noop):
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2015-12-17 14:00:17 -08:00
|
|
|
'missing_docinfo.pdf', 'missing_docinfo.pdf', '-l', 'eng', '-c',
|
|
|
|
env=spoof_tesseract_noop)
|
2015-08-24 01:23:30 -07:00
|
|
|
assert p.returncode == ExitCode.ok, err
|
|
|
|
|
2015-12-04 02:14:09 -08:00
|
|
|
|
2016-02-20 23:47:37 -08:00
|
|
|
@pytest.mark.skipif(running_in_docker(),
|
|
|
|
reason="writes to tests/resources")
|
2015-12-17 14:00:17 -08:00
|
|
|
def test_uppercase_extension(spoof_tesseract_noop):
|
2016-02-08 12:57:26 -08:00
|
|
|
shutil.copy(_infile("skew.pdf"), _infile("UPPERCASE.PDF"))
|
2015-12-04 02:14:09 -08:00
|
|
|
try:
|
2015-12-17 14:00:17 -08:00
|
|
|
check_ocrmypdf("UPPERCASE.PDF", "UPPERCASE_OUT.PDF",
|
|
|
|
env=spoof_tesseract_noop)
|
2015-12-04 02:14:09 -08:00
|
|
|
finally:
|
2016-02-08 12:57:26 -08:00
|
|
|
os.unlink(_infile("UPPERCASE.PDF"))
|
2015-12-04 02:14:09 -08:00
|
|
|
|
|
|
|
|
2015-12-04 03:07:53 -08:00
|
|
|
def test_input_file_not_found():
|
|
|
|
input_file = "does not exist.pdf"
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2016-02-08 12:57:26 -08:00
|
|
|
_infile(input_file),
|
|
|
|
_outfile("will not happen.pdf"))
|
2016-08-26 15:18:38 -07:00
|
|
|
assert p.returncode == ExitCode.input_file
|
2015-12-04 03:07:53 -08:00
|
|
|
assert (input_file in out or input_file in err)
|
|
|
|
|
|
|
|
|
|
|
|
def test_input_file_not_a_pdf():
|
|
|
|
input_file = __file__ # Try to OCR this file
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2016-02-08 12:57:26 -08:00
|
|
|
_infile(input_file),
|
|
|
|
_outfile("will not happen.pdf"))
|
2016-08-26 15:18:38 -07:00
|
|
|
assert p.returncode == ExitCode.input_file
|
2015-12-04 03:07:53 -08:00
|
|
|
assert (input_file in out or input_file in err)
|
|
|
|
|
2015-12-17 09:29:01 -08:00
|
|
|
|
|
|
|
def test_qpdf_repair_fails():
|
|
|
|
env = os.environ.copy()
|
2015-12-17 11:36:47 -08:00
|
|
|
env['OCRMYPDF_QPDF'] = os.path.abspath('./spoof/qpdf_dummy_return2.py')
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2015-12-17 09:29:01 -08:00
|
|
|
'-v', '1',
|
|
|
|
'c02-22.pdf', 'wont_be_created.pdf', env=env)
|
|
|
|
print(out)
|
|
|
|
print(err)
|
|
|
|
assert p.returncode == ExitCode.input_file
|
2015-12-17 10:37:54 -08:00
|
|
|
|
|
|
|
|
|
|
|
def test_encrypted():
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf('skew-encrypted.pdf', 'wont_be_created.pdf')
|
2015-12-17 10:37:54 -08:00
|
|
|
assert p.returncode == ExitCode.input_file
|
|
|
|
assert out.find('password')
|
2016-01-11 17:19:32 -08:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('renderer', [
|
|
|
|
'hocr',
|
|
|
|
'tesseract',
|
|
|
|
])
|
2016-01-11 17:22:50 -08:00
|
|
|
def test_pagesegmode(renderer, spoof_tesseract_cache):
|
2016-01-11 17:19:32 -08:00
|
|
|
check_ocrmypdf(
|
|
|
|
'skew.pdf', 'test_psm_%s.pdf' % renderer,
|
|
|
|
'--tesseract-pagesegmode', '7',
|
|
|
|
'-v', '1',
|
2016-01-11 17:22:50 -08:00
|
|
|
'--pdf-renderer', renderer, env=spoof_tesseract_cache)
|
2016-01-11 17:19:32 -08:00
|
|
|
|
|
|
|
|
2016-02-19 03:48:49 -08:00
|
|
|
@pytest.mark.parametrize('renderer', [
|
|
|
|
'hocr',
|
|
|
|
'tesseract',
|
|
|
|
])
|
|
|
|
def test_tesseract_crash(renderer, spoof_tesseract_crash):
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2016-02-19 03:48:49 -08:00
|
|
|
'ccitt.pdf', 'wontwork.pdf', '-v', '1',
|
|
|
|
'--pdf-renderer', renderer, env=spoof_tesseract_crash)
|
2016-08-26 15:18:38 -07:00
|
|
|
assert p.returncode == ExitCode.child_process_error
|
2016-02-19 03:48:49 -08:00
|
|
|
assert not os.path.exists(_outfile('wontwork.pdf'))
|
|
|
|
assert "ERROR" in err
|
2016-02-19 03:58:39 -08:00
|
|
|
|
|
|
|
|
|
|
|
def test_tesseract_crash_autorotate(spoof_tesseract_crash):
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2016-02-19 03:58:39 -08:00
|
|
|
'ccitt.pdf', 'wontwork.pdf',
|
|
|
|
'-r', env=spoof_tesseract_crash)
|
2016-08-26 15:18:38 -07:00
|
|
|
assert p.returncode == ExitCode.child_process_error
|
2016-02-19 03:58:39 -08:00
|
|
|
assert not os.path.exists(_outfile('wontwork.pdf'))
|
|
|
|
assert "ERROR" in err
|
2016-04-28 00:39:15 -07:00
|
|
|
print(out)
|
|
|
|
print(err)
|
2016-02-20 04:53:02 -08:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('renderer', [
|
|
|
|
'hocr',
|
|
|
|
'tesseract',
|
|
|
|
])
|
|
|
|
def test_tesseract_image_too_big(renderer, spoof_tesseract_big_image_error):
|
|
|
|
check_ocrmypdf(
|
|
|
|
'hugemono.pdf', 'hugemono_%s.pdf' % renderer, '-r',
|
|
|
|
'--pdf-renderer', renderer, env=spoof_tesseract_big_image_error)
|
2016-03-10 15:37:09 -08:00
|
|
|
|
|
|
|
|
|
|
|
def test_no_unpaper():
|
|
|
|
env = os.environ.copy()
|
|
|
|
env['OCRMYPDF_UNPAPER'] = os.path.abspath('./spoof/no_unpaper_here.py')
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2016-03-10 15:37:09 -08:00
|
|
|
'c02-22.pdf', 'wont_be_created.pdf', '--clean', env=env)
|
2016-08-26 15:18:38 -07:00
|
|
|
assert p.returncode == ExitCode.missing_dependency
|
2016-03-10 15:37:09 -08:00
|
|
|
|
|
|
|
|
|
|
|
def test_old_unpaper():
|
|
|
|
env = os.environ.copy()
|
|
|
|
env['OCRMYPDF_UNPAPER'] = os.path.abspath('./spoof/unpaper_oldversion.py')
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2016-03-10 15:37:09 -08:00
|
|
|
'c02-22.pdf', 'wont_be_created.pdf', '--clean', env=env)
|
2016-08-26 15:18:38 -07:00
|
|
|
assert p.returncode == ExitCode.missing_dependency
|
2016-03-10 15:37:09 -08:00
|
|
|
|
2016-06-23 13:21:26 -07:00
|
|
|
|
|
|
|
def test_algo4():
|
2016-08-26 15:18:38 -07:00
|
|
|
p, _, _ = run_ocrmypdf('encrypted_algo4.pdf', 'wontwork.pdf')
|
|
|
|
assert p.returncode == ExitCode.encrypted_pdf
|
2016-07-28 16:20:59 -07:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('renderer', [
|
2016-08-03 11:35:48 -07:00
|
|
|
'hocr']) # tesseract cannot pass this test - resamples to square image
|
2016-07-29 01:34:52 -07:00
|
|
|
def test_non_square_resolution(renderer, spoof_tesseract_cache):
|
2016-07-28 16:20:59 -07:00
|
|
|
# Confirm input image is non-square resolution
|
|
|
|
in_pageinfo = pdf_get_all_pageinfo(_infile('aspect.pdf'))
|
|
|
|
assert in_pageinfo[0]['xres'] != in_pageinfo[0]['yres']
|
|
|
|
|
|
|
|
out = 'aspect_%s.pdf' % renderer
|
|
|
|
check_ocrmypdf(
|
|
|
|
'aspect.pdf', out,
|
2016-07-29 01:34:52 -07:00
|
|
|
'--pdf-renderer', renderer, env=spoof_tesseract_cache)
|
2016-07-28 16:20:59 -07:00
|
|
|
|
|
|
|
out_pageinfo = pdf_get_all_pageinfo(_outfile(out))
|
|
|
|
|
|
|
|
# Confirm resolution was kept the same
|
|
|
|
assert in_pageinfo[0]['xres'] == out_pageinfo[0]['xres']
|
|
|
|
assert in_pageinfo[0]['yres'] == out_pageinfo[0]['yres']
|
2016-08-03 03:35:30 -07:00
|
|
|
|
|
|
|
|
|
|
|
def test_image_to_pdf(spoof_tesseract_noop):
|
|
|
|
check_ocrmypdf(
|
|
|
|
'LinnSequencer.jpg', 'image_to_pdf.pdf', '--image-dpi', '200',
|
|
|
|
env=spoof_tesseract_noop)
|
2016-08-03 11:35:48 -07:00
|
|
|
|
|
|
|
|
|
|
|
def test_jbig2_passthrough(spoof_tesseract_cache):
|
|
|
|
out = check_ocrmypdf(
|
|
|
|
'jbig2.pdf', 'jbig2_out.pdf',
|
|
|
|
'--output-type', 'pdf',
|
|
|
|
'--pdf-renderer', 'hocr',
|
|
|
|
env=spoof_tesseract_cache)
|
|
|
|
|
|
|
|
out_pageinfo = pdf_get_all_pageinfo(out)
|
|
|
|
assert out_pageinfo[0]['images'][0]['enc'] == 'jbig2'
|
|
|
|
|
2016-08-25 14:46:54 -07:00
|
|
|
|
|
|
|
def test_stdin(spoof_tesseract_noop):
|
|
|
|
input_file = _infile('francais.pdf')
|
|
|
|
output_file = _outfile('test_stdin.pdf')
|
|
|
|
|
2016-08-26 15:04:08 -07:00
|
|
|
# Runs: cat testfile.pdf | ocrmypdf - output.pdf
|
|
|
|
|
2016-08-25 14:46:54 -07:00
|
|
|
p1_args = ['cat', input_file]
|
|
|
|
p1 = Popen(p1_args, close_fds=True, stdin=DEVNULL, stdout=PIPE)
|
|
|
|
|
2016-08-26 15:23:26 -07:00
|
|
|
p2_args = OCRMYPDF + ['-', output_file]
|
2016-08-25 14:46:54 -07:00
|
|
|
p2 = Popen(
|
|
|
|
p2_args, close_fds=True, stdout=PIPE, stderr=PIPE,
|
|
|
|
stdin=p1.stdout, env=spoof_tesseract_noop)
|
|
|
|
p1.stdout.close()
|
|
|
|
out, err = p2.communicate()
|
|
|
|
|
|
|
|
assert p2.returncode == ExitCode.ok
|
|
|
|
|
2016-08-26 15:03:27 -07:00
|
|
|
|
|
|
|
def test_masks(spoof_tesseract_noop):
|
|
|
|
check_ocrmypdf('masks.pdf', 'test_masks.pdf', env=spoof_tesseract_noop)
|
|
|
|
|
2016-08-31 11:42:21 -07:00
|
|
|
|
2016-09-15 08:55:31 -07:00
|
|
|
@pytest.mark.skipif(not os.path.isfile(_infile('milk.pdf')),
|
|
|
|
reason="fair use restricted test resource does not exist")
|
2016-08-31 11:42:21 -07:00
|
|
|
def test_linearized_pdf_and_indirect_object(spoof_tesseract_noop):
|
|
|
|
check_ocrmypdf('milk.pdf', 'test_milk.pdf', env=spoof_tesseract_noop)
|