Remove the OCRMYPDF_program environment variables

Really, this was just replicating the functionality of the PATH
environment variable, and users probably do that anyway.
This commit is contained in:
James R. Barlow
2018-03-24 15:07:02 -07:00
parent cbdf9c88c5
commit 11d74dea09
16 changed files with 119 additions and 101 deletions
+4 -20
View File
@@ -33,11 +33,7 @@ If you want to adjust the amount of time spent on OCR, change ``--tesseract-time
Overriding default tesseract
""""""""""""""""""""""""""""
OCRmyPDF checks the environment variable ``OCRMYPDF_TESSERACT`` for the full path *to the tesseract binary* first.
.. envvar:: OCRMYPDF_TESSERACT
Specify the location of the Tesseract binary.
OCRmyPDF checks the system ``PATH`` for the ``tesseract`` binary.
.. envvar:: TESSDATA_PREFIX
@@ -48,11 +44,11 @@ For example, if you are testing tesseract 4.00 and don't wish to use an existing
.. code-block:: bash
env \
OCRMYPDF_TESSERACT=/home/user/src/tesseract4/api/tesseract \
PATH=/home/user/src/tesseract4/api:$PATH \
TESSDATA_PREFIX=/home/user/src/tesseract4 \
ocrmypdf --tesseract-oem 2 input.pdf output.pdf
In this example ``TESSDATA_PREFIX`` directs tesseract 4.0 to use LSTM training data. ``--tesseract-oem 1`` requests tesseract 4.0's new LSTM engine. (Tesseract 4.0 only.)
In this example ``TESSDATA_PREFIX`` directs Tesseract 4.0 to use LSTM training data. ``--tesseract-oem 1`` requests tesseract 4.0's new LSTM engine. (Tesseract 4.0 only.)
Overriding other support programs
@@ -64,19 +60,7 @@ In addition to tesseract, OCRmyPDF uses the following external binaries:
* ``unpaper``
* ``qpdf``
In each case OCRmyPDF will check the environment variable ``OCRMYPDF_{program}`` before asking the system to find ``{program}`` on the PATH. For example, you could redirect OCRmyPDF to ``OCRMYPDF_GS`` to override Ghostscript. The full list is below:
.. envvar:: OCRMYPDF_GS
Specify the location of the Ghostscript binary.
.. envvar:: OCRMYPDF_UNPAPER
Specify the location of the unpaper binary.
.. envvar:: OCRMYPDF_QPDF
Specify the location of the qpdf binary.
In each case OCRmyPDF will search the ``PATH`` environment variable to locate the binaries.
Changing tesseract configuration variables
+12 -1
View File
@@ -8,16 +8,27 @@ The OCRmyPDF package itself does not contain a public API, although it is fairly
v6.0.0
------
- The software license has been changed to GPLv3. The license of test files
- The software license has been changed to GPLv3. Test resource files and some individual sources may have other licenses.
- OCRmyPDF now depends on `PyMuPDF <https://pymupdf.readthedocs.io/en/latest/installation/>`_. Including PyMuPDF is the primary reason for the change to GPLv3.
- Other backward incompatible changes
+ The ``OCRMYPDF_TESSERACT``, ``OCRMYPDF_QPDF``, ``OCRMYPDF_GS`` and ``OCRMYPDF_UNPAPER`` environment variables are no longer used. Change ``PATH`` if you need to override the external programs OCRmyPDF uses.
+ The function ``ocrmypdf.exec.get_program`` was removed.
+ The deprecated module ``ocrmypdf.pageinfo`` was removed.
- Fixed an issue where OCRmyPDF failed to detect existing text on pages, depending on how the text and fonts were stored within the PDF. (#233, #232)
- Fixed an issue that caused dramatic inflation of file sizes when ``--skip-text --output-type pdf`` was used. OCRmyPDF now removes duplicate resources such as fonts, images and other objects that it generates. (#237)
- Improved performance of the inital page splitting step. Originally this step was not believed to be expensive and ran in a process. Large file testing revealed it to be a bottleneck, so it is now parallelized. (#234)
-
v5.7.0
------
+14
View File
@@ -725,6 +725,19 @@ def preamble(_log):
_log.debug('qpdf ' + qpdf.version())
def check_environ(options, _log):
old_envvars = (
'OCRMYPDF_TESSERACT',
'OCRMYPDF_QPDF',
'OCRMYPDF_GS',
'OCRMYPDF_UNPAPER')
for k in old_envvars:
if k in os.environ:
_log.warning(textwrap.dedent("""\
OCRmyPDF no longer uses the environment variable {}.
Change PATH to select alternate programs.""".format(k)))
def check_input_file(options, _log, start_input_file):
if options.input_file == '-':
# stdin
@@ -789,6 +802,7 @@ def run_pipeline():
# jobs run multithreaded.
if tesseract.v4():
os.environ.setdefault('OMP_THREAD_LIMIT', '1')
check_environ(options, _log)
try:
work_folder = mkdtemp(prefix="com.github.ocrmypdf.")
+5 -17
View File
@@ -24,17 +24,11 @@ from subprocess import run, STDOUT, PIPE, CalledProcessError
from ..exceptions import MissingDependencyError
def get_program(name):
"Check environment variables for overrides to this program"
envvar = 'OCRMYPDF_' + name.upper()
return os.environ.get(envvar, name)
def get_version(program, *,
version_arg='--version', regex=r'(\d+(\.\d+)*)'):
"Get the version of the specified program, "
"Get the version of the specified program"
args_prog = [
get_program(program),
program,
version_arg
]
try:
@@ -43,15 +37,9 @@ def get_version(program, *,
stdout=PIPE, stderr=STDOUT, check=True)
output = proc.stdout
except CalledProcessError as e:
if get_program(program) == program:
raise MissingDependencyError(
"Could not find program '{}' on the PATH".format(
program)) from e
else:
raise MissingDependencyError(
"Could not find program '{}'".format(
get_program(program))) from e
raise MissingDependencyError(
"Could not find program '{}' on the PATH".format(
program)) from e
try:
version = re.match(regex, output.strip()).group(1)
except AttributeError as e:
+3 -3
View File
@@ -22,7 +22,7 @@ from functools import lru_cache
import re
import sys
from PIL import Image
from . import get_program, get_version
from . import get_version
from ..exceptions import SubprocessOutputError, MissingDependencyError
from ..helpers import fspath
@@ -61,7 +61,7 @@ def rasterize_pdf(input_file, output_file, xres, yres, raster_device, log,
page_dpi = res
with NamedTemporaryFile(delete=True) as tmp:
args_gs = [
get_program('gs'),
'gs',
'-dQUIET',
'-dSAFER',
'-dBATCH',
@@ -129,7 +129,7 @@ def generate_pdfa(pdf_pages, output_file, compression, log,
with NamedTemporaryFile(delete=True) as gs_pdf:
args_gs = [
get_program("gs"),
"gs",
"-dQUIET",
"-dBATCH",
"-dNOPAUSE",
+7 -7
View File
@@ -24,7 +24,7 @@ import resource
from ..exceptions import InputFileError, SubprocessOutputError, \
MissingDependencyError, EncryptedPdfError
from . import get_program, get_version
from . import get_version
from ..helpers import re_symlink
@@ -35,7 +35,7 @@ def version():
def check(input_file, log=None):
args_qpdf = [
get_program('qpdf'),
'qpdf',
'--check',
input_file
]
@@ -71,7 +71,7 @@ def _probably_encrypted(e):
def repair(input_file, output_file, log):
args_qpdf = [
get_program('qpdf'), input_file, output_file
'qpdf', input_file, output_file
]
try:
run(args_qpdf, stderr=STDOUT, stdout=PIPE, universal_newlines=True,
@@ -98,7 +98,7 @@ def repair(input_file, output_file, log):
def get_npages(input_file, log):
try:
pages = run(
[get_program('qpdf'), '--show-npages', input_file],
['qpdf', '--show-npages', input_file],
universal_newlines=True, check=True, stdout=PIPE, stderr=STDOUT)
except CalledProcessError as e:
if e.returncode == 2 and e.output.find('No such file'):
@@ -115,7 +115,7 @@ def split_pages(input_file, work_folder, npages):
"""
for n in range(int(npages)):
args_qpdf = [
get_program('qpdf'), input_file,
'qpdf', input_file,
'--pages', input_file, '{0}'.format(n + 1), '--',
os.path.join(work_folder, '{0:06d}.page.pdf'.format(n + 1))
]
@@ -124,7 +124,7 @@ def split_pages(input_file, work_folder, npages):
def extract_page(input_file, output_file, pageno):
args_qpdf = [
get_program('qpdf'), input_file,
'qpdf', input_file,
'--pages', input_file, '{0}'.format(pageno + 1), '--',
output_file
]
@@ -147,7 +147,7 @@ def _merge_inner(input_files, output_file, min_version=None, log=None):
import logging as log
args_qpdf = [
get_program('qpdf')
'qpdf'
] + version_arg + [
input_files[0], '--pages'
] + input_files + ['--', output_file]
+4 -4
View File
@@ -28,7 +28,7 @@ from subprocess import PIPE, CalledProcessError, \
from ..exceptions import MissingDependencyError, TesseractConfigError
from ..helpers import page_number
from . import get_program, get_version
from . import get_version
OrientationConfidence = namedtuple(
'OrientationConfidence',
@@ -70,7 +70,7 @@ def has_textonly_pdf():
parse the parameter list
"""
args_tess = [
get_program('tesseract'),
'tesseract',
'--print-parameters'
]
params = ''
@@ -94,7 +94,7 @@ def psm():
@lru_cache(maxsize=1)
def languages():
args_tess = [
get_program('tesseract'),
'tesseract',
'--list-langs'
]
try:
@@ -113,7 +113,7 @@ def languages():
def tess_base_args(langs, engine_mode):
args = [
get_program('tesseract'),
'tesseract',
]
if langs:
args.extend(['-l', '+'.join(langs)])
+2 -2
View File
@@ -24,7 +24,7 @@ import sys
import os
from functools import lru_cache
from ..exceptions import MissingDependencyError
from . import get_program, get_version
from . import get_version
try:
@@ -41,7 +41,7 @@ def version():
def run(input_file, output_file, dpi, log, mode_args):
args_unpaper = [
get_program('unpaper'),
'unpaper',
'-v',
'--dpi', str(dpi)
] + mode_args
+20 -15
View File
@@ -60,36 +60,41 @@ OCRMYPDF = [sys.executable, '-m', 'ocrmypdf']
@pytest.helpers.register
def spoof(**kwargs):
"""Modify environment variables to override subprocess executables
def spoof(tmpdir_factory, **kwargs):
"""Modify PATH to override subprocess executables
spoof(program1='replacement', ...)
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.
Creates temporary directory with symlinks to targets.
"""
env = os.environ.copy()
slug = '-'.join(v.replace('.py', '') for v in sorted(kwargs.values()))
spoofer_base = Path(tmpdir_factory.mktemp('spoofers'))
tmpdir = spoofer_base / slug
tmpdir.mkdir(parents=True)
for replace_program, with_spoof in kwargs.items():
spoofer = os.path.join(SPOOF_PATH, with_spoof)
if not os.access(spoofer, os.X_OK):
os.chmod(spoofer, 0o755)
env['OCRMYPDF_' + replace_program.upper()] = spoofer
spoofer = Path(SPOOF_PATH) / with_spoof
spoofer.chmod(0o755)
(tmpdir / replace_program).symlink_to(spoofer)
env['_OCRMYPDF_SAVE_PATH'] = env['PATH']
env['PATH'] = str(tmpdir) + ":" + env['PATH']
return env
@pytest.fixture
def spoof_tesseract_noop():
return spoof(tesseract='tesseract_noop.py')
@pytest.fixture(scope='session')
def spoof_tesseract_noop(tmpdir_factory):
return spoof(tmpdir_factory, tesseract='tesseract_noop.py')
@pytest.fixture
def spoof_tesseract_cache():
@pytest.fixture(scope='session')
def spoof_tesseract_cache(tmpdir_factory):
if running_in_docker():
return os.environ.copy()
return spoof(tesseract="tesseract_cache.py")
return spoof(tmpdir_factory, tesseract="tesseract_cache.py")
@pytest.fixture
+1 -1
View File
@@ -40,11 +40,11 @@ not permitted in PDF/A-2, overprint mode not set"""
def main():
os.environ['PATH'] = os.environ['_OCRMYPDF_SAVE_PATH']
if '--version' in sys.argv:
print('9.20')
print('SPOOFED: ' + os.path.basename(__file__))
sys.exit(0)
gs_args = ['gs'] + sys.argv[1:]
check_call(gs_args)
+1
View File
@@ -34,6 +34,7 @@ def real_ghostscript(argv):
def main():
os.environ['PATH'] = os.environ['_OCRMYPDF_SAVE_PATH']
if '--version' in sys.argv:
print('9.20')
print('SPOOFED: ' + os.path.basename(__file__))
+1
View File
@@ -34,6 +34,7 @@ def real_ghostscript(argv):
def main():
os.environ['PATH'] = os.environ['_OCRMYPDF_SAVE_PATH']
if '--version' in sys.argv:
print('9.20')
print('SPOOFED: ' + os.path.basename(__file__))
+1
View File
@@ -34,6 +34,7 @@ def real_ghostscript(argv):
def main():
os.environ['PATH'] = os.environ['_OCRMYPDF_SAVE_PATH']
if '--version' in sys.argv:
print('9.20')
print('SPOOFED: ' + os.path.basename(__file__))
+1
View File
@@ -66,6 +66,7 @@ def real_tesseract():
return # Not reachable
def main():
os.environ['PATH'] = os.environ['_OCRMYPDF_SAVE_PATH']
operation = sys.argv[-2]
sidecar = False
if sys.argv[-1] == 'txt':
+38 -28
View File
@@ -46,44 +46,54 @@ if tesseract.has_textonly_pdf():
RENDERERS.append('sandwich')
@pytest.fixture
def spoof_tesseract_crash():
return spoof(tesseract='tesseract_crash.py')
@pytest.fixture(scope='session')
def spoof_tesseract_crash(tmpdir_factory):
return spoof(tmpdir_factory, tesseract='tesseract_crash.py')
@pytest.fixture
def spoof_tesseract_big_image_error():
return spoof(tesseract='tesseract_big_image_error.py')
@pytest.fixture(scope='session')
def spoof_tesseract_big_image_error(tmpdir_factory):
return spoof(tmpdir_factory, tesseract='tesseract_big_image_error.py')
@pytest.fixture
def spoof_no_tess_no_pdfa():
return spoof(tesseract='tesseract_noop.py', gs='gs_pdfa_failure.py')
@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')
@pytest.fixture
def spoof_no_tess_pdfa_warning():
return spoof(tesseract='tesseract_noop.py', gs='gs_feature_elision.py')
@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')
@pytest.fixture
def spoof_no_tess_gs_render_fail():
return spoof(tesseract='tesseract_noop.py', gs='gs_render_failure.py')
@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')
@pytest.fixture
def spoof_no_tess_gs_raster_fail():
return spoof(tesseract='tesseract_noop.py', gs='gs_raster_failure.py')
@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')
@pytest.fixture
def spoof_tess_bad_utf8():
return spoof(tesseract='tesseract_badutf8.py')
@pytest.fixture(scope='session')
def spoof_tess_bad_utf8(tmpdir_factory):
return spoof(tmpdir_factory, tesseract='tesseract_badutf8.py')
@pytest.fixture
def spoof_qpdf_always_error():
return spoof(qpdf='qpdf_dummy_return2.py')
@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')
def test_quick(spoof_tesseract_cache, resources, outpdf):
@@ -548,6 +558,7 @@ def test_tesseract_image_too_big(renderer, spoof_tesseract_big_image_error,
env=spoof_tesseract_big_image_error)
@pytest.mark.skipif(True, reason="need new implementation")
def test_no_unpaper(resources, no_outpdf):
env = os.environ.copy()
env['OCRMYPDF_UNPAPER'] = os.path.abspath('./spoof/no_unpaper_here.py')
@@ -556,11 +567,10 @@ def test_no_unpaper(resources, no_outpdf):
assert p.returncode == ExitCode.missing_dependency
def test_old_unpaper(resources, no_outpdf):
env = os.environ.copy()
env['OCRMYPDF_UNPAPER'] = os.path.abspath('./spoof/unpaper_oldversion.py')
def test_old_unpaper(spoof_unpaper_oldversion, resources, no_outpdf):
p, out, err = run_ocrmypdf(
resources / 'c02-22.pdf', no_outpdf, '--clean', env=env)
resources / 'c02-22.pdf', no_outpdf, '--clean',
env=spoof_unpaper_oldversion)
assert p.returncode == ExitCode.missing_dependency
+5 -3
View File
@@ -23,6 +23,7 @@ import sys
import os
import PyPDF2 as pypdf
from contextlib import contextmanager
from pathlib import Path
# pylint: disable=no-member
spoof = pytest.helpers.spoof
@@ -31,8 +32,7 @@ spoof = pytest.helpers.spoof
@pytest.fixture
def ensure_tess4():
if tesseract.v4():
# Either "tesseract" on $PATH is already v4, or
# OCRMYPDF_TESSERACT is tess4 already
# "tesseract" on $PATH is already v4
return os.environ.copy()
if os.environ.get('OCRMYPDF_TESS4'):
@@ -41,7 +41,9 @@ def ensure_tess4():
# setting OCRMYPDF_TESS4 to test tess4 and PATH to point to tess3
# on a system with both installed.
env = os.environ.copy()
env['OCRMYPDF_TESSERACT'] = env['OCRMYPDF_TESS4']
tess4 = Path(os.environ['OCRMYPDF_TESS4'])
assert tess4.is_file()
env['PATH'] = tess4.parent + ':' + env['PATH']
return env
raise EnvironmentError("Can't find Tesseract 4")