'How do I properly convert SVG with custom fonts to PNG with Python and svglib?

I have some SVG designs that I am processing with Python. These designs include custom fonts, which I reference using the at-rule @font-face. The problem seems to be related to the CSS not being parsed in the SVG. If I didn't have to use custom fonts I would just put all of the style data in their respective elements. Seeing as I have to have some CSS to reference fonts, I would like to find a solution where Python can parse this information properly.

When run, I get the following error (apologies for adding a long traceback):

Traceback (most recent call last):
  File "c:\Users\Desktop\programming\project-folders\svg-zone\main.py", line 215, in <module>
    main()
  File "c:\Users\Desktop\programming\project-folders\svg-zone\main.py", line 210, in main
    create_svgs(id, primary_name, alt_name, primary_color, secondary_color)
  File "c:\Users\Desktop\programming\project-folders\svg-zone\main.py", line 105, in create_svgs
    save_svg_as_png(file_name)
  File "c:\Users\Desktop\programming\project-folders\svg-zone\main.py", line 113, in save_svg_as_png
    logo = svg2rlg(r'{}\{}.svg'.format(generated_images, file_name))
  File "c:\Users\Desktop\programming\project-folders\svg-zone\venv\lib\site-packages\svglib\svglib.py", line 1449, in svg2rlg
    drawing = svgRenderer.render(svg_root)
  File "c:\Users\Desktop\programming\project-folders\svg-zone\venv\lib\site-packages\svglib\svglib.py", line 545, in render
    main_group = self.renderSvg(node, outermost=True)
  File "c:\Users\Desktop\programming\project-folders\svg-zone\venv\lib\site-packages\svglib\svglib.py", line 816, in renderSvg
    self.renderG(NodeTracker(def_node))
  File "c:\Users\Desktop\programming\project-folders\svg-zone\venv\lib\site-packages\svglib\svglib.py", line 854, in renderG
    item = self.renderNode(child, parent=gr)
  File "c:\Users\Desktop\programming\project-folders\svg-zone\venv\lib\site-packages\svglib\svglib.py", line 580, in renderNode
    self.renderStyle(n)
  File "c:\Users\Desktop\programming\project-folders\svg-zone\venv\lib\site-packages\svglib\svglib.py", line 864, in renderStyle
    self.attrConverter.css_rules = CSSMatcher(node.text)
  File "c:\Users\Desktop\programming\project-folders\svg-zone\venv\lib\site-packages\svglib\svglib.py", line 131, in __init__
    selectors = cssselect2.compile_selector_list(rule.prelude)
  File "c:\Users\Desktop\programming\project-folders\svg-zone\venv\lib\site-packages\cssselect2\compiler.py", line 31, in compile_selector_list  
    return [
  File "c:\Users\Desktop\programming\project-folders\svg-zone\venv\lib\site-packages\cssselect2\compiler.py", line 31, in <listcomp>
    return [
  File "c:\Users\Desktop\programming\project-folders\svg-zone\venv\lib\site-packages\cssselect2\parser.py", line 38, in parse
    raise exception
  File "c:\Users\Desktop\programming\project-folders\svg-zone\venv\lib\site-packages\cssselect2\parser.py", line 35, in parse
    yield parse_selector(tokens, namespaces)
  File "c:\Users\Desktop\programming\project-folders\svg-zone\venv\lib\site-packages\cssselect2\parser.py", line 56, in parse_selector
    result, pseudo_element = parse_compound_selector(tokens, namespaces)
  File "c:\Users\Desktop\programming\project-folders\svg-zone\venv\lib\site-packages\cssselect2\parser.py", line 92, in parse_compound_selector  
    raise SelectorError(peek, 'expected a compound selector, got %s'
cssselect2.parser.SelectorError: (None, 'expected a compound selector, got EOF')

Here is the SVG I am trying to process:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Design5" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" 
     viewBox="0 0 792 493.2" style="enable-background:new 0 0 792 493.2;" xml:space="preserve">
    <defs>
        <style type="text/css">
            @font-face {
                font-family: 'XXIIDONT-MESS-WITH-VIKINGS-HARDCORE';
                src: url('fonts/XXII DONT MESS WITH VIKINGS.ttf') format('truetype');
            }
            
            @font-face {
                font-family: 'BankGothic-Medium';
                src: url('fonts/bankgthd.ttf') format('truetype');
            }

            .text1 {
                letter-spacing:16;
                font-size:369.1761px;
                font-family:"XXIIDONT-MESS-WITH-VIKINGS-HARDCORE";
            }
            
            .text2 {
                letter-spacing:49;
                font-size:103.7314px;
                font-family:BankGothic-Medium;
            }
            
            .color1 {
                fill: #PrimaryColor;
            }
            
            .color2 {
                fill: #SecondaryColor;
            }
        </style>
    </defs>
    <text id="textLine1-3" textLength='600' lengthAdjust="spacingAndGlyphs" transform="matrix(1.3144 0 0 1 0 330)" class="color1 text1">
        primary_name
    </text>
    <text id="textLine2-1" transform="matrix(0.7966 0 0 1 0 412)" class="color2 text2">
        alt_name
    </text>
</svg>

And here is the Python code that processes the SVG (See svglib documentation):

from svglib.svglib import svg2rlg
from reportlab.graphics import renderPDF, renderPM

logo = svg2rlg(r'{}\{}.svg'.format(generated_images, file_name))
return renderPM.drawToFile(logo, r'{}\{}.png'.format(generated_images, file_name), fmt='PNG')


Solution 1:[1]

I discovered that svglib's dependency, cssselect2, does not handle at-rule CSS parsing (yet). Since I am only using this script locally, I can get away with referencing the font-family in the CSS instead of declaring a font-face.

.text1 {
    letter-spacing:16;
    font-size:369.1761px;
    font-family:"XXII DONT-MESS-WITH-VIKINGS HARDCORE";

To get the fonts to work properly it is essential to install them on the system and name them correctly in your CSS. I verified the names of each custom font I used in my SVGs by referencing the C:\Windows\Fonts directory. If you right click on the font you are referencing and select 'preview', you can see the true name of the font near the top of the window.

I am still having some issues when converting the SVG to a PNG, so it might be worth looking at other SVG packages for Python if you are getting errors after this solution is implemented.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Matthias