font creation

improve font creation operator
offer fixing common misspellings
use glyphNamesToUnicode.txt table to generate name_to_glyph_d
fix typos in code
more consistent naming font_face -> face_name
This commit is contained in:
themancalledjakob 2024-08-12 11:22:44 +02:00
parent 6f71a8f4c4
commit 7c72dd54dc
4 changed files with 12374 additions and 23 deletions

View file

@ -153,7 +153,7 @@ class FONT3D_text_properties(bpy.types.PropertyGroup):
butils.set_text_on_curve(self) butils.set_text_on_curve(self)
text_id: bpy.props.IntProperty() text_id: bpy.props.IntProperty()
font_name: bpy.props.StringProperty() font_name: bpy.props.StringProperty()
font_face: bpy.props.StringProperty() face_name: bpy.props.StringProperty()
text_object: bpy.props.PointerProperty(type=bpy.types.Object) text_object: bpy.props.PointerProperty(type=bpy.types.Object)
text: bpy.props.StringProperty( text: bpy.props.StringProperty(
update=update_callback update=update_callback
@ -411,9 +411,7 @@ class FONT3D_PT_FontCreation(bpy.types.Panel):
font3d = scene.font3d font3d = scene.font3d
font3d_data = scene.font3d_data font3d_data = scene.font3d_data
layout.row().label(text="Font name import infix:") layout.row().operator(f"{__name__}.create_font_from_objects", text='Create/Extend Font')
layout.row().prop(font3d, "import_infix", text="")
layout.row().operator(f"{__name__}.create_font_from_objects", text='Create Font')
layout.row().operator(f"{__name__}.save_font_to_file", text='Save Font To File') layout.row().operator(f"{__name__}.save_font_to_file", text='Save Font To File')
layout.row().operator(f"{__name__}.toggle_font3d_collection", text='Toggle Collection') layout.row().operator(f"{__name__}.toggle_font3d_collection", text='Toggle Collection')
box = layout.box() box = layout.box()
@ -584,8 +582,9 @@ class FONT3D_OT_PlaceText(bpy.types.Operator):
selected = font3d.target_object selected = font3d.target_object
if selected: if selected:
font_name = "NM_Origin" font = font3d_data.available_fonts[font3d_data.active_font_index]
font_face = "Tender" font_name = font.font_name
face_name = font.face_name
distribution_type = 'DEFAULT' distribution_type = 'DEFAULT'
@ -597,7 +596,7 @@ class FONT3D_OT_PlaceText(bpy.types.Operator):
t.text_id = text_id t.text_id = text_id
t.font_name = font_name t.font_name = font_name
t.font_face = font_face t.face_name = face_name
t.text_object = selected t.text_object = selected
t.text = scene.font3d.text t.text = scene.font3d.text
t.letter_spacing = scene.font3d.letter_spacing t.letter_spacing = scene.font3d.letter_spacing
@ -744,6 +743,35 @@ class FONT3D_OT_CreateFontFromObjects(bpy.types.Operator):
bl_label = "Create Font" bl_label = "Create Font"
bl_options = {'REGISTER', 'UNDO'} bl_options = {'REGISTER', 'UNDO'}
font_name: bpy.props.StringProperty(
default="NM_Origin",
)
face_name: bpy.props.StringProperty(
default="Tender",
)
import_infix: bpy.props.StringProperty(
default="_NM_Origin_Tender",
)
fix_common_misspellings: bpy.props.BoolProperty(
default=True,
)
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def draw(self, contex):
layout = self.layout
layout.prop(self, 'font_name')
layout.prop(self, 'face_name')
layout.prop(self, 'import_infix')
layout.prop(self, 'fix_common_misspellings')
for k in Font.known_misspellings:
character = ""
if Font.known_misspellings[k] in Font.name_to_glyph_d:
character = f" ({Font.name_to_glyph_d[Font.known_misspellings[k]]})"
layout.label(text=f"{k} -> {Font.known_misspellings[k]}{character}")
def execute(self, context): def execute(self, context):
global shared global shared
scene = bpy.context.scene scene = bpy.context.scene
@ -756,28 +784,30 @@ class FONT3D_OT_CreateFontFromObjects(bpy.types.Operator):
fontcollection = bpy.data.collections.new("Font3D") fontcollection = bpy.data.collections.new("Font3D")
ifxsplit = font3d.import_infix.split('_') ifxsplit = font3d.import_infix.split('_')
font_name = f"{ifxsplit[1]}_{ifxsplit[2]}" # if len(ifxsplit) != 4:
face_name = ifxsplit[3]
# font_name = f"{ifxsplit[1]}_{ifxsplit[2]}"
# face_name = ifxsplit[3]
font_name = self.font_name
face_name = self.face_name
added_font = False added_font = False
# TODO: do not clear # TODO: do not clear
font3d_data.available_fonts.clear() # font3d_data.available_fonts.clear()
Font.fonts = {} # Font.fonts = {}
currentObjects = [] currentObjects = []
for o in context.selected_objects: for o in context.selected_objects:
if o.name not in currentObjects: if o.name not in currentObjects:
if font3d.import_infix in o.name and not butils.is_metrics_obj(o): if font3d.import_infix in o.name and not butils.is_metrics_object(o):
uc = o.users_collection uc = o.users_collection
regex = f"{font3d.import_infix}(.)*" regex = f"{font3d.import_infix}(.)*"
name = re.sub(regex, "", o.name) if self.fix_common_misspellings:
glyph_id = "unknown" o.name = Font.fix_glyph_name_misspellings(o.name)
if len(name) == 1: name = re.sub(regex, "", o.name)
glyph_id = name glyph_id = Font.name_to_glyph(name)
elif name in Font.name_to_glyph_d:
glyph_id = Font.name_to_glyph_d[name]
if glyph_id != "unknown": if type(glyph_id )!= type(None):
o["glyph"] = glyph_id o["glyph"] = glyph_id
o["font_name"] = font_name o["font_name"] = font_name
o["face_name"] = face_name o["face_name"] = face_name
@ -795,14 +825,17 @@ class FONT3D_OT_CreateFontFromObjects(bpy.types.Operator):
#TODO: is there a better way to iterate over a CollectionProperty? #TODO: is there a better way to iterate over a CollectionProperty?
found = False found = False
for f in font3d_data.available_fonts.values(): for f in font3d_data.available_fonts.values():
if f.font_name == font_name: if (f.font_name == font_name
and f.face_name == face_name):
found = True found = True
break break
if not found: if not found:
f = font3d_data.available_fonts.add() f = font3d_data.available_fonts.add()
f.font_name = font_name f.font_name = font_name
f.face_name = face_name
else: else:
print(f"import warning: did not understand glyph {name}")
self.report({'INFO'}, f"did not understand glyph {name}") self.report({'INFO'}, f"did not understand glyph {name}")
return {'FINISHED'} return {'FINISHED'}
@ -931,6 +964,8 @@ def register():
butils.run_in_main_thread(butils.clear_available_fonts) butils.run_in_main_thread(butils.clear_available_fonts)
butils.run_in_main_thread(butils.load_available_fonts) butils.run_in_main_thread(butils.load_available_fonts)
Font.name_to_glyph_d = Font.generate_name_to_glyph_d()
def unregister(): def unregister():
for cls in classes: for cls in classes:
bpy.utils.unregister_class(cls) bpy.utils.unregister_class(cls)

View file

@ -606,12 +606,12 @@ def set_text_on_curve(text_properties):
glyph_id = c glyph_id = c
glyph = Font.get_glyph(text_properties.font_name, glyph = Font.get_glyph(text_properties.font_name,
text_properties.font_face, text_properties.face_name,
glyph_id) glyph_id)
if glyph == None: if glyph == None:
# self.report({'ERROR'}, f"Glyph not found for {font_name} {font_face} {glyph_id}") # self.report({'ERROR'}, f"Glyph not found for {font_name} {face_name} {glyph_id}")
print(f"Glyph not found for {text_properties.font_name} {text_properties.font_face} {glyph_id}") print(f"Glyph not found for {text_properties.font_name} {text_properties.face_name} {glyph_id}")
continue continue
ob = None ob = None

View file

@ -1,8 +1,11 @@
from typing import TypedDict from typing import TypedDict
from typing import Dict from typing import Dict
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path
# convenience dictionary for translating names to glyph ids # convenience dictionary for translating names to glyph ids
# note: overwritten/extended by the content of "glypNamesToUnicode.txt"
# when addon is registered in __init__.py
name_to_glyph_d = { name_to_glyph_d = {
"zero": "0", "zero": "0",
"one": "1", "one": "1",
@ -34,6 +37,55 @@ name_to_glyph_d = {
"space": " ", "space": " ",
} }
known_misspellings = {
# simple misspelling
"excent" : "accent",
"overdot" : "dotaccent",
"diaresis": "dieresis",
"diaeresis": "dieresis",
# character does not exist.. maybe something else
"Odoubleacute": "Ohungarumlaut",
"Udoubleacute": "Uhungarumlaut",
"Wcaron": "Wcircumflex",
"Neng": "Nlongrightleg",
"Lgrave": "Lacute",
# currency stuff
"doller": "dollar",
"euro": "Euro",
"yuan": "yen", # https://en.wikipedia.org/wiki/Yen_and_yuan_sign
"pound": "sterling",
# whoopsie
"__": "_",
}
def fix_glyph_name_misspellings(name):
for misspelling in known_misspellings:
if misspelling in name:
return name.replace(misspelling,
known_misspellings[misspelling])
return name
def name_to_glyph(name):
if len(name) == 1:
return name
if name in name_to_glyph_d:
return name_to_glyph_d[name]
else:
return None
def generate_name_to_glyph_d():
d = {}
with open(f"{Path(__file__).parent}/glyphNamesToUnicode.txt") as f:
for line in f:
if line[0] == '#':
continue
(name, hexstr) = line.split(' ')
val = chr(int(hexstr, base=16))
d[name] = val
return d
class FontFace: class FontFace:
"""FontFace is a class holding glyphs """FontFace is a class holding glyphs

12264
common/glyphNamesToUnicode.txt Normal file

File diff suppressed because it is too large Load diff