Convert all tesseract cache usages to plugin

This commit is contained in:
James R. Barlow
2020-06-05 17:55:18 -07:00
parent 6268e2faff
commit a9a473f2e5
12 changed files with 118 additions and 261 deletions
+5 -4
View File
@@ -167,7 +167,6 @@ def get_orientation(input_file: Path, engine_mode, timeout: float, tesseract_env
except TimeoutExpired:
return OrientationConfidence(angle=0, confidence=0.0)
except CalledProcessError as e:
# breakpoint()
tesseract_log_output(e.stdout)
tesseract_log_output(e.stderr)
if (
@@ -191,15 +190,17 @@ def get_orientation(input_file: Path, engine_mode, timeout: float, tesseract_env
return oc
def tesseract_log_output(stdout):
def tesseract_log_output(stream):
tlog = TesseractLoggerAdapter(
log, extra=log.extra if hasattr(log, 'extra') else None
)
if not stream:
return
try:
text = stdout.decode()
text = stream.decode()
except UnicodeDecodeError:
text = stdout.decode('utf-8', 'ignore')
text = stream.decode('utf-8', 'ignore')
lines = text.splitlines()
for line in lines:
-7
View File
@@ -169,13 +169,6 @@ def spoof(tmp_path_factory, **kwargs):
return env
@pytest.fixture
def spoof_tesseract_cache(tmp_path_factory):
if running_in_docker():
return os.environ.copy()
return spoof(tmp_path_factory, tesseract="tesseract_cache.py")
@pytest.fixture
def resources():
return Path(TESTS_ROOT) / 'resources'
+26
View File
@@ -19,6 +19,31 @@
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""Cache output of tesseract to speed up test suite
The cache is keyed by by the input test file The input arguments are slugged
into a hideous filename that more or less represents them literally. Joined
together, this becomes the name of the cache folder. A few name files like
stdout, stderr, hocr, pdf, describe the output to reproduce.
Changes to tests/resources/ or image processing algorithms don't trigger a
cache miss. By design, an input image that varies according to platform
differences (e.g. JPEG decoders are allowed to produce differing outputs,
and in practice they do) will still be a cache hit. By design, an
invocation of tesseract with the same parameters from a different test case
will be a hit. It's fragile.
The tests/cache/manifest.jsonl is a JSON lines file that contains
information about the system that produced the results used when cache was
generated. This mainly a log to answer questions about how the files
were produced.
Certain operations are not cached and routed to Tesseract OCR directly.
Assumes Tesseract 4.0.0-alpha or higher.
"""
import argparse
import json
import logging
@@ -147,6 +172,7 @@ def cached_run(options, run_args, **run_kwargs):
json.dump(manifest, f)
f.write('\n')
f.flush()
return p
class CacheOcrEngine(TesseractOcrEngine):
+1 -1
View File
@@ -19,7 +19,7 @@
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""Tesseract no-op spoof
"""Tesseract no-op plugin
To quickly run tests where getting OCR output is not necessary.
-198
View File
@@ -1,198 +0,0 @@
#!/usr/bin/env python3
# © 2016 James R. Barlow: github.com/jbarlow83
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""Cache output of tesseract to speed up test suite
The cache is keyed by an environment variable that slips the input test file
from tests/resources/ to us. The input arguments are slugged into a hideous
filename that more or less represents them literally. Joined together, this
becomes the name of the cache folder. A few name files like stdout, stderr,
hocr, pdf, describe the output to reproduce.
Changes to tests/resources/ or image processing algorithms don't trigger a
cache miss. By design, an input image that varies according to platform
differences (e.g. JPEG decoders are allowed to produce differing outputs,
and in practice they do) will still be a cache hit. By design, an
invocation of tesseract with the same parameters from a different test case
will be a hit. It's fragile.
The tests/cache/manifest.jsonl is a JSON lines file that contains
information about the system that produced the results used when cache was
generated. This mainly a log to answer questions about how the files
were produced.
For performance reasons, especially the slow performance of Tesseract on
machines with AVX2, the cache is now bundled.
Certain operations are not cached and routed to tesseract directly.
Assumes Tesseract 4.0.0-alpha or higher.
"""
import argparse
import json
import os
import platform
import re
import shutil
import subprocess
import sys
from pathlib import Path
__version__ = subprocess.check_output(
['tesseract', '--version'], stderr=subprocess.STDOUT
).decode()
parser = argparse.ArgumentParser(
prog='tesseract-cache', description='cache output of tesseract'
)
parser.add_argument('-l', '--language', action='append')
parser.add_argument('imagename')
parser.add_argument('outputbase')
parser.add_argument('configfiles', nargs='*')
parser.add_argument('--user-words', type=str)
parser.add_argument('--user-patterns', type=str)
parser.add_argument('-c', action='append')
parser.add_argument('--psm', type=int)
parser.add_argument('--oem', type=int)
TESTS_ROOT = Path(__file__).resolve().parent.parent
CACHE_ROOT = TESTS_ROOT / 'cache'
def real_tesseract():
tess_args = ['tesseract'] + sys.argv[1:]
os.execvp("tesseract", tess_args)
return # Not reachable
def main():
if any(
opt in sys.argv[1:]
for opt in ('--print-parameters', '--list-langs', '--version')
):
real_tesseract() # jump into real tesseract, replacing this process
# Convert non-standard but supported -psm to --psm
sys.argv = ['--psm' if arg == '-psm' else arg for arg in sys.argv]
if '_OCRMYPDF_TEST_INFILE' not in os.environ:
real_tesseract() # test not properly set up
source = os.environ['_OCRMYPDF_TEST_INFILE'] # required
args = parser.parse_args()
cache_disabled = os.environ.get('_OCRMYPDF_CACHE_DISABLED', False)
if args.imagename == 'stdin':
real_tesseract()
def slugs():
yield '' # so we don't start with a '-' which makes rm difficult
for arg in sys.argv[1:]:
if arg == args.imagename:
yield Path(args.imagename).name
elif arg == args.outputbase:
yield Path(args.outputbase).name
elif arg == '-c' or arg.startswith('textonly'):
pass
else:
yield arg
argv_slug = '__'.join(slugs())
argv_slug = argv_slug.replace('/', '___')
cache_folder = Path(CACHE_ROOT) / Path(source).stem / argv_slug
cache_folder.mkdir(parents=True, exist_ok=True)
print(f"Tesseract cache folder {cache_folder} - ", end='', file=sys.stderr)
if (cache_folder / 'stderr.bin').exists() and not cache_disabled:
# Cache hit
print("HIT", file=sys.stderr)
# Replicate stdout/err
sys.stdout.buffer.write((cache_folder / 'stdout.bin').read_bytes())
sys.stderr.buffer.write((cache_folder / 'stderr.bin').read_bytes())
if args.outputbase != 'stdout':
if not args.configfiles:
args.configfiles.append('txt')
for configfile in args.configfiles:
# cp cache -> output
tessfile = args.outputbase + '.' + configfile
shutil.copy(str(cache_folder / configfile) + '.bin', tessfile)
sys.exit(0)
# Cache miss
print("MISS", file=sys.stderr)
# Call tesseract
print(sys.argv[1:])
p = subprocess.run(
['tesseract'] + sys.argv[1:], stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
sys.stdout.buffer.write(p.stdout)
sys.stderr.buffer.write(p.stderr)
if p.returncode != 0:
# Do not cache errors or crashes
print("Tesseract error", file=sys.stderr)
return p.returncode
(cache_folder / 'stdout.bin').write_bytes(p.stdout)
if args.outputbase != 'stdout':
if not args.configfiles:
args.configfiles.append('txt')
for configfile in args.configfiles:
if configfile not in ('hocr', 'pdf', 'txt'):
continue
# cp pwd/{outputbase}.{configfile} -> {cache}/{configfile}
tessfile = args.outputbase + '.' + configfile
shutil.copy(tessfile, str(cache_folder / configfile) + '.bin')
(cache_folder / 'stderr.bin').write_bytes(p.stderr)
manifest = {}
manifest['tesseract_version'] = __version__.replace('\n', ' ')
manifest['platform'] = platform.platform()
manifest['python'] = platform.python_version()
manifest['argv_slug'] = argv_slug
manifest['sourcefile'] = str(Path(source).relative_to(TESTS_ROOT))
def clean_sys_argv():
for arg in sys.argv[1:]:
yield re.sub(r'.*/com.github.ocrmypdf[^/]+[/](.*)', r'$TMPDIR/\1', arg)
manifest['args'] = list(clean_sys_argv())
# pylint: disable=E1101
with (Path(CACHE_ROOT) / 'manifest.jsonl').open('a') as f:
json.dump(manifest, f)
f.write('\n')
f.flush()
if __name__ == '__main__':
main()
+54 -26
View File
@@ -52,7 +52,7 @@ def test_quick(resources, outpdf):
@pytest.mark.parametrize('renderer', RENDERERS)
def test_oversample(spoof_tesseract_cache, renderer, resources, outpdf):
def test_oversample(renderer, resources, outpdf):
oversampled_pdf = check_ocrmypdf(
resources / 'skew.pdf',
outpdf,
@@ -61,7 +61,8 @@ def test_oversample(spoof_tesseract_cache, renderer, resources, outpdf):
'-f',
'--pdf-renderer',
renderer,
env=spoof_tesseract_cache,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
pdfinfo = PdfInfo(oversampled_pdf)
@@ -75,17 +76,25 @@ def test_repeat_ocr(resources, no_outpdf):
assert result == ExitCode.already_done_ocr
def test_force_ocr(spoof_tesseract_cache, resources, outpdf):
def test_force_ocr(resources, outpdf):
out = check_ocrmypdf(
resources / 'graph_ocred.pdf', outpdf, '-f', env=spoof_tesseract_cache
resources / 'graph_ocred.pdf',
outpdf,
'-f',
'--plugin',
'tests/plugins/tesseract_cache.py',
)
pdfinfo = PdfInfo(out)
assert pdfinfo[0].has_text
def test_skip_ocr(spoof_tesseract_cache, resources, outpdf):
def test_skip_ocr(resources, outpdf):
out = check_ocrmypdf(
resources / 'graph_ocred.pdf', outpdf, '-s', env=spoof_tesseract_cache
resources / 'graph_ocred.pdf',
outpdf,
'-s',
'--plugin',
'tests/plugins/tesseract_cache.py',
)
pdfinfo = PdfInfo(out)
assert pdfinfo[0].has_text
@@ -136,9 +145,14 @@ def test_ocr_timeout(renderer, resources, outpdf):
assert not pdfinfo[0].has_text
def test_skip_big(spoof_tesseract_cache, resources, outpdf):
def test_skip_big(resources, outpdf):
out = check_ocrmypdf(
resources / 'jbig2.pdf', outpdf, '--skip-big', '1', env=spoof_tesseract_cache
resources / 'jbig2.pdf',
outpdf,
'--skip-big',
'1',
'--plugin',
'tests/plugins/tesseract_cache.py',
)
pdfinfo = PdfInfo(out)
assert not pdfinfo[0].has_text
@@ -146,9 +160,7 @@ def test_skip_big(spoof_tesseract_cache, resources, outpdf):
@pytest.mark.parametrize('renderer', RENDERERS)
@pytest.mark.parametrize('output_type', ['pdf', 'pdfa'])
def test_maximum_options(
spoof_tesseract_cache, renderer, output_type, resources, outpdf
):
def test_maximum_options(renderer, output_type, resources, outpdf):
check_ocrmypdf(
resources / 'multipage.pdf',
outpdf,
@@ -169,7 +181,8 @@ def test_maximum_options(
renderer,
'--output-type',
output_type,
env=spoof_tesseract_cache,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
@@ -208,7 +221,7 @@ def test_force_ocr_on_pdf_with_no_images(resources, no_outpdf):
pytest.helpers.is_macos() and pytest.helpers.running_in_travis(),
reason="takes too long to install language packs in Travis macOS homebrew",
)
def test_german(spoof_tesseract_cache, resources, outdir):
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.
@@ -221,7 +234,8 @@ def test_german(spoof_tesseract_cache, resources, outdir):
'deu', # more commonly installed
'--sidecar',
sidecar,
env=spoof_tesseract_cache,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
except MissingDependencyError:
if 'deu' not in tesseract.get_languages():
@@ -290,7 +304,7 @@ def test_encrypted(resources, caplog, no_outpdf):
@pytest.mark.parametrize('renderer', RENDERERS)
def test_pagesegmode(renderer, spoof_tesseract_cache, resources, outpdf):
def test_pagesegmode(renderer, resources, outpdf):
check_ocrmypdf(
resources / 'skew.pdf',
outpdf,
@@ -300,7 +314,8 @@ def test_pagesegmode(renderer, spoof_tesseract_cache, resources, outpdf):
'1',
'--pdf-renderer',
renderer,
env=spoof_tesseract_cache,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
@@ -362,7 +377,7 @@ def test_algo4(resources, outpdf):
assert p.returncode == ExitCode.encrypted_pdf
def test_jbig2_passthrough(spoof_tesseract_cache, resources, outpdf):
def test_jbig2_passthrough(resources, outpdf):
out = check_ocrmypdf(
resources / 'jbig2.pdf',
outpdf,
@@ -370,7 +385,8 @@ def test_jbig2_passthrough(spoof_tesseract_cache, resources, outpdf):
'pdf',
'--pdf-renderer',
'hocr',
env=spoof_tesseract_cache,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
out_pageinfo = PdfInfo(out)
assert out_pageinfo[0].images[0].enc == Encoding.jbig2
@@ -391,9 +407,14 @@ def test_linearized_pdf_and_indirect_object(resources, outpdf):
)
def test_very_high_dpi(spoof_tesseract_cache, resources, outpdf):
def test_very_high_dpi(resources, outpdf):
"Checks for a Decimal quantize error with high DPI, etc"
check_ocrmypdf(resources / '2400dpi.pdf', outpdf, env=spoof_tesseract_cache)
check_ocrmypdf(
resources / '2400dpi.pdf',
outpdf,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
pdfinfo = PdfInfo(outpdf)
image = pdfinfo[0].images[0]
@@ -673,7 +694,7 @@ def test_compression_changed(ocrmypdf_exec, resources, image, compression, outpd
im.close()
def test_sidecar_pagecount(spoof_tesseract_cache, resources, outpdf):
def test_sidecar_pagecount(resources, outpdf):
sidecar = outpdf.with_suffix('.txt')
check_ocrmypdf(
resources / '3small.pdf',
@@ -681,7 +702,8 @@ def test_sidecar_pagecount(spoof_tesseract_cache, resources, outpdf):
'--skip-text',
'--sidecar',
sidecar,
env=spoof_tesseract_cache,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
pdfinfo = PdfInfo(resources / '3small.pdf')
@@ -697,10 +719,15 @@ def test_sidecar_pagecount(spoof_tesseract_cache, resources, outpdf):
), "Sidecar page count does not match PDF page count"
def test_sidecar_nonempty(spoof_tesseract_cache, resources, outpdf):
def test_sidecar_nonempty(resources, outpdf):
sidecar = outpdf.with_suffix('.txt')
check_ocrmypdf(
resources / 'ccitt.pdf', outpdf, '--sidecar', sidecar, env=spoof_tesseract_cache
resources / 'ccitt.pdf',
outpdf,
'--sidecar',
sidecar,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
with open(sidecar, 'r', encoding='utf-8') as f:
@@ -709,7 +736,7 @@ def test_sidecar_nonempty(spoof_tesseract_cache, resources, outpdf):
@pytest.mark.parametrize('pdfa_level', ['1', '2', '3'])
def test_pdfa_n(spoof_tesseract_cache, pdfa_level, resources, outpdf):
def test_pdfa_n(pdfa_level, resources, outpdf):
if pdfa_level == '3' and ghostscript.version() < '9.19':
pytest.xfail(reason='Ghostscript >= 9.19 required')
@@ -718,7 +745,8 @@ def test_pdfa_n(spoof_tesseract_cache, pdfa_level, resources, outpdf):
outpdf,
'--output-type',
'pdfa-' + pdfa_level,
env=spoof_tesseract_cache,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
pdfa_info = file_claims_pdfa(outpdf)
+2 -2
View File
@@ -58,7 +58,7 @@ def test_list_range():
assert _pages_from_ranges([0, 1, 2]) == {0, 1, 2}
def test_limited_pages(resources, outpdf, spoof_tesseract_cache):
def test_limited_pages(resources, outpdf):
multi = resources / 'multipage.pdf'
ocrmypdf.ocr(
multi,
@@ -66,7 +66,7 @@ def test_limited_pages(resources, outpdf, spoof_tesseract_cache):
pages='5-6',
optimize=0,
output_type='pdf',
tesseract_env=spoof_tesseract_cache,
plugins=['tests/plugins/tesseract_cache.py'],
)
pi = PdfInfo(outpdf)
assert not pi.pages[0].has_text
+9 -10
View File
@@ -102,9 +102,7 @@ def test_remove_background(resources, outdir):
)
@pytest.mark.parametrize("renderer", ['sandwich', 'hocr'])
@pytest.mark.parametrize("output_type", ['pdf', 'pdfa'])
def test_exotic_image(
spoof_tesseract_cache, pdf, renderer, output_type, resources, outdir
):
def test_exotic_image(pdf, renderer, output_type, resources, outdir):
outfile = outdir / f'test_{pdf}_{renderer}.pdf'
check_ocrmypdf(
resources / pdf,
@@ -118,14 +116,15 @@ def test_exotic_image(
'--skip-text',
'--pdf-renderer',
renderer,
env=spoof_tesseract_cache,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
assert outfile.with_suffix('.pdf.txt').exists()
@pytest.mark.parametrize('renderer', RENDERERS)
def test_non_square_resolution(renderer, spoof_tesseract_cache, resources, outpdf):
def test_non_square_resolution(renderer, resources, outpdf):
# Confirm input image is non-square resolution
in_pageinfo = PdfInfo(resources / 'aspect.pdf')
assert in_pageinfo[0].dpi.x != in_pageinfo[0].dpi.y
@@ -135,7 +134,8 @@ def test_non_square_resolution(renderer, spoof_tesseract_cache, resources, outpd
outpdf,
'--pdf-renderer',
renderer,
env=spoof_tesseract_cache,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
out_pageinfo = PdfInfo(outpdf)
@@ -145,9 +145,7 @@ def test_non_square_resolution(renderer, spoof_tesseract_cache, resources, outpd
@pytest.mark.parametrize('renderer', RENDERERS)
def test_convert_to_square_resolution(
renderer, spoof_tesseract_cache, resources, outpdf
):
def test_convert_to_square_resolution(renderer, resources, outpdf):
# Confirm input image is non-square resolution
in_pageinfo = PdfInfo(resources / 'aspect.pdf')
assert in_pageinfo[0].dpi.x != in_pageinfo[0].dpi.y
@@ -159,7 +157,8 @@ def test_convert_to_square_resolution(
'--force-ocr',
'--pdf-renderer',
renderer,
env=spoof_tesseract_cache,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
out_pageinfo = PdfInfo(outpdf)
+6 -6
View File
@@ -97,7 +97,7 @@ def test_monochrome_correlation(resources, outdir):
@pytest.mark.slow
@pytest.mark.parametrize('renderer', RENDERERS)
def test_autorotate(spoof_tesseract_cache, renderer, resources, outdir):
def test_autorotate(renderer, resources, outdir):
# cardinal.pdf contains four copies of an image rotated in each cardinal
# direction - these ones are "burned in" not tagged with /Rotate
out = check_ocrmypdf(
@@ -108,7 +108,8 @@ def test_autorotate(spoof_tesseract_cache, renderer, resources, outdir):
'1',
'--pdf-renderer',
renderer,
env=spoof_tesseract_cache,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
for n in range(1, 4 + 1):
correlation = check_monochrome_correlation(
@@ -128,9 +129,7 @@ def test_autorotate(spoof_tesseract_cache, renderer, resources, outdir):
('99', 'correlation < 0.10'), # High thres -> never rotate -> low corr
],
)
def test_autorotate_threshold(
spoof_tesseract_cache, threshold, correlation_test, resources, outdir
):
def test_autorotate_threshold(threshold, correlation_test, resources, outdir):
out = check_ocrmypdf(
resources / 'cardinal.pdf',
outdir / 'out.pdf',
@@ -139,7 +138,8 @@ def test_autorotate_threshold(
'-r',
# '-v',
# '1',
env=spoof_tesseract_cache,
'--plugin',
'tests/plugins/tesseract_cache.py',
)
correlation = check_monochrome_correlation(
-1
View File
@@ -31,7 +31,6 @@ from ocrmypdf.exec import tesseract
check_ocrmypdf = pytest.helpers.check_ocrmypdf
run_ocrmypdf = pytest.helpers.run_ocrmypdf
spoof = pytest.helpers.spoof
@pytest.mark.parametrize('basename', ['graph_ocred.pdf', 'cardinal.pdf'])
-1
View File
@@ -31,7 +31,6 @@ from ocrmypdf.exceptions import ExitCode, MissingDependencyError
check_ocrmypdf = pytest.helpers.check_ocrmypdf
run_ocrmypdf = pytest.helpers.run_ocrmypdf
spoof = pytest.helpers.spoof
have_unpaper = pytest.helpers.have_unpaper
+15 -5
View File
@@ -25,7 +25,6 @@ from ocrmypdf.pdfinfo import PdfInfo
check_ocrmypdf = pytest.helpers.check_ocrmypdf
run_ocrmypdf = pytest.helpers.run_ocrmypdf
run_ocrmypdf_api = pytest.helpers.run_ocrmypdf_api
spoof = pytest.helpers.spoof
@pytest.fixture
@@ -39,15 +38,26 @@ def test_userunit_ghostscript_fails(poster, no_outpdf, caplog):
assert 'not supported by Ghostscript' in caplog.text
def test_userunit_pdf_passes(spoof_tesseract_cache, poster, outpdf):
def test_userunit_pdf_passes(poster, outpdf):
before = PdfInfo(poster)
check_ocrmypdf(poster, outpdf, '--output-type=pdf', env=spoof_tesseract_cache)
check_ocrmypdf(
poster,
outpdf,
'--output-type=pdf',
'--plugin',
'tests/plugins/tesseract_cache.py',
)
after = PdfInfo(outpdf)
assert isclose(before[0].width_inches, after[0].width_inches)
def test_rotate_interaction(spoof_tesseract_cache, poster, outpdf):
def test_rotate_interaction(poster, outpdf):
check_ocrmypdf(
poster, outpdf, '--output-type=pdf', '--rotate-pages', env=spoof_tesseract_cache
poster,
outpdf,
'--output-type=pdf',
'--rotate-pages',
'--plugin',
'tests/plugins/tesseract_cache.py',
)