dealing with multiple fonts

This commit is contained in:
themancalledjakob 2024-08-14 10:50:57 +02:00
parent 7c72dd54dc
commit 94f4e28605
3 changed files with 195 additions and 32 deletions

View file

@ -17,5 +17,15 @@ bpy.utils.script_paths()
``` ```
then check it for the `addons` directory then check it for the `addons` directory
# addons dir:
```
~/git/tools/blender_git/build_linux_v4.1/bin/4.1/scripts/addons/
```
# addon data:
```
~/.config/blender/4.1/datafiles
```
# reload addon in blender: # reload addon in blender:
F3 -> "reload scripts" F3 -> "reload scripts"

View file

@ -149,11 +149,32 @@ class FONT3D_glyph_properties(bpy.types.PropertyGroup):
) )
class FONT3D_text_properties(bpy.types.PropertyGroup): class FONT3D_text_properties(bpy.types.PropertyGroup):
def font_name_items(self, context):
out = []
for f in Font.fonts.keys():
out.append((f, f, "A Font"))
return tuple(out)
def face_name_items(self, context):
out = []
for ff in Font.fonts[self.font_lol].faces.keys():
out.append((ff, ff, "A Face"))
return tuple(out)
def font_name_update_callback(self, context):
self.face_lol = Font.fonts[self.font_lol].faces.keys()[0]
update_callback(self, context)
def update_callback(self, context): def update_callback(self, context):
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()
face_name: bpy.props.StringProperty() face_name: bpy.props.StringProperty()
font_lol: bpy.props.EnumProperty(
items=font_name_items,
update=font_name_update_callback
)
face_lol: bpy.props.EnumProperty(
items=face_name_items,
update=update_callback
)
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
@ -550,8 +571,11 @@ class FONT3D_OT_TemporaryHelper(bpy.types.Operator):
scene = bpy.context.scene scene = bpy.context.scene
font3d_data = scene.font3d_data font3d_data = scene.font3d_data
objects = bpy.context.selected_objects # butils.load_font_from_filepath("/home/jrkb/.config/blender/4.1/datafiles/font3d/fonts/NM_Origin.glb")
butils.add_default_metrics_to_objects(objects) butils.update_available_fonts()
# objects = bpy.context.selected_objects
# butils.add_default_metrics_to_objects(objects)
# reference_bound_box = None # reference_bound_box = None
# for o in objects: # for o in objects:
# bb = o.bound_box # bb = o.bound_box
@ -642,6 +666,24 @@ class FONT3D_OT_SaveFontToFile(bpy.types.Operator):
bl_idname = f"{__name__}.save_font_to_file" bl_idname = f"{__name__}.save_font_to_file"
bl_label = "Save Font" bl_label = "Save Font"
bl_options = {'REGISTER', 'UNDO'} bl_options = {'REGISTER', 'UNDO'}
save_path: bpy.props.StringProperty(name="save_path", subtype="DIR_PATH")
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
@ -752,6 +794,9 @@ class FONT3D_OT_CreateFontFromObjects(bpy.types.Operator):
import_infix: bpy.props.StringProperty( import_infix: bpy.props.StringProperty(
default="_NM_Origin_Tender", default="_NM_Origin_Tender",
) )
autodetect_names: bpy.props.BoolProperty(
default=True,
)
fix_common_misspellings: bpy.props.BoolProperty( fix_common_misspellings: bpy.props.BoolProperty(
default=True, default=True,
) )
@ -762,17 +807,50 @@ class FONT3D_OT_CreateFontFromObjects(bpy.types.Operator):
def draw(self, contex): def draw(self, contex):
layout = self.layout layout = self.layout
layout.prop(self, 'font_name') row = layout.row()
layout.prop(self, 'face_name') row.prop(self, 'autodetect_names')
layout.prop(self, 'import_infix') if self.autodetect_names:
scale_y = 0.5
row = layout.row(); row.scale_y = scale_y
row.label(text="Watch out, follow convention in naming your meshes:")
row = layout.row(); row.scale_y = scale_y
row.label(text="'<glyph id>_<font name>_<face name>'")
row = layout.row(); row.scale_y = scale_y
row.label(text=" - glyph id: unicode glyph name or raw glyph")
row = layout.row(); row.scale_y = scale_y
row.label(text=" - font name: font name with underscore")
row = layout.row(); row.scale_y = scale_y
row.label(text=" - face name: face name")
row = layout.row(); row.scale_y = scale_y
row.label(text="working examples:")
row = layout.row(); row.scale_y = scale_y
row.label(text="- 'A_NM_Origin_Tender'")
row = layout.row(); row.scale_y = scale_y
row.label(text="- 'B_NM_Origin_Tender'")
row = layout.row(); row.scale_y = scale_y
row.label(text="- 'arrowright_NM_Origin_Tender'")
row = layout.row(); row.scale_y = scale_y
row.label(text="- '→_NM_Origin_Tender' (equal to above)")
row = layout.row(); row.scale_y = scale_y
row.label(text="- 'quotesingle_NM_Origin_Tender.001'")
row = layout.row(); row.scale_y = scale_y
row.label(text="- 'colon_NM_Origin_Tender_2'")
box = layout.box()
box.enabled = not self.autodetect_names
box.prop(self, 'font_name')
box.prop(self, 'face_name')
box.prop(self, 'import_infix')
layout.prop(self, 'fix_common_misspellings') layout.prop(self, 'fix_common_misspellings')
for k in Font.known_misspellings: if self.fix_common_misspellings:
character = "" for k in Font.known_misspellings:
if Font.known_misspellings[k] in Font.name_to_glyph_d: character = ""
character = f" ({Font.name_to_glyph_d[Font.known_misspellings[k]]})" if Font.known_misspellings[k] in Font.name_to_glyph_d:
layout.label(text=f"{k} -> {Font.known_misspellings[k]}{character}") character = f" ({Font.name_to_glyph_d[Font.known_misspellings[k]]})"
row = layout.row(); row.scale_y = 0.5
row.label(text=f"{k}{Font.known_misspellings[k]}{character}")
def execute(self, context): def execute(self, context):
print(f"executing {self.bl_idname}")
global shared global shared
scene = bpy.context.scene scene = bpy.context.scene
font3d = scene.font3d font3d = scene.font3d
@ -791,20 +869,30 @@ class FONT3D_OT_CreateFontFromObjects(bpy.types.Operator):
font_name = self.font_name font_name = self.font_name
face_name = self.face_name face_name = self.face_name
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_object(o): print(f"processing {o.name}")
process_object = True
if self.autodetect_names:
ifxsplit = o.name.split('_')
if len(ifxsplit) < 4:
print(f"whoops name could not be autodetected {o.name}")
continue
font_name = f"{ifxsplit[1]}_{ifxsplit[2]}"
face_name = ifxsplit[3]
if butils.is_mesh(o) 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}(.)*"
if self.fix_common_misspellings: if self.fix_common_misspellings:
o.name = Font.fix_glyph_name_misspellings(o.name) o.name = Font.fix_glyph_name_misspellings(o.name)
name = re.sub(regex, "", o.name) # name = re.sub(regex, "", o.name)
# glyph_id = Font.name_to_glyph(name)
name = o.name.split('_')[0]
glyph_id = Font.name_to_glyph(name) glyph_id = Font.name_to_glyph(name)
if type(glyph_id )!= type(None): if type(glyph_id )!= type(None):
@ -820,7 +908,6 @@ class FONT3D_OT_CreateFontFromObjects(bpy.types.Operator):
face_name, face_name,
glyph_id, glyph_id,
o) o)
added_font = True
#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
@ -895,6 +982,26 @@ class FONT3D_PT_RightPropertiesPanel(bpy.types.Panel):
if is_glyph: if is_glyph:
layout.row().label(text="Glyph Properties:") layout.row().label(text="Glyph Properties:")
class FONT3D_OT_Reporter(bpy.types.Operator):
bl_idname = f"{__name__}.reporter"
bl_label = "Report"
label = bpy.props.StringProperty(
name="label",
default="INFO",
)
message = bpy.props.StringProperty(
name="message",
default="I have nothing to say really",
)
def execute(self, context):
#this is where I send the message
self.report({'INFO'}, 'whatever')
print("lalala reporter")
for i in range(0,10):
butils.ShowMessageBox('whatever','INFO','INFO')
return {'FINISHED'}
classes = ( classes = (
FONT3D_addonPreferences, FONT3D_addonPreferences,
@ -923,12 +1030,14 @@ classes = (
FONT3D_OT_SaveFontToFile, FONT3D_OT_SaveFontToFile,
FONT3D_OT_CreateFontFromObjects, FONT3D_OT_CreateFontFromObjects,
FONT3D_PT_RightPropertiesPanel, FONT3D_PT_RightPropertiesPanel,
FONT3D_OT_Reporter,
) )
@persistent @persistent
def load_handler(self, dummy): def load_handler(self, dummy):
if not bpy.app.timers.is_registered(butils.execute_queued_functions): if not bpy.app.timers.is_registered(butils.execute_queued_functions):
bpy.app.timers.register(butils.execute_queued_functions) bpy.app.timers.register(butils.execute_queued_functions)
butils.run_in_main_thread(butils.update_available_fonts)
def load_handler_unload(): def load_handler_unload():
if bpy.app.timers.is_registered(butils.execute_queued_functions): if bpy.app.timers.is_registered(butils.execute_queued_functions):
@ -963,6 +1072,7 @@ 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)
butils.run_in_main_thread(butils.update_available_fonts)
Font.name_to_glyph_d = Font.generate_name_to_glyph_d() Font.name_to_glyph_d = Font.generate_name_to_glyph_d()

View file

@ -4,6 +4,8 @@ import queue
import importlib import importlib
import os import os
import re import re
from multiprocessing import Process
# import time # for debugging performance # import time # for debugging performance
# then import dependencies for our addon # then import dependencies for our addon
@ -381,12 +383,18 @@ def load_font_from_filepath(filepath):
return False return False
font3d_data = bpy.context.scene.font3d_data font3d_data = bpy.context.scene.font3d_data
for f in bpy.context.scene.font3d_data.available_fonts.values():
print(f"inside available font: {f.font_name} {f.face_name}")
allObjectsBefore = [] allObjectsBefore = []
for ob in bpy.data.objects: for ob in bpy.data.objects:
allObjectsBefore.append(ob.name) allObjectsBefore.append(ob.name)
bpy.ops.import_scene.gltf(filepath=filepath) bpy.ops.import_scene.gltf(filepath=filepath)
print(f"after import available fonts:")
for f in bpy.context.scene.font3d_data.available_fonts.values():
print(f"after import available font: {f.font_name} {f.face_name}")
fontcollection = bpy.data.collections.get("Font3D") fontcollection = bpy.data.collections.get("Font3D")
if fontcollection is None: if fontcollection is None:
fontcollection = bpy.data.collections.new("Font3D") fontcollection = bpy.data.collections.new("Font3D")
@ -395,6 +403,8 @@ def load_font_from_filepath(filepath):
all_objects = [] all_objects = []
for o in bpy.data.objects: for o in bpy.data.objects:
all_objects.append(o) all_objects.append(o)
for f in bpy.context.scene.font3d_data.available_fonts.values():
print(f"before loop available font: {f.font_name} {f.face_name}")
for o in all_objects: for o in all_objects:
if o.name not in allObjectsBefore: if o.name not in allObjectsBefore:
# must be new # must be new
@ -404,9 +414,13 @@ def load_font_from_filepath(filepath):
and not ("type" in o.keys() and o["type"] == "metrics") and not ("type" in o.keys() and o["type"] == "metrics")
and not is_metrics_object(o) and not is_metrics_object(o)
): ):
for f in bpy.context.scene.font3d_data.available_fonts.values():
print(f"super inside available font: {f.font_name} {f.face_name}")
glyph_id = o["glyph"] glyph_id = o["glyph"]
font_name = o["font_name"] font_name = o["font_name"]
face_name = o["face_name"] face_name = o["face_name"]
ShowMessageBox("Loading Font", "INFO", f"adding glyph {glyph_id} for {font_name} {face_name}")
print(f"adding glyph {glyph_id} for {font_name} {face_name}")
glyph_obj = move_in_fontcollection( glyph_obj = move_in_fontcollection(
o, o,
fontcollection) fontcollection)
@ -422,21 +436,47 @@ def load_font_from_filepath(filepath):
if glyph_obj != o: if glyph_obj != o:
remove_list.append(o) remove_list.append(o)
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 and f.face_name == face_name: # print(f"has in availables {f.font_name} {f.face_name}")
found = True # if f.font_name == font_name and f.face_name == face_name:
break # found = True
if not found: # break
f = font3d_data.available_fonts.add() # if not found:
f.font_name = font_name # f = font3d_data.available_fonts.add()
f.face_name = face_name # f.font_name = font_name
print(f"{__name__} added {font_name} {face_name}") # f.face_name = face_name
# print(f"{__name__} added {font_name} {face_name}")
else: else:
remove_list.append(o) remove_list.append(o)
for o in remove_list: for o in remove_list:
bpy.data.objects.remove(o, do_unlink=True) bpy.data.objects.remove(o, do_unlink=True)
print(f"{__name__}: loaded fonts") print(f"{__name__}: loaded font from {filepath}")
update_available_fonts()
def update_available_fonts():
font3d_data = bpy.context.scene.font3d_data
for font_name in Font.fonts.keys():
for face_name in Font.fonts[font_name].faces.keys():
found = False
for f in font3d_data.available_fonts.values():
if font_name == f.font_name and face_name == f.face_name:
found = True
if not found:
f = font3d_data.available_fonts.add()
f.font_name = font_name
f.face_name = face_name
print("{__name__} added {font_name} {face_name}")
def update_available_texts():
font3d_data = bpy.context.scene.font3d_data
for o in bpy.context.scene.objects:
if "linked_textobject" in o.keys():
i = o["linked_textobject"]
found = False
if len(font3d_data.available_texts) > i:
if font3d_data.available_texts[i].glyphs
def getPreferences(context): def getPreferences(context):
preferences = context.preferences preferences = context.preferences
@ -447,17 +487,17 @@ def clear_available_fonts():
bpy.context.scene.font3d_data.available_fonts.clear() bpy.context.scene.font3d_data.available_fonts.clear()
def load_available_fonts(): def load_available_fonts():
preferences = getPreferences(bpy.context) preferences = getPreferences(bpy.context)
currentObjects = []
for ob in bpy.data.objects:
currentObjects.append(ob.name)
print(f"assets folder: {preferences.assets_dir}") print(f"assets folder: {preferences.assets_dir}")
font_dir = f"{preferences.assets_dir}/fonts" font_dir = f"{preferences.assets_dir}/fonts"
for file in os.listdir(font_dir): for file in os.listdir(font_dir):
if file.endswith(".glb") or file.endswith(".gltf"): if file.endswith(".glb") or file.endswith(".gltf"):
font_path = os.path.join(font_dir, file) font_path = os.path.join(font_dir, file)
ShowMessageBox("Loading Font", "INFO", f"loading font from {font_path}")
print(f"loading font from {font_path}")
for f in bpy.context.scene.font3d_data.available_fonts.values():
print(f"available font: {f.font_name} {f.face_name}")
load_font_from_filepath(font_path) load_font_from_filepath(font_path)
def ShowMessageBox(title = "Message Box", icon = 'INFO', message=""): def ShowMessageBox(title = "Message Box", icon = 'INFO', message=""):
@ -560,6 +600,9 @@ def set_text_on_curve(text_properties):
regenerate = True regenerate = True
if len(text_properties.text) > i and g.glyph_id != text_properties.text[i]: if len(text_properties.text) > i and g.glyph_id != text_properties.text[i]:
regenerate = True regenerate = True
if len(text_properties.text) > i and (g.font_name != text_properties.text[i].font_name
or g.face_name != text_properties.text[i].face_name):
regenerate = True
if len(text_properties.text) != len(text_properties.glyphs): if len(text_properties.text) != len(text_properties.glyphs):
regenerate = True regenerate = True