Convert all ghostscript spoofs to test plugins

This commit is contained in:
James R. Barlow
2020-06-09 00:00:25 -07:00
parent c22f245606
commit 2059e916da
6 changed files with 114 additions and 133 deletions
+21 -25
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
# © 2016 James R. Barlow: github.com/jbarlow83
# © 2020 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
@@ -20,34 +19,31 @@
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from unittest.mock import patch
import os
import sys
from subprocess import check_call
from gs import real_ghostscript
"""Replicate one type of Ghostscript feature elision warning during
PDF/A creation."""
from ocrmypdf import hookimpl
from ocrmypdf.builtin_plugins import ghostscript
from ocrmypdf.exec import run
elision_warning = """GPL Ghostscript 9.20: Setting Overprint Mode to 1
not permitted in PDF/A-2, overprint mode not set"""
def main():
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)
if '-sDEVICE=pdfwrite' in sys.argv[1:]:
print(elision_warning)
sys.exit(0)
def run_append_stderr(*args, **kwargs):
proc = run(*args, **kwargs)
proc.stderr = b'\n'.join([proc.stderr, elision_warning.encode('utf-8')])
return proc
if __name__ == '__main__':
main()
@hookimpl
def generate_pdfa(pdf_pages, pdfmark, output_file, compression, pdf_version, pdfa_part):
with patch('ocrmypdf.exec.ghostscript.run', new=run_append_stderr):
ghostscript.generate_pdfa(
pdf_pages=pdf_pages,
pdfmark=pdfmark,
output_file=output_file,
compression=compression,
pdf_version=pdf_version,
pdfa_part=pdfa_part,
)
return output_file
+23 -32
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
# © 2016 James R. Barlow: github.com/jbarlow83
# © 2020 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
@@ -20,41 +19,33 @@
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import os
import sys
from unittest.mock import patch
from gs import real_ghostscript
from ocrmypdf import hookimpl
from ocrmypdf.builtin_plugins import ghostscript
from ocrmypdf.exec import run
"""Replicate Ghostscript PDF/A conversion failure by suppressing some
arguments"""
def main():
if '--version' in sys.argv:
print('9.20')
print('SPOOFED: ' + os.path.basename(__file__))
sys.exit(0)
# Unless some argument is calling for PDFA generation, forward to
# real ghostscript
if not any(arg.startswith('-dPDFA') for arg in sys.argv):
real_ghostscript(sys.argv)
return
def run_rig_args(args, **kwargs):
# Remove the two arguments that tell ghostscript to create a PDF/A
# Does not remove the Postscript definition file - not necessary
# to cause PDF/A creation failure
argv = []
for arg in sys.argv:
if arg.startswith('-dPDFA'):
continue
elif arg.startswith('-dPDFACompatibilityPolicy'):
continue
argv.append(arg)
real_ghostscript(argv)
new_args = [
arg for arg in args if not arg.startswith('-dPDFA') and not arg.endswith('.ps')
]
proc = run(new_args, **kwargs)
return proc
if __name__ == '__main__':
main()
@hookimpl
def generate_pdfa(pdf_pages, pdfmark, output_file, compression, pdf_version, pdfa_part):
with patch('ocrmypdf.exec.ghostscript.run', new=run_rig_args):
ghostscript.generate_pdfa(
pdf_pages=pdf_pages,
pdfmark=pdfmark,
output_file=output_file,
compression=compression,
pdf_version=pdf_version,
pdfa_part=pdfa_part,
)
return output_file
+34 -24
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
# © 2016 James R. Barlow: github.com/jbarlow83
# © 2020 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
@@ -20,30 +19,41 @@
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from pathlib import Path
from subprocess import CalledProcessError
from unittest.mock import patch
import os
import sys
from gs import real_ghostscript
"""Replicate Ghostscript raster failure while allowing rendering"""
from ocrmypdf import hookimpl
from ocrmypdf.builtin_plugins import ghostscript
from ocrmypdf.exec import run
def main():
if '--version' in sys.argv:
print('9.20')
print('SPOOFED: ' + os.path.basename(__file__))
sys.exit(0)
# For non-image rastering calls, use real ghostscript
if '-sDEVICE=pdfwrite' in sys.argv or '-sDEVICE=txtwrite' in sys.argv:
real_ghostscript(sys.argv)
return
# Fail
print("ERROR: Ghost story archive not found", file=sys.stderr)
sys.exit(1)
def raise_gs_fail(*args, **kwargs):
raise CalledProcessError(
1, 'gs', output=b"", stderr=b"ERROR: Ghost story archive not found"
)
if __name__ == '__main__':
main()
@hookimpl
def rasterize_pdf_page(
input_file,
output_file,
raster_device,
raster_dpi,
pageno,
page_dpi=None,
rotation=None,
filter_vector=False,
) -> Path:
with patch('ocrmypdf.exec.ghostscript.run', new=raise_gs_fail):
ghostscript.rasterize_pdf_page(
input_file=input_file,
output_file=output_file,
raster_device=raster_device,
raster_dpi=raster_dpi,
pageno=pageno,
page_dpi=page_dpi,
rotation=rotation,
filter_vector=filter_vector,
)
return output_file
+23 -23
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
# © 2016-18 James R. Barlow: github.com/jbarlow83
# © 2020 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
@@ -20,29 +19,30 @@
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""Replicate Ghostscript render failure while allowing rasterizing"""
from pathlib import Path
from subprocess import CalledProcessError
from unittest.mock import patch
import os
import sys
from gs import real_ghostscript
from ocrmypdf import hookimpl
from ocrmypdf.builtin_plugins import ghostscript
from ocrmypdf.exec import run
def main():
if '--version' in sys.argv:
print('9.20')
print('SPOOFED: ' + os.path.basename(__file__))
sys.exit(0)
# For any rasterize calls (device != pdfwrite) call real ghostscript
if '-sDEVICE=pdfwrite' not in sys.argv:
real_ghostscript(sys.argv)
return
# Fail
print("ERROR: Casper is not a friendly ghost", file=sys.stderr)
sys.exit(1)
def raise_gs_fail(*args, **kwargs):
raise CalledProcessError(
1, 'gs', output=b"", stderr=b"ERROR: Casper is not a friendly ghost"
)
if __name__ == '__main__':
main()
@hookimpl
def generate_pdfa(pdf_pages, pdfmark, output_file, compression, pdf_version, pdfa_part):
with patch('ocrmypdf.exec.ghostscript.run', new=raise_gs_fail):
ghostscript.generate_pdfa(
pdf_pages=pdf_pages,
pdfmark=pdfmark,
output_file=output_file,
compression=compression,
pdf_version=pdf_version,
pdfa_part=pdfa_part,
)
return output_file
+12 -28
View File
@@ -32,26 +32,6 @@ run_ocrmypdf_api = pytest.helpers.run_ocrmypdf_api
spoof = pytest.helpers.spoof
@pytest.fixture
def spoof_gs_render_fail(tmp_path_factory):
return spoof(tmp_path_factory, gs='gs_render_failure.py')
@pytest.fixture
def spoof_gs_raster_fail(tmp_path_factory):
return spoof(tmp_path_factory, gs='gs_raster_failure.py')
@pytest.fixture
def spoof_no_pdfa(tmp_path_factory):
return spoof(tmp_path_factory, gs='gs_pdfa_failure.py')
@pytest.fixture
def spoof_pdfa_warning(tmp_path_factory):
return spoof(tmp_path_factory, gs='gs_feature_elision.py')
@pytest.fixture
def francais(resources):
path = resources / 'francais.pdf'
@@ -106,48 +86,52 @@ def test_rasterize_rotated(francais, outdir, caplog):
assert im.info['dpi'] == (forced_dpi[1], forced_dpi[0])
def test_gs_render_failure(spoof_gs_render_fail, resources, outpdf):
def test_gs_render_failure(resources, outpdf):
p, out, err = run_ocrmypdf(
resources / 'blank.pdf',
outpdf,
'--plugin',
'tests/plugins/tesseract_noop.py',
env=spoof_gs_render_fail,
'--plugin',
'tests/plugins/gs_render_failure.py',
)
assert 'Casper is not a friendly ghost' in err
assert p.returncode == ExitCode.child_process_error
def test_gs_raster_failure(spoof_gs_raster_fail, resources, outpdf):
def test_gs_raster_failure(resources, outpdf):
p, out, err = run_ocrmypdf(
resources / 'francais.pdf',
outpdf,
'--plugin',
'tests/plugins/tesseract_noop.py',
env=spoof_gs_raster_fail,
'--plugin',
'tests/plugins/gs_raster_failure.py',
)
assert 'Ghost story archive not found' in err
assert p.returncode == ExitCode.child_process_error
def test_ghostscript_pdfa_failure(spoof_no_pdfa, resources, outpdf):
def test_ghostscript_pdfa_failure(resources, outpdf):
p, out, err = run_ocrmypdf(
resources / 'francais.pdf',
outpdf,
'--plugin',
'tests/plugins/tesseract_noop.py',
env=spoof_no_pdfa,
'--plugin',
'tests/plugins/gs_pdfa_failure.py',
)
assert (
p.returncode == ExitCode.pdfa_conversion_failed
), "Unexpected return when PDF/A fails"
def test_ghostscript_feature_elision(spoof_pdfa_warning, resources, outpdf):
def test_ghostscript_feature_elision(resources, outpdf):
check_ocrmypdf(
resources / 'francais.pdf',
outpdf,
'--plugin',
'tests/plugins/tesseract_noop.py',
env=spoof_pdfa_warning,
'--plugin',
'tests/plugins/gs_feature_elision.py',
)