Convert all ghostscript spoofs to test plugins
This commit is contained in:
Executable → Regular
+21
-25
@@ -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
|
||||
Executable → Regular
+23
-32
@@ -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
|
||||
Executable → Regular
+34
-24
@@ -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
|
||||
Executable → Regular
+23
-23
@@ -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
@@ -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',
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user