Merge branch 'feature/soft-error'

This commit is contained in:
James R. Barlow
2023-06-03 00:23:10 -07:00
19 changed files with 238 additions and 25 deletions
+9 -1
View File
@@ -30,7 +30,7 @@ except AttributeError:
log = logging.getLogger(__name__)
# Most reliable what to get the bitness of Python interpreter, according to Python docs
# Most reliable way to get the bitness of Python interpreter, according to Python docs
_IS_64BIT = sys.maxsize > 2**32
_GSWIN = None
@@ -63,6 +63,7 @@ def rasterize_pdf(
page_dpi: Resolution | None = None,
rotation: int | None = None,
filter_vector: bool = False,
stop_on_error: bool = False,
):
"""Rasterize one page of a PDF at resolution raster_dpi in canvas units."""
raster_dpi = raster_dpi.round(6)
@@ -83,6 +84,7 @@ def rasterize_pdf(
f'-r{raster_dpi.x:f}x{raster_dpi.y:f}',
]
+ (['-dFILTERVECTOR'] if filter_vector else [])
+ (['-dPDFSTOPONERROR'] if stop_on_error else [])
+ [
'-o',
'-',
@@ -161,6 +163,7 @@ def generate_pdfa(
pdf_version: str = '1.5',
pdfa_part: str = '2',
progressbar_class=None,
stop_on_error: bool = False,
):
# Ghostscript's compression is all or nothing. We can either force all images
# to JPEG, force all to Flate/PNG, or let it decide how to encode the images.
@@ -193,6 +196,10 @@ def generate_pdfa(
# https://bugs.ghostscript.com/show_bug.cgi?id=705187
compression_args.append('-dNEWPDF=false')
if os.name == 'nt':
# Windows has lots of fatal "permission denied" errors
stop_on_error = False
# nb no need to specify ProcessColorModel when ColorConversionStrategy
# is set; see:
# https://bugs.ghostscript.com/show_bug.cgi?id=699392
@@ -207,6 +214,7 @@ def generate_pdfa(
"-dAutoRotatePages=/None",
"-sColorConversionStrategy=" + strategy,
]
+ (['-dPDFSTOPONERROR'] if stop_on_error else [])
+ compression_args
+ [
"-dJPEGQ=95",
+3
View File
@@ -343,6 +343,7 @@ def rasterize_preview(input_file: Path, page_context: PageContext) -> Path:
page_dpi=page_dpi,
rotation=0,
filter_vector=False,
stop_on_soft_error=not page_context.options.continue_on_soft_render_error,
)
return output_file
@@ -455,6 +456,7 @@ def rasterize(
pageno=pageinfo.pageno + 1,
rotation=correction,
filter_vector=remove_vectors,
stop_on_soft_error=not page_context.options.continue_on_soft_render_error,
)
return output_file
@@ -736,6 +738,7 @@ def convert_to_pdfa(input_pdf: Path, input_ps_stub: Path, context: PdfContext) -
if options.progress_bar
else None
),
stop_on_soft_error=not options.continue_on_soft_render_error,
)
return output_file
+1
View File
@@ -248,6 +248,7 @@ def ocr( # noqa: ruff: disable=D417
user_words: os.PathLike | None = None,
user_patterns: os.PathLike | None = None,
fast_web_view: float | None = None,
continue_on_soft_render_error: bool | None = None,
plugins: Iterable[StrPath] | None = None,
plugin_manager=None,
keep_temporary_files: bool | None = None,
@@ -45,6 +45,7 @@ def rasterize_pdf_page(
page_dpi,
rotation,
filter_vector,
stop_on_soft_error,
):
"""Rasterize a single page of a PDF file using Ghostscript."""
ghostscript.rasterize_pdf(
@@ -56,6 +57,7 @@ def rasterize_pdf_page(
page_dpi=page_dpi,
rotation=rotation,
filter_vector=filter_vector,
stop_on_error=stop_on_soft_error,
)
return output_file
@@ -69,6 +71,7 @@ def generate_pdfa(
pdf_version,
pdfa_part,
progressbar_class,
stop_on_soft_error,
):
"""Generate a PDF/A from the list of PDF pages and PDF/A metadata."""
ghostscript.generate_pdfa(
@@ -78,5 +81,6 @@ def generate_pdfa(
pdf_version=pdf_version,
pdfa_part=pdfa_part,
progressbar_class=progressbar_class,
stop_on_error=stop_on_soft_error,
)
return output_file
+8
View File
@@ -420,6 +420,14 @@ Online documentation is located at:
"which do not benefit. If the threshold is 0 it will be apply to all files. "
"Set the threshold very high to disable.",
)
advanced.add_argument(
'--continue-on-soft-render-error',
action='store_true',
help="Continue processing pages after a recoverable PDF rendering error. "
"A recoverable error is one that does not prevent the page from being "
"rendered, but may result in visual differences compared to the input "
"file. Missing fonts are a typical source of these errors.",
)
advanced.add_argument(
'--plugin',
dest='plugins',
+22 -7
View File
@@ -203,6 +203,7 @@ def rasterize_pdf_page(
page_dpi: Resolution | None,
rotation: int | None,
filter_vector: bool,
stop_on_soft_error: bool,
) -> Path:
"""Rasterize one page of a PDF at resolution raster_dpi in canvas units.
@@ -213,19 +214,26 @@ def rasterize_pdf_page(
Args:
input_file: The PDF to rasterize.
output_file: The desired name of the rasterized image.
raster_device: Type of image to produce at output_file
raster_dpi: Resolution at which to rasterize page
pageno: Page number to rasterize (beginning at page 1)
page_dpi: Resolution, overriding output image DPI
rotation: Cardinal angle, clockwise, to rotate page
filter_vector: If True, remove vector graphics objects
raster_device: Type of image to produce at output_file.
raster_dpi: Resolution in dots per inch at which to rasterize page.
pageno: Page number to rasterize (beginning at page 1).
page_dpi: Resolution, overriding output image DPI.
rotation: Cardinal angle, clockwise, to rotate page.
filter_vector: If True, remove vector graphics objects.
stop_on_soft_error: If there is an "soft error" such that PDF page image
generation can proceed, but may visually differ from the original,
the implementer of this hook should raise a detailed exception. If
``False``, continue processing and report by logging it. If the hook
cannot proceed, it should always raise an exception, regardless of
this setting. One "soft error" would be a missing font that is
required to properly rasterize the PDF.
Returns:
Path: output_file if successful
Note:
This hook will be called from child processes. Modifying global state
will not affect the main process or other child processes.
Note:
This is a :ref:`firstresult hook<firstresult>`.
"""
@@ -462,6 +470,7 @@ def generate_pdfa(
pdf_version: str,
pdfa_part: str,
progressbar_class,
stop_on_soft_error: bool,
) -> Path:
"""Generate a PDF/A.
@@ -492,6 +501,12 @@ def generate_pdfa(
and the name of the work units ("page"). Then ``instance.update()``
will be called when a work unit is completed. If ``None``, no
progress information is reported.
stop_on_soft_error: If there is an "soft error" such that PDF/A generation
can proceed and produce a valid PDF/A, but output may be invalid or
may not visually resemble the original, the implementer of this hook
should raise a detailed exception. If ``False``, continue processing
and report by logging it. If the hook cannot proceed, it should always
raise an exception, regardless of this setting.
Returns:
Path: If successful, the hook should return ``output_file``.
-2
View File
@@ -118,8 +118,6 @@ def run_polling_stderr(
def _fix_process_args(
args: Args, env: OsEnviron | None, kwargs
) -> tuple[Args, OsEnviron, logging.Logger, bool]:
assert 'universal_newlines' not in kwargs, "Use text= instead of universal_newlines"
if not env:
env = os.environ