在 Python 中使用自定义字体将 SVG 转换为 PNG

问题描述 投票:0回答:5

我正在使用基于 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?

python svg png librsvg
5个回答
4
投票

我花了一周时间研究这个问题,并得出结论,使用自定义字体处理 SVG 的服务器端渲染/光栅化的最佳方法是在服务器上安装这些字体。我尝试过的工具(rsvg、imagemagick、phantomjs、qtwebkit...)无法处理 Web 字体和 svg 字体。

Google 拥有 数百种字体(并且还在不断增加),可以在服务器上下载和使用。

  • 下载 OTF 或 TTF 字体
  • 将它们安装在服务器上并刷新字体缓存
  • 将外部字体的 CSS 定义替换为 SVG 文档中的字体系列名称。

如果您还需要在网页中使用相同的字体,您可以直接链接到 Google CDN 以获取 WOFF 文件,以节省您自己的服务器时间和网络带宽。


0
投票

你可以尝试使用inkscape,也许这会给你更好的结果:

 inkscape inputfile.svg --export-png=exportfile.png

从 python 运行此命令的描述如下:在 Python 中调用外部命令


0
投票

使用 Imagemagick,我仍然很难使用服务器上安装的字体进行 svg 光栅化,并且可以在某些操作中使用,但在使用 -convert from .svg to .png 时失败......它似乎将每种类型的文本都变成宋体。我认为这可能是 ImageMagick 的错误或 .svg

中所需的某种格式

0
投票

需要通过 RSVG 检查一些事项:

  • 字体源文件位于您的系统或用户字体路径中
  • SVG 中的字体名称不带引号,即使它们包含空格。 librsvg 将假定引号是字体名称的一部分,并且它不会找到源文件。

0
投票

如果您的所有字体都可以通过 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 选项起作用,您必须解析它并获取它的数据并将这些字体注册到字体映射中。这并不是非常困难,但我绝对太懒了,而且此时我的注意力转移到了另一个问题上,所以我没有意志力去做这件事,但这对于另一个构建者的基础来说已经足够了来并以此为基础。

只是想再次澄清:要使这项工作有效,您的文本必须有一个字体系列,并且您的字体文件必须是这些字体系列的名称。

© www.soinside.com 2019 - 2024. All rights reserved.