如何在使用svgwrite进行Python时确定文本的宽度和高度?

7 投票
4 回答
6638 浏览
提问于 2025-04-18 10:35

我想要提取文本对象的像素大小(svgwrite.Drawing.Text),也就是它在经过特定样式格式化后在文件中显示的样子。我的字体是等宽字体(Courier New)。

我需要这个信息的原因是,我想把一个字符串打印到SVG文件中,然后在生成的文本上标记一些信息:比如我有一个字符串“ABCDEF”,然后有个外部的专家告诉我“BCD”这一部分需要被标记。接着我需要知道字符“A”和字符“BCD”在两个维度上各占了多少像素(单位),然后在“BCD”部分下画一个彩色矩形、透明框,或者其他什么东西。

所以我有以下代码,我本来希望能用类似于“w = text1.width”这样的方式来提取宽度,但这样并不奏效。谢谢你们提前尝试回答我的问题。

import svgwrite
my_svg = svgwrite.Drawing(filename = "dasha.svg", size = ("800px", "600px"))
text_style = "font-size:%ipx; font-family:%s" % (12, "Courier New") 
text1 = my_svg.text("HELLO WORLD", insert=(0, 0), fill="black", style=text_style)
my_svg.add(text1)
my_svg.save()

更新1 [22.06.2014]:我目前使用的一个临时解决方案是手动在Inkscape中测量特定字体和大小的字母的高度和宽度。我尽力而为,但我不确定这些值是否准确,现在我也不能在程序中更改字体大小。

4 个回答

-1

我对SVG不是很专业,但我了解到在SVG中确定字符和文本的宽度是比较困难的。这也意味着svgwrite无法做到这一点。我所做的就是为每个字符选择一个固定的宽度,并从前一个字符的起始位置开始,分别创建每个字符。你也可以考虑通过使用text_anchor='middle'来让每个字符居中。这样,字符居中在某个点上,就可以在那个点上创建一个居中的框。

另外,你可以查看这个链接 http://www.w3.org/TR/SVG/text.html,里面提到:“在很多情况下,从字符到字形的映射算法是依赖于系统的,这意味着在不同的用户环境中,文本的呈现可能会(通常是稍微)有所不同。如果SVG内容的作者需要精确选择字体和字形,那么建议将所需的字体(可能只包含该文档所需的字形)作为SVG字体嵌入到SVG内容中,或者作为WebFonts([CSS2],第15.1节)发布在与SVG内容相同的网络位置。”

0

我最后选择使用了Tkinter:

try:
    # for Python2
    import Tkinter
except ImportError:
    # for Python3
    import tkinter as Tkinter

try:
    import tkFont
except ImportError:
    import tkinter.font as tkFont

def get_text_metrics(family, size, text):
    # initialize Tk so that font metrics will work
    tk_root = Tkinter.Tk()
    font = None
    font = tkFont.Font(family=family, size=size)
    assert font is not None
    (w, h) = (font.measure(text), font.metrics('linespace'))
    return (w, h)
1

我之前也遇到过同样的问题,不过我用的是可变宽度的字体。我解决这个问题的方法是,先把我想要的文本元素(正确的字体和内容)写入一个svg文件,然后在我电脑上用Inkscape这个软件把它渲染成图像,保存为一个临时的png文件。接着,我读取这个png文件的尺寸(从文件头提取),然后删除临时的svg和png文件,最后用这些尺寸来放置文本和周围的元素。

我发现用90的DPI渲染图像,能得到我需要的准确尺寸,或者说是svgwrite中使用的原始尺寸。-D这个标志是用来确保只渲染可绘制的元素,也就是文本。

os.cmd(/cygdrive/c/Program\ Files\ \(x86\)/Inkscape/inkscape.exe -f work_temp.svg -e work_temp.png -d 90 -D)

我用这些函数来提取png的尺寸,具体可以在这个链接找到,注意我稍微修改了一下以适应python3(仍然可以在python2中使用)

def is_png(data):
    return (data[:8] == b'\x89PNG\r\n\x1a\n'and (data[12:16] == b'IHDR'))

def get_image_info(data):
    if is_png(data):
        w, h = struct.unpack('>LL', data[16:24])
        width = int(w)
        height = int(h)
    else:
        raise Exception('not a png image')
    return width, height

if __name__ == '__main__':
    with open('foo.png', 'rb') as f:
        data = f.read()

    print is_png(data)
    print get_image_info(data)

虽然这个方法有点笨重,但确实有效。

4

我想要在svgwrite中测量特定字体的文本宽度。最后我使用了这个解决方案,来源于http://blog.mathieu-leplatre.info/text-extents-with-python-cairo.html

def textwidth(text, fontsize=14):
    try:
        import cairo
    except:
        return len(text) * fontsize
    surface = cairo.SVGSurface('undefined.svg', 1280, 200)
    cr = cairo.Context(surface)
    cr.select_font_face('Arial', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
    cr.set_font_size(fontsize)
    xbearing, ybearing, width, height, xadvance, yadvance = cr.text_extents(text)
    return width

这个方法需要用到cairo这个库,但这是我找到的最干净、最不依赖平台的解决方案。

撰写回答