在Python中将SVG转换为PNG并使用自定义字体
我正在使用基于Cairo/RSVG的方案将SVG格式转换为PNG格式。这个方法在StackOverflow上已经有介绍,具体可以查看如何在Python中将SVG转换为PNG。
不过,这个方案似乎对自定义字体不太有效。
我找到了一篇关于嵌入SVG字体的页面。
我尝试通过XLink从外部SVG文件中引入字体,按照示例中的做法。我也试过直接在同一个SVG文件中嵌入字体。尝试这些方法都没有成功,然后我又试了CSS Web Fonts的语法。无论哪种方法,在使用Cairo渲染时都没有效果(在Ubuntu的默认查看器Eye of GNOME中也不行)。
我还尝试了ImageMagick,结果和Cairo一样。
另一方面,使用这三种字体嵌入方法时,在WebKit中字体显示得很好,无论是用Google Chrome还是webkit2png.py。不过,如果可以的话,我希望在服务器上避免使用QT WebKit,因为它的设置比较复杂(包括xvfb等),而且我担心这样做可能效率不高,也不够稳定。
有没有其他方法可以在Python中将SVG转换为PNG呢?
5 个回答
你可以试试使用inkscape,也许这样能得到更好的效果:
inkscape inputfile.svg --export-png=exportfile.png
从Python运行这个的方式可以在这里找到:在Python中调用外部命令
这是我在AWS S3上加载自定义字体文件的方法(不过在这个例子中,我使用的是本地文件系统)——请注意,我使用的都是woff格式的字体。
如果你的所有字体都可以通过fontTools中的TTFont
类加载,你可以这样做——这个库也可以将不同的字体转换成其他字体:
from fontTools.ttLib import TTFont
from pathlib import Path
import xml.etree.ElementTree as ET
from io import BytesIO
from reportlab.graphics import renderPM
import svglib.fonts
from svglib.svglib import svg2rlg
def to_pdf_buffer(d, *args, **kwargs):
buffer = BytesIO()
renderPDF.drawToFile(d, buffer, *args, **kwargs)
return buffer
def to_pil(d, *args, **kwargs):
pil = renderPM.drawToPIL(d, *args, **kwargs)
return pil
ET.register_namespace('', "http://www.w3.org/2000/svg")
file_path = Path("my_svg.svg")
root = ET.parse(str(file_path)).getroot()
font_map = svglib.fonts.get_global_font_map()
fonts = []
unloaded_fonts = []
Folder = "./" # folder to where your fonts are
for e in self.root.findall(".//{http://www.w3.org/2000/svg}text"):
font_family_name = e.get('font-family')
if font_family_name is not None and font_family_name not in fonts:
fonts.append(font_family_name)
path = Path(Folder)
if path.exists() and path.is_dir():
file = path.joinpath(Path(f"./{font_family_name}.woff"))
data = file.read_bytes()
try:
TTFont(data)
except:
unloaded_fonts.append(font_family_name)
continue
else:
try:
font = BytesIO(file.read_bytes())
font_map.register_font(font_family_name, font_path=font)
except:
unloaded_fonts.append(font_family_name)
if len(unloaded_fonts) > 0:
print(f"Could not load fonts: {', '.join(unloaded_fonts)}")
drawing = svg2rlg(BytesIO(ET.tostring(root)), font_map=font_map)
pil = to_pil(drawing, dpi=1200)
pil.save(str(file_path.with_suffix(".png")))
svglib
有一个字体映射,可以通过调用svglib.get_global_font_map()
函数来获取。你可以使用这个字体映射的方法register_fonts
来加载字体,这个方法需要一个字体名称和一个路径或数据(具体可以在他们的文档中找到——你不能同时提供这两者,或者是类似的限制)。
这个过程使用了ElementTree库来解析文本元素中的字体系列标签。它会获取字体的名称,并找到一个与该字体系列名称匹配的字体文件并加载它,这对我的应用程序有效,但对其他应用程序可能不适用,因为通常你是通过css的@font-face来加载字体。为了让@font-face选项有效,你需要解析它并获取相关数据,然后将这些字体注册到字体映射中。这并不是特别困难,但我现在确实有点懒,而且注意力被转移到其他问题上,所以我没有动力去做,但这些信息足够为其他开发者提供基础,继续进行开发。
再强调一下:要让这个工作正常,你的文本必须有一个字体系列,并且你的字体文件名必须与这些字体系列相匹配;另外,我的字体都是woff格式的,所以你可能需要更改文件扩展名。
编辑:
我认为一个主要的缺点是,svglib不支持SVG 2,这也是我最终放弃这个解决方案的部分原因。我仍在为SVG 2开发其他方案。
我花了一周的时间研究这个问题,最后得出的结论是,处理服务器端渲染或转换SVG图像时,最好的办法是把自定义字体安装在服务器上。我尝试过的一些工具(比如rsvg、imagemagick、phantomjs、qtwebkit等)都无法处理网络字体和SVG字体。
谷歌提供了几百种字体(而且还在不断增加),可以下载并在服务器上使用。
- 下载OTF或TTF格式的字体
- 把这些字体安装到服务器上,并刷新字体缓存
- 在你的SVG文档中,把外部字体的CSS定义替换为字体家族名称。
如果你还想在网页中使用这些相同的字体,可以直接链接到谷歌的CDN来获取WOFF文件,这样可以节省你自己服务器的时间和网络带宽。