diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml index 8cf40f1..88047fe 100644 --- a/.github/workflows/ci-windows.yml +++ b/.github/workflows/ci-windows.yml @@ -66,6 +66,10 @@ jobs: run: | invoke build + - name: Download ChromeDriver + run: | + python hpdf/hpdf.py get_driver + - name: Run tests (Bash) run: | invoke test diff --git a/hpdf/hpdf.py b/hpdf/hpdf.py index 080dbaa..c908781 100644 --- a/hpdf/hpdf.py +++ b/hpdf/hpdf.py @@ -25,6 +25,8 @@ __version__ = "0.0.1" +DEFAULT_CACHE_DIR = os.path.join(Path.home(), ".hpdf", "chromedriver") + # HTML2PDF.js prints unicode symbols to console. The following makes it work on # Windows which otherwise complains: # UnicodeEncodeError: 'charmap' codec can't encode characters in position 129-130: character maps to @@ -35,9 +37,6 @@ class HTML2PDF_HTTPClient(HttpClient): def get(self, url, params=None, **kwargs) -> Response: - """ - Add you own logic here like session or proxy etc. - """ last_error: Optional[Exception] = None for attempt in range(1, 3): print( # noqa: T201 @@ -147,9 +146,6 @@ def get_pdf_from_html(driver, url) -> bytes: "marginRight": get_inches_from_millimeters(21), } - print("html2pdf: executing print command with ChromeDriver.") # noqa: T201 - result = driver.execute_cdp_cmd("Page.printToPDF", calculated_print_options) - class Done(Exception): pass @@ -180,25 +176,33 @@ class Done(Exception): print(entry) # noqa: T201 print('"""') # noqa: T201 + # + # Execute Print command with ChromeDriver. + # + print("html2pdf: executing print command with ChromeDriver.") # noqa: T201 + result = driver.execute_cdp_cmd("Page.printToPDF", calculated_print_options) + data = base64.b64decode(result["data"]) return data +def get_chrome_driver(path_to_cache_dir: str) -> str: + cache_manager = HTML2PDF_CacheManager( + file_manager=FileManager(os_system_manager=OperationSystemManager()), + path_to_cache_dir=path_to_cache_dir, + ) + + http_client = HTML2PDF_HTTPClient() + download_manager = WDMDownloadManager(http_client) + path_to_chrome = ChromeDriverManager( + download_manager=download_manager, cache_manager=cache_manager + ).install() + return path_to_chrome + + def create_webdriver(chromedriver: Optional[str], path_to_cache_dir: str): - print("html2pdf: creating ChromeDriver service.", flush=True) # noqa: T201 if chromedriver is None: - cache_manager = HTML2PDF_CacheManager( - file_manager=FileManager( - os_system_manager=OperationSystemManager() - ), - path_to_cache_dir=path_to_cache_dir, - ) - - http_client = HTML2PDF_HTTPClient() - download_manager = WDMDownloadManager(http_client) - path_to_chrome = ChromeDriverManager( - download_manager=download_manager, cache_manager=cache_manager - ).install() + path_to_chrome = get_chrome_driver(path_to_cache_dir) else: path_to_chrome = chromedriver print(f"html2pdf: ChromeDriver available at path: {path_to_chrome}") # noqa: T201 @@ -236,50 +240,99 @@ def main(): os.environ["WDM_LOCAL"] = "1" parser = argparse.ArgumentParser(description="HTML2PDF printer script.") + parser.add_argument( + "-v", "--version", action="version", version=__version__ + ) + + command_subparsers = parser.add_subparsers(title="command", dest="command") + command_subparsers.required = True + + # + # Get driver command. + # + command_parser_get_driver = command_subparsers.add_parser( + "get_driver", + help="Check if ChromeDriver already exists locally. If not, download it.", + description="", + ) + command_parser_get_driver.add_argument( + "--cache-dir", + type=str, + help="Optional path to a cache directory whereto the ChromeDriver is downloaded.", + ) + + # + # Print command. + # + command_parser_print = command_subparsers.add_parser( + "print", + help="Main print command", + description="", + ) + command_parser_print.add_argument( "--chromedriver", type=str, help="Optional chromedriver path. Downloaded if not given.", ) - parser.add_argument( + command_parser_print.add_argument( "--cache-dir", type=str, help="Optional path to a cache directory whereto the ChromeDriver is downloaded.", ) - parser.add_argument("paths", nargs="+", help="Paths to input HTML file.") + command_parser_print.add_argument( + "paths", nargs="+", help="Paths to input HTML file." + ) + args = parser.parse_args() - paths: List[str] = args.paths + path_to_cache_dir: str + if args.command == "get_driver": + path_to_cache_dir = ( + args.cache_dir + if args.cache_dir is not None + else (DEFAULT_CACHE_DIR) + ) - path_to_cache_dir: str = ( - args.cache_dir - if args.cache_dir is not None - else (os.path.join(Path.home(), ".hpdf", "chromedriver")) - ) - driver = create_webdriver(args.chromedriver, path_to_cache_dir) + path_to_chrome = get_chrome_driver(path_to_cache_dir) + print(f"html2pdf: ChromeDriver available at path: {path_to_chrome}") # noqa: T201 + sys.exit(0) - @atexit.register - def exit_handler(): - print("html2pdf: exit handler: quitting the ChromeDriver.") # noqa: T201 - driver.quit() + elif args.command == "print": + paths: List[str] = args.paths - assert len(paths) % 2 == 0, ( - f"Expecting an even number of input/output path arguments: {paths}." - ) - for current_pair_idx in range(0, 2, len(paths)): - path_to_input_html = paths[current_pair_idx] - path_to_output_pdf = paths[current_pair_idx + 1] + path_to_cache_dir = ( + args.cache_dir + if args.cache_dir is not None + else (DEFAULT_CACHE_DIR) + ) + driver = create_webdriver(args.chromedriver, path_to_cache_dir) - assert os.path.isfile(path_to_input_html), path_to_input_html + @atexit.register + def exit_handler(): + print("html2pdf: exit handler: quitting the ChromeDriver.") # noqa: T201 + driver.quit() - path_to_output_pdf_dir = os.path.dirname(path_to_output_pdf) - Path(path_to_output_pdf_dir).mkdir(parents=True, exist_ok=True) + assert len(paths) % 2 == 0, ( + f"Expecting an even number of input/output path arguments: {paths}." + ) + for current_pair_idx in range(0, 2, len(paths)): + path_to_input_html = paths[current_pair_idx] + path_to_output_pdf = paths[current_pair_idx + 1] - url = Path(os.path.abspath(path_to_input_html)).as_uri() + assert os.path.isfile(path_to_input_html), path_to_input_html - pdf_bytes = get_pdf_from_html(driver, url) - with open(path_to_output_pdf, "wb") as f: - f.write(pdf_bytes) + path_to_output_pdf_dir = os.path.dirname(path_to_output_pdf) + Path(path_to_output_pdf_dir).mkdir(parents=True, exist_ok=True) + + url = Path(os.path.abspath(path_to_input_html)).as_uri() + + pdf_bytes = get_pdf_from_html(driver, url) + with open(path_to_output_pdf, "wb") as f: + f.write(pdf_bytes) + else: + print("html2pdf: unknown command.") # noqa: T201 + sys.exit(1) if __name__ == "__main__": diff --git a/tasks.py b/tasks.py index 828b9bb..423fe63 100644 --- a/tasks.py +++ b/tasks.py @@ -77,6 +77,18 @@ def format_readme(context): ) +@task +def get_chrome_driver( + context, +): + run_invoke( + context, + """ + python hpdf/hpdf.py get_driver + """, + ) + + @task def lint_ruff_format(context): result: invoke.runners.Result = run_invoke( @@ -132,7 +144,7 @@ def lint(context): lint_mypy(context) -@task +@task(aliases=["ti"]) def test_integration( context, focus=None, @@ -140,6 +152,8 @@ def test_integration( ): clean_itest_artifacts(context) + get_chrome_driver(context) + cwd = os.getcwd() html2pdf_exec = f'python3 \\"{cwd}/hpdf/hpdf.py\\"' @@ -163,7 +177,7 @@ def test_integration( run_invoke(context, itest_command) -@task +@task(aliases=["t"]) def test(context): test_integration(context) diff --git a/tests/integration/01_hello_world/test.itest b/tests/integration/01_hello_world/test.itest index 94fd0d7..e978a11 100644 --- a/tests/integration/01_hello_world/test.itest +++ b/tests/integration/01_hello_world/test.itest @@ -1,4 +1,4 @@ -RUN: %html2pdf %S/index.html %S/Output/index.pdf +RUN: %html2pdf print %S/index.html %S/Output/index.pdf RUN: %check_exists --file "%S/Output/index.pdf" RUN: python %S/test.py diff --git a/tests/integration/02_two_pages/test.itest b/tests/integration/02_two_pages/test.itest index 94fd0d7..e978a11 100644 --- a/tests/integration/02_two_pages/test.itest +++ b/tests/integration/02_two_pages/test.itest @@ -1,4 +1,4 @@ -RUN: %html2pdf %S/index.html %S/Output/index.pdf +RUN: %html2pdf print %S/index.html %S/Output/index.pdf RUN: %check_exists --file "%S/Output/index.pdf" RUN: python %S/test.py diff --git a/tests/integration/03_cache_dir_argument/test.itest b/tests/integration/03_cache_dir_argument/test.itest index f22469f..4da85d4 100644 --- a/tests/integration/03_cache_dir_argument/test.itest +++ b/tests/integration/03_cache_dir_argument/test.itest @@ -1,10 +1,10 @@ -RUN: %html2pdf --cache-dir %S/Output/cache %S/index.html %S/Output/index.pdf | filecheck %s --dump-input=fail --check-prefix CHECK-RUN1 +RUN: %html2pdf print --cache-dir %S/Output/cache %S/index.html %S/Output/index.pdf | filecheck %s --dump-input=fail --check-prefix CHECK-RUN1 RUN: %check_exists --file "%S/Output/index.pdf" RUN: python %S/test.py CHECK-RUN1: html2pdf: ChromeDriver does not exist in the local cache: -RUN: %html2pdf --cache-dir %S/Output/cache %S/index.html %S/Output/index.pdf | filecheck %s --dump-input=fail --check-prefix CHECK-RUN2 +RUN: %html2pdf print --cache-dir %S/Output/cache %S/index.html %S/Output/index.pdf | filecheck %s --dump-input=fail --check-prefix CHECK-RUN2 RUN: %check_exists --file "%S/Output/index.pdf" RUN: python %S/test.py