You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中Pillow渲染Font Awesome图标显示空方块的解决方法

问题

我正在解析包含Font Awesome图标的HTML页面,示例图标映射如下:

FA_ICONS = {
    "fa-calendar": "📅",
    "fa-times-circle": "✖",
    "fa-check-circle": "✔",
    "fa-info-circle": "ℹ",
    "fa-exclamation-circle": "❗",
    "fa-exclamation-triangle": "⚠",
    "fa-lightbulb-o": "💡",
    "fa-bolt": "⚡",
    #.. etc 
}

需要在无需用户安装系统字体的前提下,使用Pillow将这些图标渲染为PNG图片。我尝试把图标通过Font Awesome Unicode字符转换为带<text>元素的SVG,再用cairosvg渲染,但输出结果是空白方块(⯀),Windows/Linux/macOS多系统都存在这个问题。我需要可无像素化缩放的矢量级图标,且要支持全部Font Awesome 12图标,而非仅含emoji等效项的子集。基于当前技术栈(Pillow、cairosvg),如何正确将Font Awesome图标渲染为PNG图片?

相关代码:

from PIL import Image
import cairosvg
import io

def render_fa_icon(icon_unicode: str = "\uf005") -> io.BytesIO:
    """Render Font Awesome icon by Unicode character"""
    svg = f'''
    <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg">
        <text x="12" y="18" font-family="Font Awesome 6 Free" font-weight="900"
              font-size="20" text-anchor="middle" fill="black">{icon_unicode}</text>
    </svg>
    '''
    png_bytes = cairosvg.svg2png(bytestring=svg.encode('utf-8'))
    img = Image.open(io.BytesIO(png_bytes))
    img.show()  # Shows empty square
    img_io = io.BytesIO()
    img.save(img_io, format='PNG')
    img_io.seek(0)
    return img_io

# 输出空白方块
render_fa_icon("\uf005")  # fa-home对应的Unicode

环境信息:

Environment:
- OS: Windows 11 / Ubuntu 22.04
- Python: 3.11.5
- Pillow: 10.1.0
- cairosvg: 2.7.0
解决方案

空白方块的核心原因是cairosvg找不到Font Awesome字体文件——SVG里指定了字体,但系统未安装,也没在SVG中内嵌或指定字体文件路径,导致渲染器无法解析Unicode对应的字形。

要实现无需系统安装字体、矢量级渲染的需求,按以下步骤修改:

1. 准备Font Awesome字体文件

从Font Awesome官方包中提取对应风格的字体文件(比如Font Awesome 6 Free-Solid-900.otf,对应代码里的font-weight:900),将其放在Python脚本可访问的路径下(如脚本同目录)。

2. 修改SVG渲染逻辑

有两种可靠方式让cairosvg识别字体:

方式一:内嵌字体到SVG(推荐,无外部文件依赖)

先将字体文件转换为Base64编码,再内嵌到SVG的<style>标签中:

import base64
from PIL import Image
import cairosvg
import io

def get_font_base64(font_path):
    with open(font_path, 'rb') as f:
        return base64.b64encode(f.read()).decode('utf-8')

def render_fa_icon(icon_unicode: str = "\uf005", font_path="Font Awesome 6 Free-Solid-900.otf") -> io.BytesIO:
    font_base64 = get_font_base64(font_path)
    svg = f'''
    <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg">
        <style>
            @font-face {{
                font-family: 'Font Awesome 6 Free';
                font-weight: 900;
                src: url(data:font/otf;base64,{font_base64}) format('opentype');
            }}
        </style>
        <text x="12" y="18" font-family="Font Awesome 6 Free" font-weight="900"
              font-size="20" text-anchor="middle" fill="black">{icon_unicode}</text>
    </svg>
    '''
    png_bytes = cairosvg.svg2png(bytestring=svg.encode('utf-8'))
    img = Image.open(io.BytesIO(png_bytes))
    img.show()
    img_io = io.BytesIO()
    img.save(img_io, format='PNG')
    img_io.seek(0)
    return img_io

方式二:指定cairosvg的字体搜索目录

如果不想内嵌字体,可通过font_config参数指定字体所在目录:

from PIL import Image
import cairosvg
from cairosvg import FontConfig
import io

def render_fa_icon(icon_unicode: str = "\uf005", font_dir="./fonts") -> io.BytesIO:
    font_config = FontConfig()
    font_config.fonts_dirs.append(font_dir)  # 存放Font Awesome字体的目录
    
    svg = f'''
    <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg">
        <text x="12" y="18" font-family="Font Awesome 6 Free" font-weight="900"
              font-size="20" text-anchor="middle" fill="black">{icon_unicode}</text>
    </svg>
    '''
    png_bytes = cairosvg.svg2png(bytestring=svg.encode('utf-8'), font_config=font_config)
    img = Image.open(io.BytesIO(png_bytes))
    img.show()
    img_io = io.BytesIO()
    img.save(img_io, format='PNG')
    img_io.seek(0)
    return img_io

3. 适配与验证

  • 确保Unicode字符和字体文件匹配:比如fa-home(\uf005)属于Solid风格,对应Font Awesome 6 Free-Solid-900.otf;若使用Regular风格图标,需替换为Font Awesome 6 Free-Regular-400.otf,同时修改font-weight为400。
  • 微调SVG的x、y坐标和font-size,让图标在画布中居中(Font Awesome字形基线与普通文本不同,可能需要调整y值)。

更稳定的替代方案:直接渲染Font Awesome原始SVG

若能获取Font Awesome官方包中的原始SVG图标文件(位于svgs目录下),可直接用cairosvg渲染,无需处理字体问题:

from PIL import Image
import cairosvg
import io

def render_fa_svg(svg_path: str) -> io.BytesIO:
    with open(svg_path, 'rb') as f:
        png_bytes = cairosvg.svg2png(file_obj=f)
    img = Image.open(io.BytesIO(png_bytes))
    img.show()
    img_io = io.BytesIO()
    img.save(img_io, format='PNG')
    img_io.seek(0)
    return img_io

# 示例:渲染fa-home的SVG文件
render_fa_svg("svgs/solid/home.svg")

这种方式直接使用矢量图形,无需依赖字体渲染,且支持所有Font Awesome图标,稳定性更高。

内容的提问来源于stack exchange,提问作者Versst

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 19:17:26