Search code examples
pythonlinuxpdfsvgfarsi

How to convert multiple svg files to pdfs


I am working on a project where I need to create a PDF file from multiple SVG files. I decided to create temporary PDF files for each of the SVG files to be combined together later on (maybe using LaTeX). I want the final PDF to be formatted in A4 landscape orientation with specific margins for odd and even pages. Note that there are a very large number of these SVG files and the final product would be used for printing.

These SVG files include:

  • Persian fonts
  • Some texts have gradient
  • External SVG files as background patterns or even images.

I'm searching to find out if I can automate this process using a Python script or some Linux tool. I would really appreciate your help in this specific situation.

I have tried using Cairosvg and Inkscape with no success. There are multiple issues I'm encountering:

  • Using Cairosvg, the Persian fonts are not supported
  • Using Inkscape, the gradient on texts and the external SVG files are not exported (I have tried --export-area-drawing too, no success)

I used this Python script but encountered some problems:

svg_files = [f for f in os.listdir(svg_dir) if f.endswith(".svg")]

for svg_file in svg_files:
    base_name = os.path.splitext(svg_file)[0]
    svg_path = os.path.join(svg_dir, svg_file)
    pdf_path = os.path.join(temp_pdf_dir, f"{base_name}.pdf")

    subprocess.run(["rsvg-convert", "-f", "pdf", "-o", pdf_path, svg_path])

pdf_files = [
    os.path.join(temp_pdf_dir, f)
    for f in os.listdir(temp_pdf_dir)
    if f.endswith(".pdf")
]
subprocess.run(["pdftk"] + pdf_files + ["cat", "output", final_pdf_path])

One issue is that none of the fonts (either the Persian fonts of English fonts) are not applied to the output. Another issue was the images I had used.

Here are some examples of how I have used custom fonts, gradients, and images in my SVG file:

<svg width="273mm" height="165mm" xmlns="http://www.w3.org/2000/svg">
    <style>
        @font-face {
            font-family: "Gungsuh W33 Regular";
            src: url("/path/fonts/Gungsuh W33 Regular.woff") format("woff");
        }
    </style>
    <defs>
        <pattern id="imagePattern" patternUnits="userSpaceOnUse" width="91mm" height="55mm">
            <image href="/path/image.svg" x="0" y="0" width="91mm" height="55mm" />
        </pattern>
        
        <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%">
            <stop offset="0%" style="stop-color:rgb(220,50,50); stop-opacity:1" />
            <stop offset="100%" style="stop-color:rgb(62,62,150); stop-opacity:1" />
        </linearGradient>
    </defs>
...
    <rect x="182mm" y="0mm" width="91mm" height="55mm" fill="url(#imagePattern)" />
    <text x="46mm" y="67.25mm" font-family="Gungsuh W33 Regular" font-size="10" letter-spacing="2.5" fill="url(#gradient)">
</svg>

Solution

  • I think possible approach would be to simply create a html file and create 1 or 2 pages of your preference and try to print it with chrome's built in print to check if styling is of your preference, you can use CSS for margin and if those pages you seems to be your liking then you can use JS to make this pages dynamic with only svgs getting updated. put these in single html file and i think as for your end product, there are many python library which can convert these html files into pdfs. This is best solution if something doesn't support in anything. you can use pyppeteer python library and arrange it. Here is the demo of how code will look like:

    import asyncio
    from pyppeteer import launch
    
    async def generate_pdf(html_file, pdf_output):
        browser = await launch()
        page = await browser.newPage()
        
        # Load your HTML file (local or remote)
        await page.goto(f'file://{html_file}', {'waitUntil': 'networkidle0'})
        
        # Generate PDF with specific settings
        await page.pdf({
            'path': pdf_output,
            'format': 'A4',
            'landscape': True,
            'printBackground': True,  # Ensures background images and colors are included
            'margin': {
                'top': '20mm',
                'bottom': '20mm',
                'left': '25mm',
                'right': '25mm'
            }
        })
        
        await browser.close()
    
    # Run the script
    html_path = '/path/to/your/input.html'
    output_pdf = '/path/to/your/output.pdf'
    
    asyncio.get_event_loop().run_until_complete(generate_pdf(html_path, output_pdf))
    

    Here you don't need to create multiple html files but automate single html file which can end up printing multiple pages in single pdf.