StrictDoc Documentation
strictdoc/features/html2pdf/pdf_print_driver.py
Source file coverage
Path:
strictdoc/features/html2pdf/pdf_print_driver.py
Lines:
104
Non-empty lines:
88
Non-empty lines covered with requirements:
88 / 88 (100.0%)
Functions:
8
Functions covered by requirements:
8 / 8 (100.0%)
1
"""
2
@relation(SDOC-SRS-51, scope=file)
3
"""
4
 
5
import os.path
6
from subprocess import CalledProcessError, CompletedProcess, TimeoutExpired, run
7
from typing import List, Tuple
8
 
9
from html2pdf4doc.main import HPDExitCode
10
 
11
from strictdoc.core.project_config import ProjectConfig
12
from strictdoc.features.html2pdf.pdf_postprocessor import PDFPostprocessor
13
from strictdoc.helpers.timing import measure_performance
14
 
15
 
16
class PDFPrintDriverException(Exception):
17
    def __init__(self, exception: Exception):
18
        super().__init__()
19
        self.exception: Exception = exception
20
 
21
    def get_server_user_message(self) -> str:
22
        """
23
        Provide a user-friendly message that describes the underlying exception/error.
24
        """
25
 
26
        if self.is_could_not_detect_chrome():
27
            return "HTML2PDF could not detect an existing Chrome installation."
28
 
29
        if self.is_timeout_error():
30
            return "HTML2PDF timeout error."
31
 
32
        if self.is_js_success_timeout():
33
            return "HTML2PDF.js success timeout error."
34
 
35
        return "HTML2PDF internal error."
36
 
37
    def is_timeout_error(self) -> bool:
38
        return isinstance(self.exception, TimeoutExpired)
39
 
40
    def is_could_not_detect_chrome(self) -> bool:
41
        return (
42
            isinstance(self.exception, CalledProcessError)
43
            and self.exception.returncode == HPDExitCode.COULD_NOT_FIND_CHROME
44
        )
45
 
46
    def is_js_success_timeout(self) -> bool:
47
        return (
48
            isinstance(self.exception, CalledProcessError)
49
            and self.exception.returncode
50
            == HPDExitCode.DID_NOT_RECEIVE_SUCCESS_STATUS_FROM_HTML2PDF4DOC_JS
51
        )
52
 
53
 
54
class PDFPrintDriver:
55
    @staticmethod
56
    def get_pdf_from_html(
57
        project_config: ProjectConfig,
58
        paths_to_print: List[Tuple[str, str]],
59
        path_to_input_root: str,
60
    ) -> None:
61
        assert isinstance(paths_to_print, list), paths_to_print
62
        path_to_html2pdf4doc_cache = os.path.join(
63
            project_config.get_path_to_cache_dir(), "html2pdf"
64
        )
65
        cmd: List[str] = [
66
            # Using sys.executable instead of "python" is important because
67
            # venv subprocess call to python resolves to wrong interpreter,
68
            # https://github.com/python/cpython/issues/86207
69
            # Switching back to calling html2pdf4doc directly because the
70
            # python -m doesn't work well with PyInstaller.
71
            # sys.executable, "-m"
72
            "html2pdf4doc",
73
            "print",
74
            "--cache-dir",
75
            path_to_html2pdf4doc_cache,
76
        ]
77
        if project_config.chromedriver is not None:
78
            cmd.extend(
79
                [
80
                    "--chromedriver",
81
                    project_config.chromedriver,
82
                ]
83
            )
84
        if project_config.html2pdf_strict:
85
            cmd.append("--strict")
86
        for path_to_print_ in paths_to_print:
87
            cmd.append(path_to_print_[0])
88
            cmd.append(path_to_print_[1])
89
 
90
        with measure_performance(
91
            "PDFPrintDriver: printing HTML to PDF using HTML2PDF and Chrome Driver"
92
        ):
93
            try:
94
                _: CompletedProcess[bytes] = run(
95
                    cmd,
96
                    capture_output=False,
97
                    check=True,
98
                )
99
                PDFPostprocessor.rewrite_cross_document_links(
100
                    path_to_input_root=path_to_input_root,
101
                    paths_to_print=paths_to_print,
102
                )
103
            except Exception as e_:
104
                raise PDFPrintDriverException(e_) from e_