-
-
Save CatherineH/499a312a04582a00e7559ac0c8f133fa to your computer and use it in GitHub Desktop.
from svgpathtools import wsvg, Line, QuadraticBezier, Path | |
from freetype import Face | |
def tuple_to_imag(t): | |
return t[0] + t[1] * 1j | |
face = Face('./Vera.ttf') | |
face.set_char_size(48 * 64) | |
face.load_char('a') | |
outline = face.glyph.outline | |
y = [t[1] for t in outline.points] | |
# flip the points | |
outline_points = [(p[0], max(y) - p[1]) for p in outline.points] | |
start, end = 0, 0 | |
paths = [] | |
for i in range(len(outline.contours)): | |
end = outline.contours[i] | |
points = outline_points[start:end + 1] | |
points.append(points[0]) | |
tags = outline.tags[start:end + 1] | |
tags.append(tags[0]) | |
segments = [[points[0], ], ] | |
for j in range(1, len(points)): | |
segments[-1].append(points[j]) | |
if tags[j] and j < (len(points) - 1): | |
segments.append([points[j], ]) | |
for segment in segments: | |
if len(segment) == 2: | |
paths.append(Line(start=tuple_to_imag(segment[0]), | |
end=tuple_to_imag(segment[1]))) | |
elif len(segment) == 3: | |
paths.append(QuadraticBezier(start=tuple_to_imag(segment[0]), | |
control=tuple_to_imag(segment[1]), | |
end=tuple_to_imag(segment[2]))) | |
elif len(segment) == 4: | |
C = ((segment[1][0] + segment[2][0]) / 2.0, | |
(segment[1][1] + segment[2][1]) / 2.0) | |
paths.append(QuadraticBezier(start=tuple_to_imag(segment[0]), | |
control=tuple_to_imag(segment[1]), | |
end=tuple_to_imag(C))) | |
paths.append(QuadraticBezier(start=tuple_to_imag(C), | |
control=tuple_to_imag(segment[2]), | |
end=tuple_to_imag(segment[3]))) | |
else: | |
print(f"incompatible segment length: {len(segment)}") | |
start = end + 1 | |
path = Path(*paths) | |
wsvg(path, filename="text.svg") |
@CatherineH The link was just an example. Also I'm quite bad in understanding heavily math-ed papers :D
Does that mean that FT_Outline_Decompose
isn't as helpful as you first thought?
as test with char 'B' and
face = Face('C:\Windows\Fonts\arial.ttf')
there are some segments of length 5 and 6
this seems to be Ok (as TrueType infact has only quads):
...
elif len(segment) == 5:
C12 = segment[1]
C23 = segment[2]
C34 = segment[3]
P1 = segment[0]
P2 = ((segment[1][0] + segment[2][0]) / 2.0,
(segment[1][1] + segment[2][1]) / 2.0)
P3 = ((segment[2][0] + segment[3][0]) / 2.0,
(segment[2][1] + segment[3][1]) / 2.0)
P4 = segment[4]
paths.append(QuadraticBezier(start=tuple_to_imag(P1),
control=tuple_to_imag(C12),
end=tuple_to_imag(P2)))
paths.append(QuadraticBezier(start=tuple_to_imag(P2),
control=tuple_to_imag(C23),
end=tuple_to_imag(P3)))
paths.append(QuadraticBezier(start=tuple_to_imag(P3),
control=tuple_to_imag(C34),
end=tuple_to_imag(P4)))
elif len(segment) == 6:
C12 = segment[1]
C23 = segment[2]
C34 = segment[3]
C45 = segment[4]
P1 = segment[0]
P2 = ((segment[1][0] + segment[2][0]) / 2.0,
(segment[1][1] + segment[2][1]) / 2.0)
P3 = ((segment[2][0] + segment[3][0]) / 2.0,
(segment[2][1] + segment[3][1]) / 2.0)
P4 = ((segment[3][0] + segment[4][0]) / 2.0,
(segment[3][1] + segment[4][1]) / 2.0)
P5 = segment[5]
paths.append(QuadraticBezier(start=tuple_to_imag(P1),
control=tuple_to_imag(C12),
end=tuple_to_imag(P2)))
paths.append(QuadraticBezier(start=tuple_to_imag(P2),
control=tuple_to_imag(C23),
end=tuple_to_imag(P3)))
paths.append(QuadraticBezier(start=tuple_to_imag(P3),
control=tuple_to_imag(C34),
end=tuple_to_imag(P4)))
paths.append(QuadraticBezier(start=tuple_to_imag(P4),
control=tuple_to_imag(C45),
end=tuple_to_imag(P5)))
By the way:
def move_to(a, ctx):
ctx.append("M {},{}".format(a.x, a.y))
def line_to(a, ctx):
ctx.append("L {},{}".format(a.x, a.y))
def conic_to(a, b, ctx):
ctx.append("Q {},{} {},{}".format(a.x, a.y, b.x, b.y))
def cubic_to(a, b, c, ctx):
ctx.append("C {},{} {},{} {},{}".format(a.x, a.y, b.x, b.y, c.x, c.y))
ctx = []
outline.decompose(ctx, move_to=move_to, line_to=line_to, conic_to=conic_to, cubic_to=cubic_to)
works, but gives svg "y" in the opposite direction
https://github.com/rougier/freetype-py/releases
https://github.com/rougier/freetype-py/blob/master/examples/glyph-vector-decompose.py
Has someone here understood the face.set_char_size() and how to get a 'reasonable' svg font size ?
how to get the right scaling back to for example to style="font-size=11" , when working with svg ?
Thanks in advance,
XM
... and with ctx:
svg = """
<svg xmlns="http://www.w3.org/2000/svg"
width="100mm"
height="100mm"
viewBox="0 0 100 100"
version="1.1"
<path
transform="scale(0.00338) scale(10)"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
d="{}"
/>
""".format(" ".join(ctx))
print(svg)
FT_Outline_Decompose
essentially does what this script does; it skips over higher order bezier curves. Inkscape also uses this function in their codebase, but only if the glyph is of type "outline", and all of these fonts report that they are type "bitmap". I'm not sure how Inkscape is able to convert non-glyph fonts. I need to understand the inkscape libnrtype library better.