From 9c0cf9e5fbd3a81bc21e564d74991b4baa8869bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20L=C3=B6vqvist?= Date: Wed, 18 Feb 2026 03:34:17 +0100 Subject: [PATCH] Dusted off - made to work to some extent --- .gitignore | 7 +++-- arch-linux.txt | 5 +++ ffimport.py | 22 ++++++++++--- hmpparser.py | 2 +- dataclasses.py => local_dataclasses.py | 0 main.py | 12 +++---- ocxparser.py | 2 +- svggenerator.py | 43 +++++++++++++------------- 8 files changed, 55 insertions(+), 38 deletions(-) create mode 100644 arch-linux.txt rename dataclasses.py => local_dataclasses.py (100%) diff --git a/.gitignore b/.gitignore index fbbaa17..864f86b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ venv -svgs -ttf -idea \ No newline at end of file +svgs/ +ttf/ +idea +__pycache__/ diff --git a/arch-linux.txt b/arch-linux.txt new file mode 100644 index 0000000..e929447 --- /dev/null +++ b/arch-linux.txt @@ -0,0 +1,5 @@ +arch packages: fontforge python-svgwrite-1.4.3-5 + +prep: + mkdir svgs +k \ No newline at end of file diff --git a/ffimport.py b/ffimport.py index 3a82c4b..cca9d4c 100644 --- a/ffimport.py +++ b/ffimport.py @@ -18,6 +18,7 @@ def run(path, font_name): font.fontname = font_name # noinspection SpellCheckingInspection font.familyname = 'Hershey' + print(os.listdir(os.path.join(svg_folder, font_name))) for svg in os.listdir(os.path.join(svg_folder, font_name)): if svg[-4:] == '.svg': match = re.match(r'ascii(\d+)_l(-?[\d.]+)_r(-?[\d.]+).svg', svg) @@ -26,18 +27,29 @@ def run(path, font_name): glyph = font.createMappedChar(character) # Import the outlines *before* setting the bearings. Otherwise, everything will be reset. - glyph.importOutlines(os.path.join(svg_folder, font_name, svg)) + + print("and then?", os.path.join(svg_folder, font_name, svg)) + glyph.importOutlines(os.path.join(svg_folder, font_name, svg)) # SILENT CRASH + + glyph.stroke('circular', stroke_size, 'round', 'round') - # glyph.correctDirection() - glyph.removeOverlap() + glyph.correctDirection() glyph.simplify() - glyph.left_side_bearing = float(match.group(2)) + glyph.round() + glyph.removeOverlap() + + #glyph.stroke('circular', stroke_size, 'round', 'round') + # glyph.correctDirection() + #glyph.removeOverlap() + #glyph.simplify() + glyph.left_side_bearing = int(round(float(match.group(2)))) # Compensate for the stroke with on the right side bearing - glyph.right_side_bearing = float(match.group(3)) - stroke_size + glyph.right_side_bearing = int(round(float(match.group(3)) - stroke_size)) else: print(f'Not a SVG file: {os.path.join(svg_folder, font_name, svg)}') # Assumes ttf folder exists + print("Done gen, writing") ttf_folder = os.path.join(path, 'ttf') font.generate(os.path.join(ttf_folder, font_name + '.ttf')) diff --git a/hmpparser.py b/hmpparser.py index b34ab89..6636251 100644 --- a/hmpparser.py +++ b/hmpparser.py @@ -1,7 +1,7 @@ import os import re -from dataclasses import Metadata +from local_dataclasses import Metadata types = {'p': 'Plain', 's': 'Simplex', 'd': 'Duplex', 'cs': 'Complex_Small', 'c': 'Complex', 't': 'Triplex'} names = {'roman': 'Roman', 'greek': 'Greek', 'italic': 'Italic', 'script': 'Script', 'cyril': 'Cyrillic', diff --git a/dataclasses.py b/local_dataclasses.py similarity index 100% rename from dataclasses.py rename to local_dataclasses.py diff --git a/main.py b/main.py index 3037920..c3e3419 100644 --- a/main.py +++ b/main.py @@ -7,7 +7,7 @@ from sys import argv import hmpparser import ocxparser import svggenerator -from dataclasses import Metadata +from local_dataclasses import Metadata path = os.getcwd() svgs_folder = os.path.join(os.getcwd(), 'svgs') @@ -15,12 +15,10 @@ font_forge_path = 'C:\\Program Files (x86)\\FontForgeBuilds\\bin' def generate_ttf(font_name): - if os.path.isdir(os.path.join(svgs_folder, font_name)): - # Remember that the command must be single-line. - # That is why we use semicolons instead of line breaks. - os.chdir(font_forge_path) - subprocess.run(['fontforge', '-c', - f'import os;os.chdir(r"{path}");import ffimport;ffimport.run(r"{path}", "{font_name}")']) + print('GEN', font_name, path) + # 'gdb', '--args', + subprocess.run([ 'fontforge', '-c', f'import os;os.chdir(r"{path}");import ffimport;ffimport.run(r"{path}", "{font_name}")']) + exit() if __name__ == '__main__': diff --git a/ocxparser.py b/ocxparser.py index da21bb4..e286077 100644 --- a/ocxparser.py +++ b/ocxparser.py @@ -1,7 +1,7 @@ import os import re -from dataclasses import Glyph +from local_dataclasses import Glyph offset = ord('R') diff --git a/svggenerator.py b/svggenerator.py index 9af3f19..36d1aa5 100644 --- a/svggenerator.py +++ b/svggenerator.py @@ -1,34 +1,31 @@ import os import svgwrite -from svgwrite.shapes import Polyline +from svgwrite.path import Path -from dataclasses import Glyph, Metadata +from local_dataclasses import Glyph, Metadata base = 16 cap = -16 svg_size = 1024 guard = 12 -line_style = {'fill': 'none', 'stroke': 'black', 'stroke-width': 0, 'stroke-linecap': 'round', - 'stroke-linejoin': 'round'} def generate(glyph: Glyph, metadata: Metadata): - """Uses the glyph and the metadata to create the SVGs. + print(f"Generating character '{metadata.character}' for font {metadata.font_name}") - The subfolder is the font name. - The SVG name contains the metadata necessary to import the glyph to the font. - """ - print(f'Generating character \'{metadata.character}\' for font {metadata.font_name}') - - # Assumes font folder already exists font_path = os.path.join(os.getcwd(), 'svgs', metadata.font_name) left_side_bearing, right_side_bearing = _calculate_bearings(glyph) svg_filename = f'ascii{str(metadata.ascii_value)}_l{left_side_bearing}_r{right_side_bearing}.svg' - dwg = svgwrite.Drawing(os.path.join(font_path, svg_filename), viewBox=f"0 0 {svg_size} {svg_size}") + + dwg = svgwrite.Drawing( + os.path.join(font_path, svg_filename), + viewBox=f"0 0 {svg_size} {svg_size}" + ) points = [] + for coordinate in glyph.coordinates: if coordinate: x, y = coordinate @@ -36,20 +33,27 @@ def generate(glyph: Glyph, metadata: Metadata): y = _map_to_svg(y) points.append((x, y)) else: - # Pen up - # Always check if line is empty (i.e. points array is empty). - # Empty lines can cause problems. if points: - dwg.add(Polyline(points, **line_style)) + _add_path(dwg, points) points = [] - # Finalize the drawing if points: - dwg.add(Polyline(points, **line_style)) + _add_path(dwg, points) dwg.save() +def _add_path(dwg, points): + if not points: + return + + p = Path() + p.push(f"M {points[0][0]} {points[0][1]}") + for x, y in points[1:]: + p.push(f"L {x} {y}") + dwg.add(p) + + def _calculate_bearings(glyph: Glyph): if glyph.coordinates: leftmost_point = _map_to_svg(min(coordinate[0] for coordinate in glyph.coordinates if coordinate)) @@ -63,7 +67,4 @@ def _calculate_bearings(glyph: Glyph): def _map_to_svg(x): - # Scale proportionally (* svg_size / (base - cap)) - # Guard to avoid going out of bounds after stroking the character - # Offset, since the SVG starts at zero (+ svg_size / 2) return x * (svg_size - 2 * guard) / (base - cap) + svg_size / 2