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:
parent
6f71a8f4c4
commit
7c72dd54dc
4 changed files with 12374 additions and 23 deletions
75
__init__.py
75
__init__.py
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
12264
common/glyphNamesToUnicode.txt
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue