76 lines
2.1 KiB
Python
76 lines
2.1 KiB
Python
import pathlib
|
|
import shutil
|
|
import subprocess
|
|
from io import BufferedReader, BytesIO
|
|
from typing import Literal, Optional, Union
|
|
|
|
T_repair_setting = Literal["default", "prepress", "printer", "ebook", "screen"]
|
|
|
|
|
|
def _repair(
|
|
path_or_fp: Union[str, pathlib.Path, BufferedReader, BytesIO],
|
|
password: Optional[str] = None,
|
|
gs_path: Optional[Union[str, pathlib.Path]] = None,
|
|
setting: T_repair_setting = "default",
|
|
) -> BytesIO:
|
|
|
|
executable = (
|
|
gs_path
|
|
or shutil.which("gs")
|
|
or shutil.which("gswin32c")
|
|
or shutil.which("gswin64c")
|
|
)
|
|
if executable is None: # pragma: nocover
|
|
raise Exception(
|
|
"Cannot find Ghostscript, which is required for repairs.\n"
|
|
"Visit https://www.ghostscript.com/ for installation instructions."
|
|
)
|
|
|
|
repair_args = [
|
|
executable,
|
|
"-sstdout=%stderr",
|
|
"-o",
|
|
"-",
|
|
"-sDEVICE=pdfwrite",
|
|
f"-dPDFSETTINGS=/{setting}",
|
|
]
|
|
|
|
if password:
|
|
repair_args += [f"-sPDFPassword={password}"]
|
|
|
|
if isinstance(path_or_fp, (str, pathlib.Path)):
|
|
stdin = None
|
|
repair_args += [str(pathlib.Path(path_or_fp).absolute())]
|
|
else:
|
|
stdin = path_or_fp
|
|
repair_args += ["-"]
|
|
|
|
proc = subprocess.Popen(
|
|
repair_args,
|
|
stdin=subprocess.PIPE if stdin else None,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
)
|
|
|
|
stdout, stderr = proc.communicate(stdin.read() if stdin else None)
|
|
|
|
if proc.returncode:
|
|
raise Exception(f"{stderr.decode('utf-8')}")
|
|
|
|
return BytesIO(stdout)
|
|
|
|
|
|
def repair(
|
|
path_or_fp: Union[str, pathlib.Path, BufferedReader, BytesIO],
|
|
outfile: Optional[Union[str, pathlib.Path]] = None,
|
|
password: Optional[str] = None,
|
|
gs_path: Optional[Union[str, pathlib.Path]] = None,
|
|
setting: T_repair_setting = "default",
|
|
) -> Optional[BytesIO]:
|
|
repaired = _repair(path_or_fp, password, gs_path=gs_path, setting=setting)
|
|
if outfile:
|
|
with open(outfile, "wb") as f:
|
|
f.write(repaired.read())
|
|
return None
|
|
else:
|
|
return repaired
|