2017-11-29 12:47:09 -08:00
|
|
|
# © 2015-17 James R. Barlow: github.com/jbarlow83
|
2018-03-14 14:40:48 -07:00
|
|
|
#
|
|
|
|
# This file is part of OCRmyPDF.
|
|
|
|
#
|
|
|
|
# OCRmyPDF is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# OCRmyPDF is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
|
2015-07-22 02:59:25 -07:00
|
|
|
|
2016-08-25 14:46:54 -07:00
|
|
|
from subprocess import Popen, PIPE, check_output, check_call, DEVNULL
|
2018-02-19 22:15:07 -08:00
|
|
|
from pathlib import Path
|
2015-07-22 02:59:25 -07:00
|
|
|
import os
|
2015-07-22 04:00:59 -07:00
|
|
|
import shutil
|
2017-09-12 14:33:47 -07:00
|
|
|
import resource
|
2015-07-28 00:43:22 -07:00
|
|
|
import pytest
|
2017-05-19 15:48:23 -07:00
|
|
|
from ocrmypdf.pdfinfo import PdfInfo, Colorspace, Encoding
|
2015-08-05 17:09:38 -07:00
|
|
|
import PyPDF2 as pypdf
|
2016-12-10 15:24:24 -08:00
|
|
|
from ocrmypdf.exceptions 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
|
2017-11-26 22:52:53 -08:00
|
|
|
from ocrmypdf.exec import ghostscript, tesseract, qpdf
|
2017-05-14 23:15:29 -07:00
|
|
|
import logging
|
2017-05-18 18:39:14 -07:00
|
|
|
from math import isclose
|
2015-07-27 22:07:04 -07:00
|
|
|
|
2018-01-10 15:43:59 -08:00
|
|
|
import PIL
|
|
|
|
|
2018-02-24 11:59:01 -08:00
|
|
|
# pytest.helpers is dynamic
|
|
|
|
# pylint: disable=no-member
|
|
|
|
# pylint: disable=w0612
|
2015-07-22 11:21:33 -07:00
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
check_ocrmypdf = pytest.helpers.check_ocrmypdf
|
|
|
|
run_ocrmypdf = pytest.helpers.run_ocrmypdf
|
|
|
|
spoof = pytest.helpers.spoof
|
2015-12-17 11:36:47 -08:00
|
|
|
|
2016-02-19 03:48:49 -08:00
|
|
|
|
2017-06-13 13:09:12 -07:00
|
|
|
RENDERERS = ['hocr', 'tesseract']
|
|
|
|
if tesseract.has_textonly_pdf():
|
|
|
|
RENDERERS.append('sandwich')
|
|
|
|
|
|
|
|
|
2018-03-24 15:07:02 -07:00
|
|
|
@pytest.fixture(scope='session')
|
|
|
|
def spoof_tesseract_crash(tmpdir_factory):
|
|
|
|
return spoof(tmpdir_factory, tesseract='tesseract_crash.py')
|
2015-12-17 12:52:12 -08:00
|
|
|
|
|
|
|
|
2018-03-24 15:07:02 -07:00
|
|
|
@pytest.fixture(scope='session')
|
|
|
|
def spoof_tesseract_big_image_error(tmpdir_factory):
|
|
|
|
return spoof(tmpdir_factory, tesseract='tesseract_big_image_error.py')
|
2016-12-03 00:31:40 -08:00
|
|
|
|
|
|
|
|
2018-03-24 15:07:02 -07:00
|
|
|
@pytest.fixture(scope='session')
|
|
|
|
def spoof_no_tess_no_pdfa(tmpdir_factory):
|
|
|
|
return spoof(tmpdir_factory, tesseract='tesseract_noop.py', gs='gs_pdfa_failure.py')
|
2016-12-03 00:31:40 -08:00
|
|
|
|
|
|
|
|
2018-03-24 15:07:02 -07:00
|
|
|
@pytest.fixture(scope='session')
|
|
|
|
def spoof_no_tess_pdfa_warning(tmpdir_factory):
|
|
|
|
return spoof(tmpdir_factory, tesseract='tesseract_noop.py', gs='gs_feature_elision.py')
|
2016-02-20 04:53:02 -08:00
|
|
|
|
|
|
|
|
2018-03-24 15:07:02 -07:00
|
|
|
@pytest.fixture(scope='session')
|
|
|
|
def spoof_no_tess_gs_render_fail(tmpdir_factory):
|
|
|
|
return spoof(tmpdir_factory, tesseract='tesseract_noop.py', gs='gs_render_failure.py')
|
2017-05-01 15:44:21 -07:00
|
|
|
|
|
|
|
|
2018-03-24 15:07:02 -07:00
|
|
|
@pytest.fixture(scope='session')
|
|
|
|
def spoof_no_tess_gs_raster_fail(tmpdir_factory):
|
|
|
|
return spoof(tmpdir_factory, tesseract='tesseract_noop.py', gs='gs_raster_failure.py')
|
2017-05-01 15:44:21 -07:00
|
|
|
|
|
|
|
|
2018-03-24 15:07:02 -07:00
|
|
|
@pytest.fixture(scope='session')
|
|
|
|
def spoof_tess_bad_utf8(tmpdir_factory):
|
|
|
|
return spoof(tmpdir_factory, tesseract='tesseract_badutf8.py')
|
2017-11-29 14:08:07 -08:00
|
|
|
|
|
|
|
|
2018-03-24 15:07:02 -07:00
|
|
|
@pytest.fixture(scope='session')
|
|
|
|
def spoof_qpdf_always_error(tmpdir_factory):
|
|
|
|
return spoof(tmpdir_factory, qpdf='qpdf_dummy_return2.py')
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='session')
|
|
|
|
def spoof_unpaper_missing(tmpdir_factory):
|
|
|
|
return spoof(tmpdir_factory) #
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='session')
|
|
|
|
def spoof_unpaper_old(tmpdir_factory):
|
|
|
|
return spoof(tmpdir_factory, unpaper='unpaper_oldversion.py')
|
2017-01-26 16:38:59 -08:00
|
|
|
|
|
|
|
|
|
|
|
def test_quick(spoof_tesseract_cache, resources, outpdf):
|
|
|
|
check_ocrmypdf(resources / 'ccitt.pdf', outpdf, env=spoof_tesseract_cache)
|
2015-12-17 11:56:09 -08:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_deskew(spoof_tesseract_noop, resources, outdir):
|
2015-07-27 16:11:51 -07:00
|
|
|
# Run with deskew
|
2015-12-17 11:36:47 -08:00
|
|
|
deskewed_pdf = check_ocrmypdf(
|
2018-01-10 16:35:31 -08:00
|
|
|
resources / 'skew.pdf', outdir / 'skew.pdf', '-d',
|
2017-01-26 16:38:59 -08:00
|
|
|
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
|
|
|
log = logging.getLogger()
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
deskewed_png = outdir / 'deskewed.png'
|
2015-07-27 15:39:54 -07:00
|
|
|
|
2017-05-14 23:15:29 -07:00
|
|
|
ghostscript.rasterize_pdf(
|
2017-07-21 13:28:30 -07:00
|
|
|
deskewed_pdf,
|
|
|
|
deskewed_png,
|
2015-07-27 15:39:54 -07:00
|
|
|
xres=150,
|
|
|
|
yres=150,
|
|
|
|
raster_device='pngmono',
|
2018-01-10 16:35:31 -08:00
|
|
|
log=log,
|
|
|
|
pageno=1)
|
2015-07-27 15:39:54 -07:00
|
|
|
|
2016-02-08 15:20:01 -08:00
|
|
|
from ocrmypdf.leptonica import Pix
|
2017-01-26 16:38:59 -08:00
|
|
|
pix = Pix.read(str(deskewed_png))
|
2016-02-08 15:20:01 -08:00
|
|
|
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
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_clean(spoof_tesseract_noop, resources, outpdf):
|
|
|
|
check_ocrmypdf(resources / 'skew.pdf', outpdf, '-c',
|
2016-08-03 02:47:18 -07:00
|
|
|
env=spoof_tesseract_noop)
|
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_remove_background(spoof_tesseract_noop, resources, outdir):
|
2016-10-14 17:23:34 -07:00
|
|
|
from PIL import Image
|
|
|
|
|
|
|
|
# Ensure the input image does not contain pure white/black
|
2017-01-26 16:38:59 -08:00
|
|
|
im = Image.open(resources / 'congress.jpg')
|
2016-10-14 17:23:34 -07:00
|
|
|
assert im.getextrema() != ((0, 255), (0, 255), (0, 255))
|
|
|
|
|
|
|
|
output_pdf = check_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'congress.jpg',
|
|
|
|
outdir / 'test_remove_bg.pdf',
|
|
|
|
'--remove-background',
|
2016-10-14 17:23:34 -07:00
|
|
|
'--image-dpi', '150',
|
|
|
|
env=spoof_tesseract_noop)
|
|
|
|
|
|
|
|
log = logging.getLogger()
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
output_png = outdir / 'remove_bg.png'
|
2016-10-14 17:23:34 -07:00
|
|
|
|
2017-05-14 23:15:29 -07:00
|
|
|
ghostscript.rasterize_pdf(
|
2017-07-21 13:28:30 -07:00
|
|
|
output_pdf,
|
|
|
|
output_png,
|
2016-10-14 17:23:34 -07:00
|
|
|
xres=100,
|
|
|
|
yres=100,
|
|
|
|
raster_device='png16m',
|
2018-01-10 16:35:31 -08:00
|
|
|
log=log,
|
|
|
|
pageno=1)
|
2016-10-14 17:23:34 -07:00
|
|
|
|
|
|
|
# The output image should contain pure white and black
|
|
|
|
im = Image.open(output_png)
|
|
|
|
assert im.getextrema() == ((0, 255), (0, 255), (0, 255))
|
|
|
|
|
|
|
|
|
2016-08-03 02:47:18 -07:00
|
|
|
# This will run 5 * 2 * 2 = 20 test cases
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"pdf",
|
|
|
|
['palette.pdf', 'cmyk.pdf', 'ccitt.pdf', 'jbig2.pdf', 'lichtenstein.pdf'])
|
2017-06-13 13:13:58 -07:00
|
|
|
@pytest.mark.parametrize("renderer", ['auto', 'tesseract'])
|
2016-08-03 02:47:18 -07:00
|
|
|
@pytest.mark.parametrize("output_type", ['pdf', 'pdfa'])
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_exotic_image(spoof_tesseract_cache, pdf, renderer, output_type,
|
|
|
|
resources, outdir):
|
2017-05-10 15:22:44 -07:00
|
|
|
outfile = outdir / 'test_{0}_{1}.pdf'.format(pdf, renderer)
|
2015-08-28 04:48:51 -07:00
|
|
|
check_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / pdf,
|
2017-05-10 15:22:44 -07:00
|
|
|
outfile,
|
2015-08-28 04:48:51 -07:00
|
|
|
'-dc',
|
2015-12-17 14:00:17 -08:00
|
|
|
'-v', '1',
|
2016-08-03 02:47:18 -07:00
|
|
|
'--output-type', output_type,
|
2017-05-10 15:22:44 -07:00
|
|
|
'--sidecar',
|
2018-03-24 02:24:45 -07:00
|
|
|
'--skip-text',
|
2015-12-17 14:00:17 -08:00
|
|
|
'--pdf-renderer', renderer, env=spoof_tesseract_cache)
|
2015-08-28 04:48:51 -07:00
|
|
|
|
2017-05-10 15:22:44 -07:00
|
|
|
assert outfile.with_suffix('.pdf.txt').exists()
|
|
|
|
|
2015-08-28 04:48:51 -07:00
|
|
|
|
2016-08-03 02:47:18 -07:00
|
|
|
@pytest.mark.parametrize("output_type", [
|
|
|
|
'pdfa', 'pdf'
|
|
|
|
])
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_preserve_metadata(spoof_tesseract_noop, output_type,
|
|
|
|
resources, outpdf):
|
|
|
|
pdf_before = pypdf.PdfFileReader(str(resources / 'graph.pdf'))
|
2015-08-05 17:09:38 -07:00
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
output = check_ocrmypdf(
|
|
|
|
resources / 'graph.pdf', outpdf,
|
|
|
|
'--output-type', output_type,
|
|
|
|
env=spoof_tesseract_noop)
|
2015-08-05 17:09:38 -07:00
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
pdf_after = pypdf.PdfFileReader(str(output))
|
2015-08-05 17:09:38 -07:00
|
|
|
|
|
|
|
for key in ('/Title', '/Author'):
|
|
|
|
assert pdf_before.documentInfo[key] == pdf_after.documentInfo[key]
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
pdfa_info = file_claims_pdfa(str(output))
|
2016-08-03 02:47:18 -07:00
|
|
|
assert pdfa_info['output'] == output_type
|
|
|
|
|
2015-08-05 17:09:38 -07:00
|
|
|
|
2016-08-03 02:47:18 -07:00
|
|
|
@pytest.mark.parametrize("output_type", [
|
|
|
|
'pdfa', 'pdf'
|
|
|
|
])
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_override_metadata(spoof_tesseract_noop, output_type, resources,
|
|
|
|
outpdf):
|
|
|
|
input_file = resources / 'c02-22.pdf'
|
2015-08-05 16:57:04 -07:00
|
|
|
german = 'Du siehst den Wald vor lauter Bäumen nicht.'
|
|
|
|
chinese = '孔子'
|
|
|
|
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
input_file, outpdf,
|
2015-08-05 16:57:04 -07:00
|
|
|
'--title', german,
|
|
|
|
'--author', chinese,
|
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
|
|
|
|
2017-03-24 15:15:46 -07:00
|
|
|
assert p.returncode == ExitCode.ok, err
|
2015-08-11 00:23:48 -07:00
|
|
|
|
2017-03-28 11:05:43 -07:00
|
|
|
reader = pypdf.PdfFileReader(outpdf)
|
2015-07-27 16:11:51 -07:00
|
|
|
|
2017-03-28 11:05:43 -07:00
|
|
|
assert reader.documentInfo['/Title'] == german
|
|
|
|
assert reader.documentInfo['/Author'] == chinese
|
|
|
|
assert reader.documentInfo.get('/Keywords', '') == ''
|
2015-07-27 17:18:02 -07:00
|
|
|
|
2017-03-28 11:05:43 -07:00
|
|
|
pdfa_info = file_claims_pdfa(outpdf)
|
2016-08-03 02:47:18 -07:00
|
|
|
assert pdfa_info['output'] == output_type
|
|
|
|
|
2015-07-27 17:18:02 -07:00
|
|
|
|
2017-03-28 11:08:38 -07:00
|
|
|
def test_high_unicode(spoof_tesseract_noop, resources, no_outpdf):
|
|
|
|
|
|
|
|
# Ghostscript doesn't support high Unicode, so neither do we, to be
|
|
|
|
# safe
|
|
|
|
input_file = resources / 'c02-22.pdf'
|
|
|
|
high_unicode = 'U+1030C is: 𐌌'
|
|
|
|
|
|
|
|
p, out, err = run_ocrmypdf(
|
|
|
|
input_file, no_outpdf,
|
|
|
|
'--subject', high_unicode,
|
|
|
|
'--output-type', 'pdfa',
|
|
|
|
env=spoof_tesseract_noop)
|
|
|
|
|
|
|
|
assert p.returncode == ExitCode.bad_args, err
|
|
|
|
|
|
|
|
|
2017-06-13 13:09:12 -07:00
|
|
|
@pytest.mark.parametrize('renderer', RENDERERS)
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_oversample(spoof_tesseract_cache, renderer, resources, outpdf):
|
2015-07-28 00:43:22 -07:00
|
|
|
oversampled_pdf = check_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'skew.pdf', outpdf, '--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
|
|
|
|
2017-05-18 16:43:50 -07:00
|
|
|
pdfinfo = PdfInfo(oversampled_pdf)
|
2015-07-27 17:18:02 -07:00
|
|
|
|
2017-05-18 17:12:04 -07:00
|
|
|
print(pdfinfo[0].xres)
|
|
|
|
assert abs(pdfinfo[0].xres - 350) < 1
|
2015-07-27 22:07:04 -07:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_repeat_ocr(resources, no_outpdf):
|
|
|
|
p, _, _ = run_ocrmypdf(resources / 'graph_ocred.pdf', no_outpdf)
|
2016-08-26 15:18:38 -07:00
|
|
|
assert p.returncode != 0
|
2015-07-27 22:07:04 -07:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_force_ocr(spoof_tesseract_cache, resources, outpdf):
|
|
|
|
out = check_ocrmypdf(resources / 'graph_ocred.pdf', outpdf, '-f',
|
2015-12-17 12:52:12 -08:00
|
|
|
env=spoof_tesseract_cache)
|
2017-05-18 16:30:43 -07:00
|
|
|
pdfinfo = PdfInfo(out)
|
2017-05-18 17:12:04 -07:00
|
|
|
assert pdfinfo[0].has_text
|
2015-07-27 22:07:04 -07:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_skip_ocr(spoof_tesseract_cache, resources, outpdf):
|
|
|
|
check_ocrmypdf(resources / 'graph_ocred.pdf', outpdf, '-s',
|
2015-12-17 12:52:12 -08:00
|
|
|
env=spoof_tesseract_cache)
|
2015-07-28 01:47:30 -07:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_argsfile(spoof_tesseract_noop, resources, outdir):
|
2017-01-26 17:53:27 -08:00
|
|
|
path_argsfile = outdir / 'test_argsfile.txt'
|
|
|
|
with open(str(path_argsfile), 'w') as argsfile:
|
2015-08-05 23:17:38 -07:00
|
|
|
print('--title', 'ArgsFile Test', '--author', 'Test Cases',
|
|
|
|
sep='\n', end='\n', file=argsfile)
|
2017-01-26 17:53:27 -08:00
|
|
|
check_ocrmypdf(resources / 'graph.pdf', path_argsfile,
|
2017-01-26 16:38:59 -08:00
|
|
|
'@' + str(outdir / '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(
|
2017-01-26 16:38:59 -08:00
|
|
|
outdir,
|
2016-02-15 17:17:43 -08:00
|
|
|
reference_pdf, reference_pageno, test_pdf, test_pageno):
|
|
|
|
gslog = logging.getLogger()
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
reference_png = outdir / '{}.ref{:04d}.png'.format(
|
2017-01-26 18:07:09 -08:00
|
|
|
reference_pdf.name, reference_pageno)
|
2017-01-26 16:38:59 -08:00
|
|
|
test_png = outdir / '{}.test{:04d}.png'.format(
|
2017-01-26 18:07:09 -08:00
|
|
|
test_pdf.name, test_pageno)
|
2016-02-15 17:17:43 -08:00
|
|
|
|
|
|
|
def rasterize(pdf, pageno, png):
|
2017-01-26 16:38:59 -08:00
|
|
|
if png.exists():
|
2016-02-15 17:17:43 -08:00
|
|
|
print(png)
|
|
|
|
return
|
|
|
|
ghostscript.rasterize_pdf(
|
2017-07-21 13:28:30 -07:00
|
|
|
pdf, png, xres=100, yres=100,
|
2016-02-15 17:17:43 -08:00
|
|
|
raster_device='pngmono', log=gslog, pageno=pageno)
|
|
|
|
|
|
|
|
rasterize(reference_pdf, reference_pageno, reference_png)
|
|
|
|
rasterize(test_pdf, test_pageno, test_png)
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
pix_ref = leptonica.Pix.read(str(reference_png))
|
|
|
|
pix_test = leptonica.Pix.read(str(test_png))
|
2016-02-15 17:17:43 -08:00
|
|
|
|
|
|
|
return leptonica.Pix.correlation_binary(pix_ref, pix_test)
|
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_monochrome_correlation(resources, outdir):
|
2016-02-15 17:17:43 -08:00
|
|
|
# Verify leptonica: check that an incorrect rotated image has poor
|
|
|
|
# correlation with reference
|
|
|
|
corr = check_monochrome_correlation(
|
2017-01-26 16:38:59 -08:00
|
|
|
outdir,
|
|
|
|
reference_pdf=resources / 'cardinal.pdf',
|
2016-02-15 17:17:43 -08:00
|
|
|
reference_pageno=1, # north facing page
|
2017-01-26 16:38:59 -08:00
|
|
|
test_pdf=resources / 'cardinal.pdf',
|
2016-02-15 17:17:43 -08:00
|
|
|
test_pageno=3, # south facing page
|
|
|
|
)
|
|
|
|
assert corr < 0.10
|
2016-02-19 14:35:31 -08:00
|
|
|
corr = check_monochrome_correlation(
|
2017-01-26 16:38:59 -08:00
|
|
|
outdir,
|
|
|
|
reference_pdf=resources / 'cardinal.pdf',
|
2016-02-19 14:35:31 -08:00
|
|
|
reference_pageno=2,
|
2017-01-26 16:38:59 -08:00
|
|
|
test_pdf=resources / 'cardinal.pdf',
|
2016-02-19 14:35:31 -08:00
|
|
|
test_pageno=2,
|
|
|
|
)
|
|
|
|
assert corr > 0.90
|
2016-02-15 17:17:43 -08:00
|
|
|
|
|
|
|
|
2017-06-13 13:09:12 -07:00
|
|
|
@pytest.mark.parametrize('renderer', RENDERERS)
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_autorotate(spoof_tesseract_cache, renderer, resources, outdir):
|
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
|
2017-01-26 16:38:59 -08:00
|
|
|
out = check_ocrmypdf(resources / 'cardinal.pdf', outdir / 'out.pdf',
|
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(
|
2017-01-26 16:38:59 -08:00
|
|
|
outdir,
|
|
|
|
reference_pdf=resources / 'cardinal.pdf',
|
2016-02-15 17:17:43 -08:00
|
|
|
reference_pageno=1,
|
2017-01-26 16:38:59 -08:00
|
|
|
test_pdf=outdir / 'out.pdf',
|
2016-02-15 17:17:43 -08:00
|
|
|
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
|
|
|
|
2017-01-26 18:07:59 -08:00
|
|
|
@pytest.mark.parametrize('threshold, correlation_test', [
|
|
|
|
('1', 'correlation > 0.80'), # Low thresh -> always rotate -> high corr
|
|
|
|
('99', 'correlation < 0.10'), # High thres -> never rotate -> low corr
|
|
|
|
])
|
|
|
|
def test_autorotate_threshold(
|
|
|
|
spoof_tesseract_cache, threshold, correlation_test, resources, outdir):
|
2017-01-26 16:38:59 -08:00
|
|
|
out = check_ocrmypdf(resources / 'cardinal.pdf', outdir / 'out.pdf',
|
2017-01-26 18:07:59 -08:00
|
|
|
'--rotate-pages-threshold', threshold,
|
2016-04-14 13:49:44 -07:00
|
|
|
'-r', '-v', '1', env=spoof_tesseract_cache)
|
|
|
|
|
|
|
|
correlation = check_monochrome_correlation(
|
2017-01-26 16:38:59 -08:00
|
|
|
outdir,
|
|
|
|
reference_pdf=resources / 'cardinal.pdf',
|
2016-04-14 13:49:44 -07:00
|
|
|
reference_pageno=1,
|
2017-01-26 18:07:59 -08:00
|
|
|
test_pdf=outdir / 'out.pdf',
|
2016-04-14 13:49:44 -07:00
|
|
|
test_pageno=3)
|
2017-01-26 18:07:59 -08:00
|
|
|
assert eval(correlation_test)
|
2016-04-14 13:49:44 -07:00
|
|
|
|
|
|
|
|
2017-06-13 13:09:12 -07:00
|
|
|
@pytest.mark.parametrize('renderer',RENDERERS)
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_ocr_timeout(renderer, resources, outpdf):
|
|
|
|
out = check_ocrmypdf(resources / 'skew.pdf', outpdf,
|
2015-07-28 02:31:18 -07:00
|
|
|
'--tesseract-timeout', '1.0')
|
2017-05-18 16:43:50 -07:00
|
|
|
pdfinfo = PdfInfo(out)
|
2017-05-18 17:12:04 -07:00
|
|
|
assert not pdfinfo[0].has_text
|
2015-07-28 01:47:30 -07:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_skip_big(spoof_tesseract_cache, resources, outpdf):
|
|
|
|
out = check_ocrmypdf(resources / 'enormous.pdf', outpdf,
|
2015-12-17 12:52:12 -08:00
|
|
|
'--skip-big', '10', env=spoof_tesseract_cache)
|
2017-05-18 16:43:50 -07:00
|
|
|
pdfinfo = PdfInfo(out)
|
2017-05-18 17:12:04 -07:00
|
|
|
assert not pdfinfo[0].has_text
|
2015-07-28 02:31:18 -07:00
|
|
|
|
2015-07-28 03:02:35 -07:00
|
|
|
|
2017-06-13 13:09:12 -07:00
|
|
|
@pytest.mark.parametrize('renderer', RENDERERS)
|
2016-08-03 02:47:18 -07:00
|
|
|
@pytest.mark.parametrize('output_type', ['pdf', 'pdfa'])
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_maximum_options(spoof_tesseract_cache, renderer, output_type,
|
|
|
|
resources, outpdf):
|
2015-07-28 03:02:35 -07:00
|
|
|
check_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'multipage.pdf', outpdf,
|
2015-07-28 03:02:35 -07:00
|
|
|
'-d', '-c', '-i', '-g', '-f', '-k', '--oversample', '300',
|
2016-10-14 17:23:34 -07:00
|
|
|
'--remove-background',
|
2015-07-28 03:02:35 -07:00
|
|
|
'--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
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_tesseract_missing_tessdata(resources, no_outpdf):
|
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(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'graph_ocred.pdf', no_outpdf,
|
|
|
|
'-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
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_invalid_input_pdf(resources, no_outpdf):
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'invalid.pdf', no_outpdf)
|
2015-08-11 02:19:46 -07:00
|
|
|
assert p.returncode == ExitCode.input_file, err
|
|
|
|
|
2015-08-14 00:46:50 -07:00
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_blank_input_pdf(resources, outpdf):
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'blank.pdf', outpdf)
|
2015-08-14 00:46:50 -07:00
|
|
|
assert p.returncode == ExitCode.ok
|
|
|
|
|
2015-08-18 23:27:50 -07:00
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_force_ocr_on_pdf_with_no_images(spoof_tesseract_crash, resources,
|
|
|
|
no_outpdf):
|
2016-07-27 15:06:49 -07:00
|
|
|
# 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(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'blank.pdf', no_outpdf, '--force-ocr',
|
2016-07-27 15:06:49 -07:00
|
|
|
env=spoof_tesseract_crash)
|
|
|
|
assert p.returncode == ExitCode.child_process_error, err
|
2017-01-26 16:38:59 -08:00
|
|
|
assert not os.path.exists(no_outpdf)
|
2016-07-27 15:06:49 -07:00
|
|
|
|
|
|
|
|
2017-03-02 22:27:06 -08:00
|
|
|
@pytest.mark.skipif(
|
|
|
|
pytest.helpers.is_macos() and pytest.helpers.running_in_travis(),
|
|
|
|
reason="takes too long to install language packs in Travis macOS homebrew")
|
2017-08-30 13:25:54 -07:00
|
|
|
def test_french(spoof_tesseract_cache, resources, outdir):
|
|
|
|
# Produce a sidecar too - implicit test that system locale is set up
|
|
|
|
# properly
|
|
|
|
sidecar = outdir / 'francais.txt'
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2017-08-30 13:25:54 -07:00
|
|
|
resources / 'francais.pdf', outdir / 'francais.pdf', '-l', 'fra',
|
|
|
|
'--sidecar', sidecar,
|
2017-01-26 16:38:59 -08:00
|
|
|
env=spoof_tesseract_cache)
|
2017-03-02 22:27:06 -08:00
|
|
|
print(os.environ)
|
2015-08-18 23:27:50 -07:00
|
|
|
assert p.returncode == ExitCode.ok, \
|
|
|
|
"This test may fail if Tesseract language packs are missing"
|
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_klingon(resources, outpdf):
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'francais.pdf', outpdf, '-l', 'klz')
|
2017-08-24 13:01:02 -07:00
|
|
|
assert p.returncode == ExitCode.missing_dependency
|
2015-08-24 01:23:30 -07:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_missing_docinfo(spoof_tesseract_noop, resources, outpdf):
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2018-02-07 16:05:48 -08:00
|
|
|
resources / 'missing_docinfo.pdf', outpdf, '-l', 'eng', '--skip-text',
|
2015-12-17 14:00:17 -08:00
|
|
|
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
|
|
|
|
2017-01-26 13:22:01 -08:00
|
|
|
@pytest.mark.skipif(pytest.helpers.running_in_docker(),
|
2017-01-26 16:38:59 -08:00
|
|
|
reason="<no longer true> writes to tests/resources")
|
|
|
|
def test_uppercase_extension(spoof_tesseract_noop, resources, outdir):
|
|
|
|
shutil.copy(
|
|
|
|
str(resources / "skew.pdf"),
|
|
|
|
str(outdir / "UPPERCASE.PDF"))
|
|
|
|
|
|
|
|
check_ocrmypdf(outdir / "UPPERCASE.PDF", outdir / "UPPERCASE_OUT.PDF",
|
|
|
|
env=spoof_tesseract_noop)
|
2015-12-04 02:14:09 -08:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_input_file_not_found(no_outpdf):
|
2015-12-04 03:07:53 -08:00
|
|
|
input_file = "does not exist.pdf"
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
input_file,
|
|
|
|
no_outpdf)
|
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)
|
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_input_file_not_a_pdf(no_outpdf):
|
2015-12-04 03:07:53 -08:00
|
|
|
input_file = __file__ # Try to OCR this file
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
input_file,
|
|
|
|
no_outpdf)
|
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
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_qpdf_repair_fails(spoof_qpdf_always_error, resources, no_outpdf):
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2017-01-26 17:53:27 -08:00
|
|
|
resources / 'c02-22.pdf', no_outpdf,
|
2015-12-17 09:29:01 -08:00
|
|
|
'-v', '1',
|
2017-01-26 17:53:27 -08:00
|
|
|
env=spoof_qpdf_always_error)
|
2015-12-17 09:29:01 -08:00
|
|
|
print(err)
|
|
|
|
assert p.returncode == ExitCode.input_file
|
2015-12-17 10:37:54 -08:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_encrypted(resources, no_outpdf):
|
2016-12-10 15:24:24 -08:00
|
|
|
p, out, err = run_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'skew-encrypted.pdf', no_outpdf)
|
2016-12-10 15:24:24 -08:00
|
|
|
assert p.returncode == ExitCode.encrypted_pdf
|
2018-02-27 15:08:22 -08:00
|
|
|
assert out.find('encrypted')
|
2016-01-11 17:19:32 -08:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('renderer', [
|
|
|
|
'hocr',
|
|
|
|
'tesseract',
|
|
|
|
])
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_pagesegmode(renderer, spoof_tesseract_cache, resources, outpdf):
|
2016-01-11 17:19:32 -08:00
|
|
|
check_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'skew.pdf', outpdf,
|
2016-01-11 17:19:32 -08:00
|
|
|
'--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
|
|
|
|
|
|
|
|
2017-07-21 14:10:02 -07:00
|
|
|
@pytest.mark.parametrize('renderer', RENDERERS)
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_tesseract_crash(renderer, spoof_tesseract_crash,
|
|
|
|
resources, no_outpdf):
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'ccitt.pdf', no_outpdf, '-v', '1',
|
2016-02-19 03:48:49 -08:00
|
|
|
'--pdf-renderer', renderer, env=spoof_tesseract_crash)
|
2016-08-26 15:18:38 -07:00
|
|
|
assert p.returncode == ExitCode.child_process_error
|
2017-01-26 16:38:59 -08:00
|
|
|
assert not os.path.exists(no_outpdf)
|
2016-02-19 03:48:49 -08:00
|
|
|
assert "ERROR" in err
|
2016-02-19 03:58:39 -08:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_tesseract_crash_autorotate(spoof_tesseract_crash,
|
|
|
|
resources, no_outpdf):
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'ccitt.pdf', no_outpdf,
|
2016-02-19 03:58:39 -08:00
|
|
|
'-r', env=spoof_tesseract_crash)
|
2016-08-26 15:18:38 -07:00
|
|
|
assert p.returncode == ExitCode.child_process_error
|
2017-01-26 16:38:59 -08:00
|
|
|
assert not os.path.exists(no_outpdf)
|
2016-02-19 03:58:39 -08:00
|
|
|
assert "ERROR" in err
|
2016-04-28 00:39:15 -07:00
|
|
|
print(out)
|
|
|
|
print(err)
|
2016-02-20 04:53:02 -08:00
|
|
|
|
|
|
|
|
2017-06-13 13:09:12 -07:00
|
|
|
@pytest.mark.parametrize('renderer', RENDERERS)
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_tesseract_image_too_big(renderer, spoof_tesseract_big_image_error,
|
|
|
|
resources, outpdf):
|
2016-02-20 04:53:02 -08:00
|
|
|
check_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'hugemono.pdf', outpdf, '-r',
|
2018-01-10 16:55:18 -08:00
|
|
|
'--pdf-renderer', renderer,
|
|
|
|
'--max-image-mpixels', '0',
|
|
|
|
env=spoof_tesseract_big_image_error)
|
2016-03-10 15:37:09 -08:00
|
|
|
|
|
|
|
|
2018-03-24 15:07:02 -07:00
|
|
|
@pytest.mark.skipif(True, reason="need new implementation")
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_no_unpaper(resources, no_outpdf):
|
2016-03-10 15:37:09 -08:00
|
|
|
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(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'c02-22.pdf', no_outpdf, '--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
|
|
|
|
|
|
|
|
2018-03-24 15:07:02 -07:00
|
|
|
def test_old_unpaper(spoof_unpaper_oldversion, resources, no_outpdf):
|
2016-08-26 15:18:38 -07:00
|
|
|
p, out, err = run_ocrmypdf(
|
2018-03-24 15:07:02 -07:00
|
|
|
resources / 'c02-22.pdf', no_outpdf, '--clean',
|
|
|
|
env=spoof_unpaper_oldversion)
|
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
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_algo4(resources, no_outpdf):
|
|
|
|
p, _, _ = run_ocrmypdf(resources / 'encrypted_algo4.pdf', no_outpdf)
|
2016-08-26 15:18:38 -07:00
|
|
|
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
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_non_square_resolution(renderer, spoof_tesseract_cache,
|
|
|
|
resources, outpdf):
|
2016-07-28 16:20:59 -07:00
|
|
|
# Confirm input image is non-square resolution
|
2017-05-18 16:43:50 -07:00
|
|
|
in_pageinfo = PdfInfo(resources / 'aspect.pdf')
|
2017-05-18 17:12:04 -07:00
|
|
|
assert in_pageinfo[0].xres != in_pageinfo[0].yres
|
2016-07-28 16:20:59 -07:00
|
|
|
|
|
|
|
check_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'aspect.pdf', outpdf,
|
2016-07-29 01:34:52 -07:00
|
|
|
'--pdf-renderer', renderer, env=spoof_tesseract_cache)
|
2016-07-28 16:20:59 -07:00
|
|
|
|
2017-05-18 16:43:50 -07:00
|
|
|
out_pageinfo = PdfInfo(outpdf)
|
2016-07-28 16:20:59 -07:00
|
|
|
|
|
|
|
# Confirm resolution was kept the same
|
2017-05-18 17:12:04 -07:00
|
|
|
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
|
|
|
|
|
|
|
|
2017-06-13 13:09:12 -07:00
|
|
|
@pytest.mark.parametrize('renderer', RENDERERS)
|
2017-02-26 17:13:16 -08:00
|
|
|
def test_convert_to_square_resolution(renderer, spoof_tesseract_cache,
|
|
|
|
resources, outpdf):
|
|
|
|
from math import isclose
|
|
|
|
|
|
|
|
# Confirm input image is non-square resolution
|
2017-05-18 16:43:50 -07:00
|
|
|
in_pageinfo = PdfInfo(resources / 'aspect.pdf')
|
2017-05-18 17:12:04 -07:00
|
|
|
assert in_pageinfo[0].xres != in_pageinfo[0].yres
|
2017-02-26 17:13:16 -08:00
|
|
|
|
|
|
|
# --force-ocr requires means forced conversion to square resolution
|
|
|
|
check_ocrmypdf(
|
|
|
|
resources / 'aspect.pdf', outpdf,
|
|
|
|
'--force-ocr',
|
|
|
|
'--pdf-renderer', renderer, env=spoof_tesseract_cache)
|
|
|
|
|
2017-05-18 16:43:50 -07:00
|
|
|
out_pageinfo = PdfInfo(outpdf)
|
2017-02-26 17:13:16 -08:00
|
|
|
|
|
|
|
in_p0, out_p0 = in_pageinfo[0], out_pageinfo[0]
|
|
|
|
|
|
|
|
# Resolution show now be equal
|
2017-05-18 17:12:04 -07:00
|
|
|
assert out_p0.xres == out_p0.yres
|
2017-02-26 17:13:16 -08:00
|
|
|
|
|
|
|
# Page size should match input page size
|
2017-05-18 17:12:04 -07:00
|
|
|
assert isclose(in_p0.width_inches,
|
|
|
|
out_p0.width_inches)
|
|
|
|
assert isclose(in_p0.height_inches,
|
|
|
|
out_p0.height_inches)
|
2017-02-26 17:13:16 -08:00
|
|
|
|
|
|
|
# Because we rasterized the page to produce a new image, it should occupy
|
|
|
|
# the entire page
|
2017-05-18 17:12:04 -07:00
|
|
|
out_im_w = out_p0.images[0]['width'] / out_p0.images[0]['dpi_w']
|
|
|
|
out_im_h = out_p0.images[0]['height'] / out_p0.images[0]['dpi_h']
|
|
|
|
assert isclose(out_p0.width_inches, out_im_w)
|
|
|
|
assert isclose(out_p0.height_inches, out_im_h)
|
2017-02-26 17:13:16 -08:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_image_to_pdf(spoof_tesseract_noop, resources, outpdf):
|
2016-08-03 03:35:30 -07:00
|
|
|
check_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'LinnSequencer.jpg', outpdf, '--image-dpi', '200',
|
2016-08-03 03:35:30 -07:00
|
|
|
env=spoof_tesseract_noop)
|
2016-08-03 11:35:48 -07:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_jbig2_passthrough(spoof_tesseract_cache, resources, outpdf):
|
2016-08-03 11:35:48 -07:00
|
|
|
out = check_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'jbig2.pdf', outpdf,
|
2016-08-03 11:35:48 -07:00
|
|
|
'--output-type', 'pdf',
|
|
|
|
'--pdf-renderer', 'hocr',
|
|
|
|
env=spoof_tesseract_cache)
|
|
|
|
|
2017-05-18 16:43:50 -07:00
|
|
|
out_pageinfo = PdfInfo(out)
|
2017-05-18 22:32:27 -07:00
|
|
|
assert out_pageinfo[0].images[0].enc == Encoding.jbig2
|
2016-08-03 11:35:48 -07:00
|
|
|
|
2016-08-25 14:46:54 -07:00
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_stdin(spoof_tesseract_noop, ocrmypdf_exec, resources, outpdf):
|
|
|
|
input_file = str(resources / 'francais.pdf')
|
|
|
|
output_file = str(outpdf)
|
2016-08-25 14:46:54 -07:00
|
|
|
|
2016-10-27 16:01:07 -07:00
|
|
|
# Runs: ocrmypdf - output.pdf < testfile.pdf
|
|
|
|
with open(input_file, 'rb') as input_stream:
|
2017-01-26 16:38:59 -08:00
|
|
|
p_args = ocrmypdf_exec + ['-', output_file]
|
2016-10-27 16:01:07 -07:00
|
|
|
p = Popen(
|
|
|
|
p_args, close_fds=True, stdout=PIPE, stderr=PIPE,
|
|
|
|
stdin=input_stream, env=spoof_tesseract_noop)
|
|
|
|
out, err = p.communicate()
|
|
|
|
|
|
|
|
assert p.returncode == ExitCode.ok
|
2016-08-25 14:46:54 -07:00
|
|
|
|
2016-08-26 15:03:27 -07:00
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_stdout(spoof_tesseract_noop, ocrmypdf_exec, resources, outpdf):
|
|
|
|
input_file = str(resources / 'francais.pdf')
|
|
|
|
output_file = str(outpdf)
|
2016-10-27 16:14:42 -07:00
|
|
|
|
|
|
|
# Runs: ocrmypdf francais.pdf - > test_stdout.pdf
|
|
|
|
with open(output_file, 'wb') as output_stream:
|
2017-01-26 16:38:59 -08:00
|
|
|
p_args = ocrmypdf_exec + [input_file, '-']
|
2016-10-27 16:14:42 -07:00
|
|
|
p = Popen(
|
|
|
|
p_args, close_fds=True, stdout=output_stream, stderr=PIPE,
|
|
|
|
stdin=DEVNULL, env=spoof_tesseract_noop)
|
|
|
|
out, err = p.communicate()
|
|
|
|
|
|
|
|
assert p.returncode == ExitCode.ok
|
|
|
|
|
2016-12-10 15:34:00 -08:00
|
|
|
from ocrmypdf.exec import qpdf
|
2016-10-27 16:14:42 -07:00
|
|
|
assert qpdf.check(output_file, log=None)
|
|
|
|
|
|
|
|
|
2017-03-13 15:52:57 -07:00
|
|
|
def test_closed_streams(spoof_tesseract_noop, ocrmypdf_exec, resources, outpdf):
|
|
|
|
input_file = str(resources / 'francais.pdf')
|
|
|
|
output_file = str(outpdf)
|
|
|
|
|
|
|
|
def evil_closer():
|
|
|
|
os.close(0)
|
|
|
|
os.close(1)
|
|
|
|
|
|
|
|
p_args = ocrmypdf_exec + [input_file, output_file]
|
|
|
|
p = Popen(
|
|
|
|
p_args, close_fds=True, stdout=None, stderr=PIPE, stdin=None,
|
|
|
|
env=spoof_tesseract_noop, preexec_fn=evil_closer)
|
|
|
|
out, err = p.communicate()
|
|
|
|
print(err.decode())
|
|
|
|
assert p.returncode == ExitCode.ok
|
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_masks(spoof_tesseract_noop, resources, outpdf):
|
|
|
|
check_ocrmypdf(resources / 'masks.pdf', outpdf, env=spoof_tesseract_noop)
|
2016-08-26 15:03:27 -07:00
|
|
|
|
2016-08-31 11:42:21 -07:00
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_linearized_pdf_and_indirect_object(spoof_tesseract_noop,
|
|
|
|
resources, outpdf):
|
2016-10-06 13:10:28 -07:00
|
|
|
check_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'epson.pdf', outpdf,
|
2016-10-06 13:10:28 -07:00
|
|
|
env=spoof_tesseract_noop)
|
2016-11-07 14:15:03 -08:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_rotated_skew_timeout(resources, outpdf):
|
2016-11-07 14:15:03 -08:00
|
|
|
"""This document contains an image that is rotated 90 into place with a
|
|
|
|
/Rotate tag and intentionally skewed by altering the transformation matrix.
|
|
|
|
|
|
|
|
This tests for a bug where the combinatino of preprocessing and a tesseract
|
|
|
|
timeout produced a page whose dimensions did not match the original's.
|
|
|
|
"""
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
input_file = str(resources / 'rotated_skew.pdf')
|
2017-05-18 16:30:43 -07:00
|
|
|
in_pageinfo = PdfInfo(input_file)[0]
|
2016-11-07 14:15:03 -08:00
|
|
|
|
2017-05-18 17:12:04 -07:00
|
|
|
assert in_pageinfo.height_pixels < in_pageinfo.width_pixels, \
|
2016-11-07 14:15:03 -08:00
|
|
|
"Expected the input page to be landscape"
|
2017-05-18 17:12:04 -07:00
|
|
|
assert in_pageinfo.rotation == 90, "Expected a rotated page"
|
2016-11-07 14:15:03 -08:00
|
|
|
|
|
|
|
out = check_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
input_file, outpdf,
|
2016-11-07 14:17:31 -08:00
|
|
|
'--pdf-renderer', 'hocr',
|
2016-11-07 14:15:03 -08:00
|
|
|
'--deskew', '--tesseract-timeout', '0')
|
|
|
|
|
2017-05-18 16:43:50 -07:00
|
|
|
out_pageinfo = PdfInfo(out)[0]
|
2016-11-07 14:15:03 -08:00
|
|
|
|
2017-05-18 17:12:04 -07:00
|
|
|
assert out_pageinfo.height_pixels > out_pageinfo.width_pixels, \
|
2016-11-07 14:15:03 -08:00
|
|
|
"Expected the output page to be portrait"
|
|
|
|
|
2017-05-18 17:12:04 -07:00
|
|
|
assert out_pageinfo.rotation == 0, \
|
2016-11-07 14:15:03 -08:00
|
|
|
"Expected no page rotation for output"
|
|
|
|
|
2017-05-18 17:12:04 -07:00
|
|
|
assert in_pageinfo.width_pixels == out_pageinfo.height_pixels and \
|
|
|
|
in_pageinfo.height_pixels == out_pageinfo.width_pixels, \
|
2016-11-07 14:15:03 -08:00
|
|
|
"Expected page rotation to be baked in"
|
|
|
|
|
2016-12-03 00:31:40 -08:00
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_ghostscript_pdfa_failure(spoof_no_tess_no_pdfa, resources, outpdf):
|
2016-12-03 00:31:40 -08:00
|
|
|
p, out, err = run_ocrmypdf(
|
2017-01-26 16:38:59 -08:00
|
|
|
resources / 'ccitt.pdf', outpdf,
|
2016-12-03 00:31:40 -08:00
|
|
|
env=spoof_no_tess_no_pdfa)
|
|
|
|
assert p.returncode == 4, "Expected return code 4 when PDF/A fails"
|
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_ghostscript_feature_elision(spoof_no_tess_pdfa_warning,
|
|
|
|
resources, outpdf):
|
|
|
|
check_ocrmypdf(resources / 'ccitt.pdf', outpdf,
|
2016-12-03 00:31:40 -08:00
|
|
|
env=spoof_no_tess_pdfa_warning)
|
2016-12-08 16:04:14 -08:00
|
|
|
|
|
|
|
|
2017-01-26 16:38:59 -08:00
|
|
|
def test_very_high_dpi(spoof_tesseract_cache, resources, outpdf):
|
2016-12-08 16:04:14 -08:00
|
|
|
"Checks for a Decimal quantize error with high DPI, etc"
|
2017-01-26 16:38:59 -08:00
|
|
|
check_ocrmypdf(resources / '2400dpi.pdf', outpdf,
|
2016-12-08 16:04:14 -08:00
|
|
|
env=spoof_tesseract_cache)
|
2017-05-18 16:30:43 -07:00
|
|
|
pdfinfo = PdfInfo(outpdf)
|
2017-05-06 22:34:01 -07:00
|
|
|
|
2017-05-18 17:12:04 -07:00
|
|
|
image = pdfinfo[0].images[0]
|
2017-05-18 18:39:14 -07:00
|
|
|
assert isclose(image.xres, image.yres)
|
|
|
|
assert isclose(image.xres, 2400)
|
2017-01-26 17:24:40 -08:00
|
|
|
|
|
|
|
|
|
|
|
def test_overlay(spoof_tesseract_noop, resources, outpdf):
|
|
|
|
check_ocrmypdf(resources / 'overlay.pdf', outpdf,
|
2018-03-24 02:24:45 -07:00
|
|
|
'--skip-text',
|
2017-01-26 17:24:40 -08:00
|
|
|
env=spoof_tesseract_noop)
|
2017-01-26 22:08:24 -08:00
|
|
|
|
|
|
|
|
2017-01-29 18:26:52 -08:00
|
|
|
@pytest.mark.skipif(
|
|
|
|
os.getuid() == 0 or os.geteuid() == 0,
|
|
|
|
reason="root can write to anything"
|
|
|
|
)
|
2017-01-26 22:08:24 -08:00
|
|
|
def test_destination_not_writable(spoof_tesseract_noop, resources, outdir):
|
|
|
|
protected_file = outdir / 'protected.pdf'
|
|
|
|
protected_file.touch()
|
|
|
|
protected_file.chmod(0o400) # Read-only
|
|
|
|
p, out, err = run_ocrmypdf(
|
|
|
|
resources / 'jbig2.pdf', protected_file,
|
|
|
|
env=spoof_tesseract_noop)
|
|
|
|
assert p.returncode == ExitCode.file_access_error, "Expected error"
|
2017-01-28 22:06:51 -08:00
|
|
|
|
|
|
|
|
|
|
|
def test_tesseract_config_valid(resources, outdir):
|
|
|
|
cfg_file = outdir / 'test.cfg'
|
|
|
|
with cfg_file.open('w') as f:
|
|
|
|
f.write('''\
|
|
|
|
load_system_dawg 0
|
|
|
|
language_model_penalty_non_dict_word 0
|
|
|
|
language_model_penalty_non_freq_dict_word 0
|
|
|
|
''')
|
|
|
|
|
|
|
|
check_ocrmypdf(
|
|
|
|
resources / 'ccitt.pdf', outdir / 'out.pdf',
|
2017-07-21 16:39:22 -07:00
|
|
|
'--tesseract-config', cfg_file)
|
2017-01-28 22:06:51 -08:00
|
|
|
|
|
|
|
|
2017-06-13 13:09:12 -07:00
|
|
|
@pytest.mark.parametrize('renderer', RENDERERS)
|
2017-01-28 22:06:51 -08:00
|
|
|
def test_tesseract_config_notfound(renderer, resources, outdir):
|
|
|
|
cfg_file = outdir / 'nofile.cfg'
|
|
|
|
|
|
|
|
p, out, err = run_ocrmypdf(
|
|
|
|
resources / 'ccitt.pdf', outdir / 'out.pdf',
|
|
|
|
'--pdf-renderer', renderer,
|
2017-07-21 16:39:22 -07:00
|
|
|
'--tesseract-config', cfg_file)
|
2017-01-28 22:06:51 -08:00
|
|
|
assert "Can't open" in err, "No error message about missing config file"
|
|
|
|
assert p.returncode == ExitCode.ok
|
|
|
|
|
|
|
|
|
2017-06-13 13:09:12 -07:00
|
|
|
@pytest.mark.parametrize('renderer', RENDERERS)
|
2017-01-28 22:06:51 -08:00
|
|
|
def test_tesseract_config_invalid(renderer, resources, outdir):
|
|
|
|
cfg_file = outdir / 'test.cfg'
|
|
|
|
with cfg_file.open('w') as f:
|
|
|
|
f.write('''\
|
|
|
|
THIS FILE IS INVALID
|
|
|
|
''')
|
|
|
|
|
|
|
|
p, out, err = run_ocrmypdf(
|
|
|
|
resources / 'ccitt.pdf', outdir / 'out.pdf',
|
|
|
|
'--pdf-renderer', renderer,
|
2017-07-21 16:39:22 -07:00
|
|
|
'--tesseract-config', cfg_file)
|
2017-01-28 22:06:51 -08:00
|
|
|
assert "parameter not found" in err, "No error message"
|
|
|
|
assert p.returncode == ExitCode.invalid_config
|
2017-02-14 12:51:15 -08:00
|
|
|
|
|
|
|
|
2017-07-21 16:40:20 -07:00
|
|
|
def test_user_words(resources, outdir):
|
|
|
|
word_list = outdir / 'wordlist.txt'
|
|
|
|
sidecar_before = outdir / 'sidecar_before.txt'
|
|
|
|
sidecar_after = outdir / 'sidecar_after.txt'
|
|
|
|
|
2017-07-26 21:03:51 -07:00
|
|
|
# Don't know how to make this test pass on various versions and platforms
|
|
|
|
# so weaken to merely testing that the argument is accepted
|
|
|
|
consistent = False
|
|
|
|
|
|
|
|
if consistent:
|
|
|
|
check_ocrmypdf(
|
|
|
|
resources / 'crom.png', outdir / 'out.pdf',
|
|
|
|
'--image-dpi', 150,
|
|
|
|
'--sidecar', sidecar_before
|
|
|
|
)
|
|
|
|
assert 'cromulent' not in sidecar_before.open().read()
|
2017-07-21 16:40:20 -07:00
|
|
|
|
|
|
|
with word_list.open('w') as f:
|
|
|
|
f.write('cromulent\n') # a perfectly cromulent word
|
|
|
|
|
|
|
|
check_ocrmypdf(
|
|
|
|
resources / 'crom.png', outdir / 'out.pdf',
|
|
|
|
'--image-dpi', 150,
|
|
|
|
'--sidecar', sidecar_after,
|
|
|
|
'--user-words', word_list
|
|
|
|
)
|
|
|
|
|
2017-07-26 21:03:51 -07:00
|
|
|
if consistent:
|
|
|
|
assert 'cromulent' in sidecar_after.open().read()
|
2017-07-21 16:40:20 -07:00
|
|
|
|
|
|
|
|
2017-02-14 12:51:15 -08:00
|
|
|
def test_form_xobject(spoof_tesseract_noop, resources, outpdf):
|
|
|
|
check_ocrmypdf(resources / 'formxobject.pdf', outpdf,
|
|
|
|
'--force-ocr',
|
|
|
|
env=spoof_tesseract_noop)
|
2017-03-24 13:23:03 -07:00
|
|
|
|
|
|
|
|
2017-06-13 13:09:12 -07:00
|
|
|
@pytest.mark.parametrize('renderer', RENDERERS)
|
2017-03-24 13:23:03 -07:00
|
|
|
def test_pagesize_consistency(renderer, resources, outpdf):
|
|
|
|
from math import isclose
|
|
|
|
|
|
|
|
first_page_dimensions = pytest.helpers.first_page_dimensions
|
|
|
|
|
|
|
|
infile = resources / 'linn.pdf'
|
|
|
|
|
|
|
|
before_dims = first_page_dimensions(infile)
|
|
|
|
|
|
|
|
check_ocrmypdf(
|
|
|
|
infile,
|
|
|
|
outpdf, '--pdf-renderer', renderer,
|
|
|
|
'--clean', '--deskew', '--remove-background', '--clean-final')
|
|
|
|
|
|
|
|
after_dims = first_page_dimensions(outpdf)
|
|
|
|
|
|
|
|
assert isclose(before_dims[0], after_dims[0])
|
|
|
|
assert isclose(before_dims[1], after_dims[1])
|
2017-04-18 15:20:25 -07:00
|
|
|
|
|
|
|
|
|
|
|
def test_skip_big_with_no_images(spoof_tesseract_noop, resources, outpdf):
|
|
|
|
check_ocrmypdf(resources / 'blank.pdf', outpdf,
|
|
|
|
'--skip-big', '5',
|
|
|
|
'--force-ocr',
|
2017-05-01 15:44:21 -07:00
|
|
|
env=spoof_tesseract_noop)
|
|
|
|
|
|
|
|
|
|
|
|
def test_gs_render_failure(spoof_no_tess_gs_render_fail, resources, outpdf):
|
|
|
|
p, out, err = run_ocrmypdf(
|
|
|
|
resources / 'blank.pdf', outpdf,
|
|
|
|
env=spoof_no_tess_gs_render_fail)
|
|
|
|
print(err)
|
|
|
|
assert p.returncode == ExitCode.child_process_error
|
|
|
|
|
|
|
|
|
|
|
|
def test_gs_raster_failure(spoof_no_tess_gs_raster_fail, resources, outpdf):
|
|
|
|
p, out, err = run_ocrmypdf(
|
|
|
|
resources / 'ccitt.pdf', outpdf,
|
|
|
|
env=spoof_no_tess_gs_raster_fail)
|
|
|
|
print(err)
|
|
|
|
assert p.returncode == ExitCode.child_process_error
|
2017-05-01 15:46:15 -07:00
|
|
|
|
|
|
|
|
2018-03-06 04:58:22 -05:00
|
|
|
@pytest.mark.skipif('8.0.0' <= qpdf.version() <= '8.0.1',
|
|
|
|
reason="qpdf regression")
|
2017-05-01 15:46:15 -07:00
|
|
|
def test_no_contents(spoof_tesseract_noop, resources, outpdf):
|
|
|
|
check_ocrmypdf(resources / 'no_contents.pdf', outpdf, '--force-ocr',
|
2017-05-06 22:27:25 -07:00
|
|
|
env=spoof_tesseract_noop)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('image', [
|
|
|
|
'baiona.png',
|
|
|
|
'baiona_gray.png',
|
2017-05-09 16:37:29 -07:00
|
|
|
'congress.jpg'
|
2017-05-06 22:27:25 -07:00
|
|
|
])
|
2017-05-09 16:37:29 -07:00
|
|
|
def test_compression_preserved(spoof_tesseract_noop, ocrmypdf_exec,
|
|
|
|
resources, image, outpdf):
|
2017-05-06 22:27:25 -07:00
|
|
|
from PIL import Image
|
|
|
|
|
|
|
|
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:
|
2017-05-09 16:37:29 -07:00
|
|
|
p_args = ocrmypdf_exec + [
|
|
|
|
'--image-dpi', '150', '--output-type', 'pdf', '-', output_file]
|
2017-05-06 22:27:25 -07:00
|
|
|
p = Popen(
|
|
|
|
p_args, close_fds=True, stdout=PIPE, stderr=PIPE,
|
|
|
|
stdin=input_stream, env=spoof_tesseract_noop)
|
|
|
|
out, err = p.communicate()
|
|
|
|
|
|
|
|
assert p.returncode == ExitCode.ok
|
|
|
|
|
2017-05-18 16:30:43 -07:00
|
|
|
pdfinfo = PdfInfo(output_file)
|
2017-05-09 16:37:29 -07:00
|
|
|
|
2017-05-18 17:12:04 -07:00
|
|
|
pdfimage = pdfinfo[0].images[0]
|
2017-05-09 16:37:29 -07:00
|
|
|
|
|
|
|
if input_file.endswith('.png'):
|
2017-05-18 22:32:27 -07:00
|
|
|
assert pdfimage.enc != Encoding.jpeg, \
|
2017-05-09 16:37:29 -07:00
|
|
|
"Lossless compression changed to lossy!"
|
|
|
|
elif input_file.endswith('.jpg'):
|
2017-05-18 22:32:27 -07:00
|
|
|
assert pdfimage.enc == Encoding.jpeg, \
|
2017-05-09 16:37:29 -07:00
|
|
|
"Lossy compression changed to lossless!"
|
|
|
|
if im.mode.startswith('RGB') or im.mode.startswith('BGR'):
|
2017-05-18 22:32:27 -07:00
|
|
|
assert pdfimage.color == Colorspace.rgb, \
|
2017-05-09 16:37:29 -07:00
|
|
|
"Colorspace changed"
|
|
|
|
elif im.mode.startswith('L'):
|
2017-05-18 22:32:27 -07:00
|
|
|
assert pdfimage.color == Colorspace.gray, \
|
2017-05-09 16:37:29 -07:00
|
|
|
"Colorspace changed"
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('image,compression', [
|
|
|
|
('baiona.png', 'jpeg'),
|
|
|
|
('baiona_gray.png', 'lossless'),
|
|
|
|
('congress.jpg', 'lossless')
|
|
|
|
])
|
|
|
|
def test_compression_changed(spoof_tesseract_noop, ocrmypdf_exec,
|
|
|
|
resources, image, compression, outpdf):
|
|
|
|
from PIL import Image
|
|
|
|
|
|
|
|
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 + [
|
|
|
|
'--image-dpi', '150', '--output-type', 'pdfa',
|
|
|
|
'--pdfa-image-compression', compression,
|
|
|
|
'-', output_file]
|
|
|
|
p = Popen(
|
|
|
|
p_args, close_fds=True, stdout=PIPE, stderr=PIPE,
|
|
|
|
stdin=input_stream, env=spoof_tesseract_noop)
|
|
|
|
out, err = p.communicate()
|
|
|
|
|
|
|
|
assert p.returncode == ExitCode.ok
|
|
|
|
|
2017-05-18 16:30:43 -07:00
|
|
|
pdfinfo = PdfInfo(output_file)
|
2017-05-09 16:37:29 -07:00
|
|
|
|
2017-05-18 17:12:04 -07:00
|
|
|
pdfimage = pdfinfo[0].images[0]
|
2017-05-09 16:37:29 -07:00
|
|
|
|
2017-05-18 22:32:27 -07:00
|
|
|
if compression == "jpeg":
|
|
|
|
assert pdfimage.enc == Encoding.jpeg
|
2017-05-09 16:37:29 -07:00
|
|
|
elif compression == 'lossless':
|
2017-05-18 22:32:27 -07:00
|
|
|
assert pdfimage.enc not in (Encoding.jpeg, Encoding.jpeg2000)
|
2017-05-09 16:37:29 -07:00
|
|
|
|
2017-05-06 22:27:25 -07:00
|
|
|
if im.mode.startswith('RGB') or im.mode.startswith('BGR'):
|
2017-05-18 22:32:27 -07:00
|
|
|
assert pdfimage.color == Colorspace.rgb, \
|
2017-05-06 22:27:25 -07:00
|
|
|
"Colorspace changed"
|
|
|
|
elif im.mode.startswith('L'):
|
2017-05-18 22:32:27 -07:00
|
|
|
assert pdfimage.color == Colorspace.gray, \
|
2017-05-11 00:43:36 -07:00
|
|
|
"Colorspace changed"
|
|
|
|
|
|
|
|
|
|
|
|
def test_sidecar_pagecount(spoof_tesseract_cache, resources, outpdf):
|
|
|
|
sidecar = outpdf + '.txt'
|
|
|
|
check_ocrmypdf(
|
|
|
|
resources / 'multipage.pdf', outpdf,
|
|
|
|
'--skip-text',
|
|
|
|
'--sidecar', sidecar,
|
|
|
|
env=spoof_tesseract_cache)
|
|
|
|
|
2017-05-18 16:43:50 -07:00
|
|
|
pdfinfo = PdfInfo(resources / 'multipage.pdf')
|
2017-05-11 00:43:36 -07:00
|
|
|
num_pages = len(pdfinfo)
|
|
|
|
|
|
|
|
with open(sidecar, 'r') 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
|
|
|
|
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
|
|
|
|
|
|
|
|
2017-10-26 18:15:31 -07:00
|
|
|
def test_sidecar_nonempty(spoof_tesseract_cache, resources, outpdf):
|
|
|
|
sidecar = outpdf + '.txt'
|
|
|
|
check_ocrmypdf(
|
|
|
|
resources / 'ccitt.pdf', outpdf,
|
|
|
|
'--sidecar', sidecar,
|
|
|
|
env=spoof_tesseract_cache
|
|
|
|
)
|
|
|
|
|
|
|
|
with open(sidecar, 'r') as f:
|
|
|
|
ocr_text = f.read()
|
|
|
|
assert 'the' in ocr_text
|
|
|
|
|
|
|
|
|
2017-10-11 14:32:58 -07:00
|
|
|
def test_pdfa_1(spoof_tesseract_cache, resources, outpdf):
|
|
|
|
check_ocrmypdf(
|
|
|
|
resources / 'ccitt.pdf', outpdf,
|
|
|
|
'--output-type', 'pdfa-1',
|
|
|
|
env=spoof_tesseract_cache
|
|
|
|
)
|
|
|
|
|
|
|
|
pdfa_info = file_claims_pdfa(outpdf)
|
2017-11-16 17:18:02 -08:00
|
|
|
assert pdfa_info['conformance'] == 'PDF/A-1B'
|
|
|
|
|
|
|
|
|
|
|
|
def test_bad_locale():
|
|
|
|
env = os.environ.copy()
|
2017-11-16 20:37:30 -08:00
|
|
|
env['LC_ALL'] = 'C'
|
2017-11-16 17:18:02 -08:00
|
|
|
|
|
|
|
p, out, err = run_ocrmypdf(
|
|
|
|
'a', 'b', env=env
|
|
|
|
)
|
|
|
|
assert out == '', "stdout not clean"
|
|
|
|
assert p.returncode != 0
|
2017-11-26 22:52:53 -08:00
|
|
|
assert 'configured to use ASCII as encoding' in err, "should whine"
|
|
|
|
|
2017-11-29 14:08:07 -08:00
|
|
|
|
|
|
|
@pytest.mark.parametrize('renderer', RENDERERS)
|
|
|
|
def test_bad_utf8(spoof_tess_bad_utf8, renderer, resources, no_outpdf):
|
|
|
|
p, out, err = run_ocrmypdf(
|
|
|
|
resources / 'ccitt.pdf', no_outpdf,
|
|
|
|
'--pdf-renderer', renderer,
|
|
|
|
env=spoof_tess_bad_utf8
|
|
|
|
)
|
|
|
|
|
|
|
|
assert out == '', "stdout not clean"
|
|
|
|
assert p.returncode != 0
|
|
|
|
assert 'not utf-8' in err, "should whine about utf-8"
|
|
|
|
assert '\\x96' in err, 'should repeat backslash encoded output'
|
2018-01-09 00:17:53 -08:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
|
|
not tesseract.has_textonly_pdf(),
|
|
|
|
reason="issue only affects sandwich")
|
|
|
|
def test_rotate_deskew_timeout(resources, outdir):
|
|
|
|
check_ocrmypdf(
|
|
|
|
resources / 'rotated_skew.pdf',
|
|
|
|
outdir / 'deskewed.pdf',
|
|
|
|
'--deskew',
|
|
|
|
'--tesseract-timeout', '0',
|
|
|
|
'--pdf-renderer', 'sandwich'
|
|
|
|
)
|
|
|
|
|
|
|
|
correlation = check_monochrome_correlation(
|
|
|
|
outdir,
|
|
|
|
reference_pdf=resources / 'ccitt.pdf',
|
|
|
|
reference_pageno=1,
|
|
|
|
test_pdf=outdir / 'deskewed.pdf',
|
|
|
|
test_pageno=1)
|
|
|
|
|
|
|
|
# Confirm that the page still got deskewed
|
2018-01-10 15:43:59 -08:00
|
|
|
assert correlation > 0.50
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
|
|
PIL.PILLOW_VERSION < '5.0.0',
|
|
|
|
reason="Pillow < 5.0.0 doesn't raise the exception")
|
|
|
|
def test_decompression_bomb(resources, outpdf):
|
|
|
|
p, out, err = run_ocrmypdf(
|
|
|
|
resources / 'hugemono.pdf',
|
|
|
|
outpdf
|
|
|
|
)
|
|
|
|
assert 'decompression bomb' in err
|
|
|
|
|
|
|
|
p, out, err = run_ocrmypdf(
|
|
|
|
resources / 'hugemono.pdf',
|
|
|
|
outpdf,
|
|
|
|
'--max-image-mpixels', '2000'
|
|
|
|
)
|
2018-02-08 00:15:12 -08:00
|
|
|
assert p.returncode == 0
|
|
|
|
|
|
|
|
|
|
|
|
def test_text_curves(spoof_tesseract_noop, resources, outpdf):
|
|
|
|
check_ocrmypdf(
|
|
|
|
resources / 'vector.pdf', outpdf, env=spoof_tesseract_noop)
|
|
|
|
|
|
|
|
info = PdfInfo(outpdf)
|
|
|
|
assert len(info.pages[0].images) == 0, "added images to the vector PDF"
|
|
|
|
|
|
|
|
check_ocrmypdf(
|
|
|
|
resources / 'vector.pdf', outpdf, '--force-ocr',
|
|
|
|
env=spoof_tesseract_noop)
|
|
|
|
|
|
|
|
info = PdfInfo(outpdf)
|
|
|
|
assert len(info.pages[0].images) != 0, "force did not rasterize"
|
|
|
|
|
2018-02-19 22:15:07 -08:00
|
|
|
|
|
|
|
def test_dev_null(spoof_tesseract_noop, resources):
|
|
|
|
p, out, err = run_ocrmypdf(
|
|
|
|
resources / 'trivial.pdf',
|
|
|
|
os.devnull,
|
|
|
|
'--force-ocr',
|
|
|
|
env=spoof_tesseract_noop
|
|
|
|
)
|
|
|
|
assert p.returncode == 0, "could not send output to /dev/null"
|
|
|
|
assert len(out) == 0, "wrote to stdout"
|
|
|
|
|
|
|
|
|
|
|
|
def test_output_is_dir(spoof_tesseract_noop, resources, outdir):
|
|
|
|
p, out, err = run_ocrmypdf(
|
|
|
|
resources / 'trivial.pdf',
|
|
|
|
outdir,
|
|
|
|
'--force-ocr',
|
|
|
|
env=spoof_tesseract_noop
|
|
|
|
)
|
|
|
|
assert p.returncode == ExitCode.file_access_error
|
|
|
|
assert 'is not a writable file' in err
|
|
|
|
|
|
|
|
|
|
|
|
def test_output_is_symlink(spoof_tesseract_noop, resources, outdir):
|
|
|
|
sym = Path(outdir / 'this_is_a_symlink')
|
|
|
|
sym.symlink_to(outdir / 'out.pdf')
|
|
|
|
p, out, err = run_ocrmypdf(
|
|
|
|
resources / 'trivial.pdf',
|
|
|
|
sym,
|
|
|
|
'--force-ocr',
|
|
|
|
env=spoof_tesseract_noop
|
|
|
|
)
|
2018-03-02 16:57:46 -08:00
|
|
|
assert p.returncode == ExitCode.ok, err
|
2018-02-19 22:15:07 -08:00
|
|
|
assert (outdir / 'out.pdf').stat().st_size > 0, 'target file not created'
|
|
|
|
|