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]