我正在使用基于 Cairo/RSVG 的解决方案将 SVG 光栅化为 PNG。它已经在 StackOverflow 上的 Convert SVG to PNG in Python 中进行了描述。 但是,该解决方案似乎不适用于自定义字体。
我发现此页面描述了嵌入 SVG 字体。
我尝试通过 XLink 从外部 SVG 包含它们,如示例中所述。我尝试过将字体直接嵌入到同一个 SVG 文件中。失败后,我尝试了 CSS Web Fonts 语法。使用 Cairo 渲染时,这 3 种方法都不起作用(在 Ubuntu 的默认查看器 Eye of GNOME 中也不起作用)。
我尝试过 ImageMagick,结果与 Cairo 完全相同。
另一方面,使用所有 3 种字体嵌入方法,无论使用 Google Chrome 或 webkit2png.py,字体在 WebKit 中都呈现得很好。但是,如果可能的话,我想避免在服务器上使用 QT WebKit,因为它是不简单的设置(包括 xvfb 等),而且我担心这可能不会非常有效或稳定。
有没有其他方法可以从 Python 将 SVG 渲染为 PNG?
我花了一周时间研究这个问题,并得出结论,使用自定义字体处理 SVG 的服务器端渲染/光栅化的最佳方法是在服务器上安装这些字体。我尝试过的工具(rsvg、imagemagick、phantomjs、qtwebkit...)无法处理 Web 字体和 svg 字体。
Google 拥有 数百种字体(并且还在不断增加),可以在服务器上下载和使用。
如果您还需要在网页中使用相同的字体,您可以直接链接到 Google CDN 以获取 WOFF 文件,以节省您自己的服务器时间和网络带宽。
你可以尝试使用inkscape,也许这会给你更好的结果:
inkscape inputfile.svg --export-png=exportfile.png
从 python 运行此命令的描述如下:在 Python 中调用外部命令
使用 Imagemagick,我仍然很难使用服务器上安装的字体进行 svg 光栅化,并且可以在某些操作中使用,但在使用 -convert from .svg to .png 时失败......它似乎将每种类型的文本都变成宋体。我认为这可能是 ImageMagick 的错误或 .svg
中所需的某种格式需要通过 RSVG 检查一些事项:
如果您的所有字体都可以通过 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)
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 选项起作用,您必须解析它并获取它的数据并将这些字体注册到字体映射中。这并不是非常困难,但我绝对太懒了,而且此时我的注意力转移到了另一个问题上,所以我没有意志力去做这件事,但这对于另一个构建者的基础来说已经足够了来并以此为基础。
只是想再次澄清:要使这项工作有效,您的文本必须有一个字体系列,并且您的字体文件必须是这些字体系列的名称。