The existing logic would call an OCR plugin's get_languages function before allowing the plugin to check if its dependencies were available. This caused an exception if Tesseract was installed, when we were supposed to issue an error message advising the user to install Tesseract.
315 lines
10 KiB
Python
315 lines
10 KiB
Python
# SPDX-FileCopyrightText: 2022 James R. Barlow
|
|
# SPDX-License-Identifier: MPL-2.0
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
from unittest.mock import patch
|
|
|
|
import pikepdf
|
|
import pytest
|
|
|
|
from ocrmypdf import _validation as vd
|
|
from ocrmypdf._concurrent import NullProgressBar, SerialExecutor
|
|
from ocrmypdf._exec.tesseract import TesseractVersion
|
|
from ocrmypdf._plugin_manager import get_plugin_manager
|
|
from ocrmypdf.api import create_options
|
|
from ocrmypdf.cli import get_parser
|
|
from ocrmypdf.exceptions import BadArgsError, MissingDependencyError
|
|
from ocrmypdf.pdfinfo import PdfInfo
|
|
|
|
from .conftest import run_ocrmypdf_api
|
|
|
|
|
|
def make_opts_pm(input_file='a.pdf', output_file='b.pdf', language='eng', **kwargs):
|
|
if language is not None:
|
|
kwargs['language'] = language
|
|
parser = get_parser()
|
|
pm = get_plugin_manager(kwargs.get('plugins', []))
|
|
pm.hook.add_options(parser=parser) # pylint: disable=no-member
|
|
return (
|
|
create_options(
|
|
input_file=input_file, output_file=output_file, parser=parser, **kwargs
|
|
),
|
|
pm,
|
|
)
|
|
|
|
|
|
def make_opts(*args, **kwargs):
|
|
opts, _pm = make_opts_pm(*args, **kwargs)
|
|
return opts
|
|
|
|
|
|
def test_hocr_notlatin_warning(caplog):
|
|
# Bypass the test to see if the language is installed; we just want to pretend
|
|
# that a non-Latin language is installed
|
|
vd.check_options(
|
|
*make_opts_pm(language='chi_sim', pdf_renderer='hocr', output_type='pdfa'),
|
|
)
|
|
assert 'PDF renderer is known to cause' in caplog.text
|
|
|
|
|
|
def test_old_ghostscript(caplog):
|
|
with patch('ocrmypdf._exec.ghostscript.version', return_value='9.19'):
|
|
vd.check_options(*make_opts_pm(language='chi_sim', output_type='pdfa'))
|
|
assert 'does not work correctly' in caplog.text
|
|
|
|
with patch('ocrmypdf._exec.ghostscript.version', return_value='9.18'):
|
|
with pytest.raises(MissingDependencyError):
|
|
vd.check_options(*make_opts_pm(output_type='pdfa-3'))
|
|
|
|
with patch('ocrmypdf._exec.ghostscript.version', return_value='9.24'):
|
|
with pytest.raises(MissingDependencyError):
|
|
vd.check_options(*make_opts_pm())
|
|
|
|
|
|
def test_old_tesseract_error():
|
|
with patch('ocrmypdf._exec.tesseract.version', return_value='4.00.00alpha'):
|
|
with pytest.raises(MissingDependencyError):
|
|
opts = make_opts(pdf_renderer='sandwich', language='eng')
|
|
plugin_manager = get_plugin_manager(opts.plugins)
|
|
vd.check_options(opts, plugin_manager)
|
|
|
|
|
|
def test_lossless_redo():
|
|
with pytest.raises(BadArgsError):
|
|
vd.check_options_output(make_opts(redo_ocr=True, deskew=True))
|
|
|
|
|
|
def test_mutex_options():
|
|
with pytest.raises(BadArgsError):
|
|
vd.check_options_ocr_behavior(make_opts(force_ocr=True, skip_text=True))
|
|
with pytest.raises(BadArgsError):
|
|
vd.check_options_ocr_behavior(make_opts(redo_ocr=True, skip_text=True))
|
|
with pytest.raises(BadArgsError):
|
|
vd.check_options_ocr_behavior(make_opts(redo_ocr=True, force_ocr=True))
|
|
|
|
|
|
def test_optimizing(caplog):
|
|
opts = make_opts(optimize=0, jbig2_lossy=True, png_quality=18, jpeg_quality=10)
|
|
plugin_manager = get_plugin_manager(opts.plugins)
|
|
vd.check_options(opts, plugin_manager)
|
|
assert 'will be ignored because' in caplog.text
|
|
|
|
|
|
def test_user_words(caplog):
|
|
with patch('ocrmypdf._exec.tesseract.has_user_words', return_value=False):
|
|
opts = make_opts(user_words='foo')
|
|
plugin_manager = get_plugin_manager(opts.plugins)
|
|
vd.check_options(opts, plugin_manager)
|
|
assert (
|
|
'Tesseract 4.0 (which you have installed) ignores --user-words'
|
|
in caplog.text
|
|
)
|
|
caplog.clear()
|
|
with patch('ocrmypdf._exec.tesseract.has_user_words', return_value=True):
|
|
opts = make_opts(user_patterns='foo')
|
|
plugin_manager = get_plugin_manager(opts.plugins)
|
|
vd.check_options(opts, plugin_manager)
|
|
assert (
|
|
'Tesseract 4.0 (which you have installed) ignores --user-words'
|
|
not in caplog.text
|
|
)
|
|
|
|
|
|
def test_pillow_options():
|
|
vd.check_options_pillow(make_opts(max_image_mpixels=0))
|
|
|
|
|
|
def test_output_tty():
|
|
with patch('sys.stdout.isatty', return_value=True):
|
|
with pytest.raises(BadArgsError):
|
|
vd.check_requested_output_file(make_opts(output_file='-'))
|
|
|
|
|
|
def test_report_file_size(tmp_path, caplog):
|
|
in_ = tmp_path / 'a.pdf'
|
|
out = tmp_path / 'b.pdf'
|
|
pdf = pikepdf.new()
|
|
pdf.save(in_)
|
|
pdf.save(out)
|
|
opts = make_opts(output_type='pdf')
|
|
vd.report_output_file_size(opts, in_, out)
|
|
assert caplog.text == ''
|
|
caplog.clear()
|
|
|
|
waste_of_space = b'Dummy' * 5000
|
|
pdf.Root.Dummy = waste_of_space
|
|
pdf.save(in_)
|
|
pdf.Root.Dummy2 = waste_of_space + waste_of_space
|
|
pdf.save(out)
|
|
|
|
vd.report_output_file_size(opts, in_, out, ['The optional dependency...'])
|
|
assert 'optional dependency' in caplog.text
|
|
caplog.clear()
|
|
|
|
vd.report_output_file_size(opts, in_, out, [])
|
|
assert 'No reason' in caplog.text
|
|
caplog.clear()
|
|
|
|
opts = make_opts(in_, out, optimize=0, output_type='pdf')
|
|
vd.report_output_file_size(opts, in_, out, ["Optimization was disabled."])
|
|
assert 'disabled' in caplog.text
|
|
caplog.clear()
|
|
|
|
|
|
def test_false_action_store_true():
|
|
opts = make_opts(keep_temporary_files=True)
|
|
assert opts.keep_temporary_files
|
|
opts = make_opts(keep_temporary_files=False)
|
|
assert not opts.keep_temporary_files
|
|
|
|
|
|
@pytest.mark.parametrize('progress_bar', [True, False])
|
|
def test_no_progress_bar(progress_bar, resources):
|
|
opts = make_opts(progress_bar=progress_bar, input_file=(resources / 'trivial.pdf'))
|
|
plugin_manager = get_plugin_manager(opts.plugins)
|
|
|
|
vd.check_options(opts, plugin_manager)
|
|
|
|
pbar_disabled = None
|
|
|
|
class CheckProgressBar(NullProgressBar):
|
|
def __init__(self, disable, **kwargs):
|
|
nonlocal pbar_disabled
|
|
pbar_disabled = disable
|
|
super().__init__(disable=disable, **kwargs)
|
|
|
|
executor = SerialExecutor(pbar_class=CheckProgressBar)
|
|
pdfinfo = PdfInfo(opts.input_file, progbar=opts.progress_bar, executor=executor)
|
|
|
|
assert pdfinfo is not None
|
|
assert pbar_disabled is not None and pbar_disabled != progress_bar
|
|
|
|
|
|
def test_language_warning(caplog):
|
|
opts = make_opts(language=None)
|
|
_plugin_manager = get_plugin_manager(opts.plugins)
|
|
caplog.set_level(logging.DEBUG)
|
|
with patch(
|
|
'ocrmypdf._validation.locale.getlocale', return_value=('en_US', 'UTF-8')
|
|
) as mock:
|
|
vd.check_options_languages(opts, {'eng'})
|
|
assert opts.languages == {'eng'}
|
|
assert '' in caplog.text
|
|
mock.assert_called_once()
|
|
|
|
opts = make_opts(language=None)
|
|
with patch(
|
|
'ocrmypdf._validation.locale.getlocale', return_value=('fr_FR', 'UTF-8')
|
|
) as mock:
|
|
vd.check_options_languages(opts, {'eng'})
|
|
assert opts.languages == {'eng'}
|
|
assert 'assuming --language' in caplog.text
|
|
mock.assert_called_once()
|
|
|
|
|
|
def test_version_comparison():
|
|
vd.check_external_program(
|
|
program="dummy_basic",
|
|
package="dummy",
|
|
version_checker=lambda: '9.0',
|
|
need_version='8.0.2',
|
|
)
|
|
vd.check_external_program(
|
|
program="dummy_doubledigit",
|
|
package="dummy",
|
|
version_checker=lambda: '10.0',
|
|
need_version='8.0.2',
|
|
)
|
|
with pytest.raises(MissingDependencyError):
|
|
vd.check_external_program(
|
|
program="tesseract",
|
|
package="tesseract",
|
|
version_checker=lambda: '4.0.0-beta.1',
|
|
need_version='4.0.0',
|
|
version_parser=TesseractVersion,
|
|
)
|
|
vd.check_external_program(
|
|
program="tesseract",
|
|
package="tesseract",
|
|
version_checker=lambda: 'v5.0.0-alpha.20200201',
|
|
need_version='4.0.0',
|
|
version_parser=TesseractVersion,
|
|
)
|
|
vd.check_external_program(
|
|
program="tesseract",
|
|
package="tesseract",
|
|
version_checker=lambda: '5.0.0-rc1.20211030',
|
|
need_version='4.0.0',
|
|
version_parser=TesseractVersion,
|
|
)
|
|
vd.check_external_program(
|
|
program="tesseract",
|
|
package="tesseract",
|
|
version_checker=lambda: 'v4.0.0.20181030', # Some Windows builds use this format
|
|
need_version='4.0.0',
|
|
version_parser=TesseractVersion,
|
|
)
|
|
vd.check_external_program(
|
|
program="tesseract",
|
|
package="tesseract",
|
|
version_checker=lambda: '4.1.1-rc2-25-g9707',
|
|
need_version='4.0.0',
|
|
version_parser=TesseractVersion,
|
|
)
|
|
with pytest.raises(MissingDependencyError):
|
|
vd.check_external_program(
|
|
program="dummy_fails",
|
|
package="dummy",
|
|
version_checker=lambda: '1.0',
|
|
need_version='2.0',
|
|
)
|
|
|
|
|
|
def test_optional_program_recommended(caplog):
|
|
caplog.clear()
|
|
|
|
def raiser():
|
|
raise FileNotFoundError('jbig2')
|
|
|
|
with caplog.at_level(logging.WARNING):
|
|
vd.check_external_program(
|
|
program="jbig2",
|
|
package="jbig2enc",
|
|
version_checker=raiser,
|
|
need_version='42',
|
|
required_for='this test case',
|
|
recommended=True,
|
|
)
|
|
assert any(
|
|
(loglevel == logging.WARNING and "recommended" in msg)
|
|
for _logger_name, loglevel, msg in caplog.record_tuples
|
|
)
|
|
|
|
|
|
def test_pagesegmode_warning(caplog):
|
|
opts = make_opts(tesseract_pagesegmode='0')
|
|
plugin_manager = get_plugin_manager(opts.plugins)
|
|
vd.check_options(opts, plugin_manager)
|
|
assert 'disable OCR' in caplog.text
|
|
|
|
|
|
def test_two_languages():
|
|
vd.check_options_languages(
|
|
create_options(
|
|
input_file='a.pdf',
|
|
output_file='b.pdf',
|
|
parser=get_parser(),
|
|
language='fakelang1+fakelang2',
|
|
),
|
|
{'fakelang1', 'fakelang2'},
|
|
)
|
|
|
|
|
|
def test_sidecar_equals_output(resources, no_outpdf):
|
|
op = no_outpdf
|
|
with pytest.raises(BadArgsError, match=r'--sidecar'):
|
|
run_ocrmypdf_api(resources / 'trivial.pdf', op, '--sidecar', op)
|
|
|
|
|
|
def test_devnull_sidecar(resources):
|
|
with pytest.raises(BadArgsError, match=r'--sidecar.*NUL'):
|
|
run_ocrmypdf_api(resources / 'trivial.pdf', os.devnull, '--sidecar')
|