stabilizing, user experience

use class for glyph availability
use isinstance instead of type
better user experience when export directory does not exist
This commit is contained in:
jrkb 2025-05-29 15:27:24 +02:00
parent 777644e509
commit 335ab1face
4 changed files with 279 additions and 129 deletions

View file

@ -1,5 +1,6 @@
from typing import Dict
from pathlib import Path
from typing import NamedTuple
# convenience dictionary for translating names to glyph ids
# note: overwritten/extended by the content of "glypNamesToUnicode.txt"
@ -160,6 +161,7 @@ class Font:
self.faces = faces
def register_font(font_name, face_name, glyphs_in_fontfile, filepath):
if not fonts.keys().__contains__(font_name):
fonts[font_name] = Font({})
@ -177,6 +179,34 @@ def register_font(font_name, face_name, glyphs_in_fontfile, filepath):
fonts[font_name].faces[face_name].filepaths.append(filepath)
def get_font(font_name):
if not fonts.keys().__contains__(font_name):
print(f"ABC3D::get_font: font name({font_name}) not found")
print(fonts.keys())
return None
return fonts[font_name]
def get_font_face(font_name, face_name):
font = get_font(font_name)
if font is None:
return None
if not font.faces.keys().__contains__(face_name):
print(
f"ABC3D::get_font_face (font: {font_name}): face name({face_name}) not found"
)
print(font.faces.keys())
return None
return font.faces[face_name]
def get_font_face_filepaths(font_name, face_name):
face = get_font_face(font_name, face_name)
if not face:
return None
return face.filepaths
def add_glyph(font_name, face_name, glyph_id, glyph_object):
"""add_glyph adds a glyph to a FontFace
it creates the :class:`Font` and :class:`FontFace` if it does not exist yet
@ -203,6 +233,38 @@ def add_glyph(font_name, face_name, glyph_id, glyph_object):
fonts[font_name].faces[face_name].loaded_glyphs.append(glyph_id)
def get_glyphs(font_name, face_name, glyph_id):
"""get_glyphs returns an array of glyphs of a FontFace
:param font_name: The :class:`Font` you want to get the glyph from
:type font_name: str
:param face_name: The :class:`FontFace` you want to get the glyph from
:type face_name: str
:param glyph_id: The ``glyph_id`` from the glyph you want
:type glyph_id: str
...
:return: returns a list of the glyph objects, or an empty list if none exists
:rtype: `List`
"""
face = get_font_face(font_name, face_name)
if face is None:
print(f"ABC3D::get_glyph: font({font_name}) face({face_name}) not found")
print(fonts[font_name].faces.keys())
return []
glyphs_for_id = face.glyphs.get(glyph_id)
if glyphs_for_id is None:
print(
f"ABC3D::get_glyph: font({font_name}) face({face_name}) glyph({glyph_id}) not found"
)
if glyph_id not in fonts[font_name].faces[face_name].missing_glyphs:
fonts[font_name].faces[face_name].missing_glyphs.append(glyph_id)
return []
return glyphs_for_id
def get_glyph(font_name, face_name, glyph_id, alternate=0):
"""add_glyph adds a glyph to a FontFace
it creates the :class:`Font` and :class:`FontFace` if it does not exist yet
@ -218,25 +280,22 @@ def get_glyph(font_name, face_name, glyph_id, alternate=0):
:rtype: `Object`
"""
if not fonts.keys().__contains__(font_name):
# print(f"ABC3D::get_glyph: font name({font_name}) not found")
# print(fonts.keys())
glyphs = get_glyphs(font_name, face_name, glyph_id)
if len(glyphs) <= alternate or len(glyphs) == 0:
print(
f"ABC3D::get_glyph: font({font_name}) face({face_name}) glyph({glyph_id})[{alternate}] not found"
)
return None
face = fonts[font_name].faces.get(face_name)
if face is None:
# print(f"ABC3D::get_glyph: font({font_name}) face({face_name}) not found")
# print(fonts[font_name].faces.keys())
return None
return glyphs[alternate]
glyphs_for_id = face.glyphs.get(glyph_id)
if glyphs_for_id is None or len(glyphs_for_id) <= alternate:
# print(f"ABC3D::get_glyph: font({font_name}) face({face_name}) glyph({glyph_id})[{alternate}] not found")
if glyph_id not in fonts[font_name].faces[face_name].missing_glyphs:
fonts[font_name].faces[face_name].missing_glyphs.append(glyph_id)
return None
return fonts[font_name].faces[face_name].glyphs.get(glyph_id)[alternate]
class GlyphsAvailability(NamedTuple):
loaded: str
missing: str
unloaded: str
filepaths: list[str]
def test_glyphs_availability(font_name, face_name, text):
@ -245,24 +304,24 @@ def test_glyphs_availability(font_name, face_name, text):
not fonts.keys().__contains__(font_name)
or fonts[font_name].faces.get(face_name) is None
):
return "", "", text # <loaded>, <missing>, <maybe>
return GlyphsAvailability("", "", "", [])
loaded = []
missing = []
maybe = []
unloaded = []
for c in text:
if c in fonts[font_name].faces[face_name].loaded_glyphs:
loaded.append(c)
elif c in fonts[font_name].faces[face_name].glyphs_in_fontfile:
maybe.append(c)
unloaded.append(c)
else:
if c not in fonts[font_name].faces[face_name].missing_glyphs:
fonts[font_name].faces[face_name].missing_glyphs.append(c)
missing.append(c)
return (
return GlyphsAvailability(
"".join(loaded),
"".join(missing),
"".join(maybe),
"".join(unloaded),
fonts[font_name].faces[face_name].filepaths,
)
@ -288,15 +347,10 @@ def test_availability(font_name, face_name, text):
return MISSING_FONT
if fonts[font_name].faces.get(face_name) is None:
return MISSING_FACE
loaded, missing, maybe, filepaths = test_glyphs_availability(
availability: GlyphsAvailability = test_glyphs_availability(
font_name, face_name, text
)
return {
"loaded": loaded,
"missing": missing,
"maybe": maybe,
"filepaths": filepaths,
}
return availability
# holds all fonts

View file

@ -89,6 +89,26 @@ def printerr(*args, **kwargs):
def removeNonAlphabetic(s):
return "".join([i for i in s if i.isalpha()])
import pathlib
import os
def can_create_path(path_str : str):
path = pathlib.Path(path_str).absolute().resolve()
while True: # this looks dangerours, but it actually is not
if path.exists():
if os.access(path, os.W_OK):
return True
else:
return False
elif path == path.parent:
# should never be reached, because root exists
# but if it doesn't.. well then we can't
return False
path = path.parent
# # Evaluate a bezier curve for the parameter 0<=t<=1 along its length
# def evaluateBezierPoint(p1, h1, h2, p2, t):