Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
143 changes: 98 additions & 45 deletions hpdf/hpdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <undefined>
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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__":
Expand Down
18 changes: 16 additions & 2 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -132,14 +144,16 @@ def lint(context):
lint_mypy(context)


@task
@task(aliases=["ti"])
def test_integration(
context,
focus=None,
debug=False,
):
clean_itest_artifacts(context)

get_chrome_driver(context)

cwd = os.getcwd()

html2pdf_exec = f'python3 \\"{cwd}/hpdf/hpdf.py\\"'
Expand All @@ -163,7 +177,7 @@ def test_integration(
run_invoke(context, itest_command)


@task
@task(aliases=["t"])
def test(context):
test_integration(context)

Expand Down
2 changes: 1 addition & 1 deletion tests/integration/01_hello_world/test.itest
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion tests/integration/02_two_pages/test.itest
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions tests/integration/03_cache_dir_argument/test.itest
Original file line number Diff line number Diff line change
@@ -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

Expand Down