0

我想用 PyQt5 QPainterPath 绘制一个 TrueType 字体字形。字形片段示例:(来自 Fonttools ttx 的数据)

<pt x="115" y="255" on="1"/>
<pt x="71" y="255" on="0"/>
<pt x="64" y="244" on="0"/>
<pt x="53" y="213" on="0"/>
<pt x="44" y="180" on="0"/>
<pt x="39" y="166" on="1"/> 

on=0 表示控制点,on=1 表示起点/终点我假设这不会使用 (QPainterPath) quadTo 或cubicTo,因为它是更高阶的曲线。

4

1 回答 1

0

True type 字体实际上只使用二次贝塞尔曲线。这是有道理的,它们是非常简单的曲线,不需要大量计算,当您必须为一个简单的段落绘制成百上千条曲线时,这对性能很有好处。

意识到这一点后,我发现你有一条有 4 个控制点的曲线很奇怪,但后来我做了一些研究,发现了这个有趣的答案

实际上,TrueType 格式允许对始终在每个控制点中间共享每个起点或终点的二次曲线进行分组。

因此,从您的列表开始:

Start  <pt x="115" y="255" on="1"/>
C1     <pt x="71" y="255" on="0"/>
C2     <pt x="64" y="244" on="0"/>
C3     <pt x="53" y="213" on="0"/>
C4     <pt x="44" y="180" on="0"/>
End    <pt x="39" y="166" on="1"/> 

我们有 6 个点,但有 4 条曲线,4 个控制点之间的中间点是曲线上存在的剩余起点/终点

开始 控制 结尾
开始 C1 (C2-C1)/2
(C2-C1)/2 C2 (C3-C2)/2
(C3-C2)/2 C3 (C4-C3)/2
(C4-C3)/2 C4 结尾

为了计算所有这些,我们可以循环遍历这些点并存储对前一个的引用,并且每当我们在它们之后有一个控制点或曲线上的点时,我们就会在路径中添加一条新的二次曲线。

开始 控制 结尾
115×255 71 x 255 67.5 x 249.5
67.5 x 249.5 64×244 58.5 x 228.5
58.5 x 228.5 53×213 48.5 x 106.5
48.5 x 106.5 44×180 39×166

以下代码将创建一个对应于每个<contour>组的 QPainterPath。

path = QtGui.QPainterPath()
currentCurve = []
started = False
for x, y, onCurve in contour:
    point = QtCore.QPointF(x, y)
    if onCurve:
        if not currentCurve:
            # start of curve
            currentCurve.append(point)
        else:
            # end of curve
            start, cp = currentCurve
            path.quadTo(cp, point)
            currentCurve = []
            started = False
    else:
        if len(currentCurve) == 1:
            # control point
            currentCurve.append(point)
        else:
            start, cp = currentCurve
            # find the midpoint
            end = QtCore.QLineF(cp, point).pointAt(.5)
            if not started:
                # first curve of many
                path.moveTo(start)
                started = True
            path.quadTo(cp, end)
            currentCurve = [end, point]
于 2022-01-15T17:49:15.217 回答