Skip to content

Instantly share code, notes, and snippets.

@teknoraver
Created April 7, 2026 00:00
Show Gist options
  • Select an option

  • Save teknoraver/ae21c26a4c22d6e9dd40da45a8d1993e to your computer and use it in GitHub Desktop.

Select an option

Save teknoraver/ae21c26a4c22d6e9dd40da45a8d1993e to your computer and use it in GitHub Desktop.
Convert SVG files to a single multi-page PDF using headless Chrome
#!/usr/bin/env python3
"""Convert SVG files to a single multi-page PDF using headless Chrome."""
import glob
import os
import re
import subprocess
import sys
import tempfile
from PyPDF2 import PdfMerger
CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
SVG_DIR = os.path.dirname(os.path.abspath(__file__))
OUTPUT = os.path.join(SVG_DIR, "AL.pdf")
def get_page_size(svg_content):
"""Extract viewBox dimensions and convert to inches at 96 DPI."""
m = re.search(r'viewBox="0 0 (\d+) (\d+)"', svg_content)
if m:
w = int(m.group(1)) / 96
h = int(m.group(2)) / 96
return w, h
return 8.27, 11.69 # fallback to A4
def create_wrapper(svg_path, html_path):
"""Create an HTML wrapper with zero margins that embeds the SVG inline."""
with open(svg_path, "r") as f:
svg_content = f.read()
w, h = get_page_size(svg_content)
base_url = f"file://{os.path.dirname(os.path.abspath(svg_path))}/"
html = f"""<!DOCTYPE html>
<html>
<head>
<base href="{base_url}">
<style>
@page {{ size: {w}in {h}in; margin: 0; }}
* {{ margin: 0; padding: 0; }}
html, body {{ width: 100%; height: 100%; overflow: hidden; }}
</style>
</head>
<body>
{svg_content}
</body>
</html>"""
with open(html_path, "w") as f:
f.write(html)
def svg_to_pdf(svg_path, pdf_path, tmpdir):
"""Convert a single SVG to PDF using headless Chrome."""
html_path = os.path.join(tmpdir, "wrapper.html")
create_wrapper(svg_path, html_path)
html_url = f"file://{html_path}"
result = subprocess.run(
[
CHROME,
"--headless=new",
"--disable-gpu",
"--no-sandbox",
"--disable-extensions",
"--run-all-compositor-stages-before-draw",
f"--print-to-pdf={pdf_path}",
"--print-to-pdf-no-header",
"--no-pdf-header-footer",
html_url,
],
capture_output=True,
text=True,
)
if result.returncode != 0:
print(f" WARNING: Chrome exited with code {result.returncode}", file=sys.stderr)
return os.path.exists(pdf_path)
def main():
svg_files = sorted(glob.glob(os.path.join(SVG_DIR, "page_*.svg")))
if not svg_files:
print("No page_*.svg files found")
sys.exit(1)
print(f"Found {len(svg_files)} SVG files")
with tempfile.TemporaryDirectory() as tmpdir:
merger = PdfMerger()
for svg_file in svg_files:
basename = os.path.splitext(os.path.basename(svg_file))[0]
pdf_path = os.path.join(tmpdir, f"{basename}.pdf")
print(f"Converting {os.path.basename(svg_file)}...")
if svg_to_pdf(svg_file, pdf_path, tmpdir):
merger.append(pdf_path)
else:
print(f" FAILED: {svg_file}", file=sys.stderr)
print(f"Merging into {OUTPUT}...")
merger.write(OUTPUT)
merger.close()
print(f"Done: {OUTPUT} ({os.path.getsize(OUTPUT) / 1024 / 1024:.1f} MB)")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment