Reputation: 21
I have a class that handles loading and converting SVG icons to glyphs using svgpathtools. Then, I have a separate font class that takes those glyphs and generates the actual font. However, I'm encountering some issues in this transformation process and generating the final font.
Here’s an outline of my approach:
The Icons class is responsible for loading the SVGs and converting them into glyphs using svgpathtools. The Font class takes those glyphs and generates the final font. I’d appreciate any insights or suggestions on how to improve this process or troubleshoot the issues I'm running into.
import os
from svgpathtools import svg2paths2
class Icons():
svg_path = os.path.join(os.path.dirname(__file__), "svg")
def __init__(self):
self._icons = []
self.load_icons()
@property
def get_icons(self):
return self._icons
def convert_svg_to_glyph(self, svg_file):
paths, attributes, _ = svg2paths2(os.path.join(self.svg_path, svg_file))
glyph = {
"name": svg_file.split(".")[0],
"paths": paths,
"attributes": attributes
}
return glyph
def load_icons(self):
if not os.listdir(self.svg_path):
return "Not svg files found"
icon_files = [f for f in os.listdir(self.svg_path) if f.endswith('.svg')]
for icon_file in icon_files:
glyph = self.convert_svg_to_glyph(icon_file)
self._icons.append(glyph)
from typing import Tuple
from icons import Icons
from fontTools.ttLib import TTFont, newTable
from fontTools.pens.ttGlyphPen import TTGlyphPen
from fontTools.ttLib.tables._c_m_a_p import CmapSubtable
from svgpathtools import Path, Line, CubicBezier, QuadraticBezier, Arc
class Font:
def __init__(self):
self.icons = Icons().get_icons
self.font = TTFont()
self.font.setGlyphOrder([".notdef"])
self.font["head"] = newTable("head")
self.font["hhea"] = newTable("hhea")
self.font["maxp"] = newTable("maxp")
self.font["OS/2"] = newTable("OS/2")
self.font["name"] = newTable("name")
self.font["post"] = newTable("post")
self.font["glyf"] = newTable("glyf")
self.font["hmtx"] = newTable("hmtx")
self.font["cmap"] = newTable("cmap")
self.create_font()
def create_font(self):
cmap = {}
glyf_table = self.font["glyf"]
hmtx_table = self.font["hmtx"]
hmtx_table.metrics = {}
unicode_start = 0xE000
for icon in self.icons:
glyph_name = icon['name']
glyph_paths = icon['paths']
pen = CustomPen(None)
for path in glyph_paths:
self.draw_path(path, pen)
glyph = pen.glyph()
glyf_table[glyph_name] = glyph
hmtx_table.metrics[glyph_name] = (600, 0)
cmap[unicode_start] = glyph_name
unicode_start += 1
cmap_table = self.font["cmap"]
cmap_subtable = CmapSubtable.newSubtable(4)
cmap_subtable.platformID = 3
cmap_subtable.platEncID = 1
cmap_subtable.language = 0
cmap_subtable.cmap = cmap
cmap_table.tables = [cmap_subtable]
self.font["head"].unitsPerEm = 1000
self.font["head"].magicNumber = 0x5F0F3CF5
self.font["head"].created = 0
self.font["hhea"].ascent = 800
self.font["hhea"].descent = -200
self.font["hhea"].lineGap = 200
self.font["hhea"].numOfLongMetrics = len(self.icons)
self.font["maxp"].numGlyphs = len(self.icons) + 1
self.font["post"].formatType = 3.0
self.font.save("custom_font.ttf")
def draw_path(self, path, pen):
contour_started = False
first_point = None
for segment in path:
if isinstance(segment, Line):
if not contour_started:
pen.moveTo((segment.start.real, segment.start.imag))
first_point = (segment.start.real, segment.start.imag)
contour_started = True
pen.lineTo((segment.end.real, segment.end.imag))
elif isinstance(segment, CubicBezier):
if not contour_started:
pen.moveTo((segment.start.real, segment.start.imag))
first_point = (segment.start.real, segment.start.imag)
contour_started = True
pen.curveTo(
(segment.control1.real, segment.control1.imag),
(segment.control2.real, segment.control2.imag),
(segment.end.real, segment.end.imag)
)
elif isinstance(segment, QuadraticBezier):
if not contour_started:
pen.moveTo((segment.start.real, segment.start.imag))
first_point = (segment.start.real, segment.start.imag)
contour_started = True
pen.qCurveTo(
(segment.control.real, segment.control.imag),
(segment.end.real, segment.end.imag)
)
if contour_started and first_point and (pen.current_position != first_point):
pen.lineTo(first_point)
class CustomPen(TTGlyphPen):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.current_position = None
def moveTo(self, pt: Tuple[float, float]) -> None:
if not self._isClosed():
self.closePath()
super().moveTo(pt)
self.current_position = pt
def lineTo(self, pt: Tuple[float, float]) -> None:
super().lineTo(pt)
self.current_position = pt
def curveTo(self, *points) -> None:
super().curveTo(*points)
self.current_position = points[-1]
def closePath(self) -> None:
if self.current_position != self.points[0]:
self.lineTo(self.points[0])
super().closePath()
def endPath(self) -> None:
if not self._isClosed():
self.closePath()
super().endPath()
def glyph(self):
if not self._isClosed():
self.closePath()
return super().glyph()
Upvotes: 0
Views: 38