'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 |