From c27b53b01269e19c850a328068045e9a045a2a15 Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Thu, 27 Jun 2024 14:56:43 +0200 Subject: [PATCH] save font file --- __init__.py | 378 +++++++++++++++++++++++++++++++++++++++---------- butils.py | 223 ++++++++++++++++++++++++++--- common/Font.py | 6 +- 3 files changed, 513 insertions(+), 94 deletions(-) diff --git a/__init__.py b/__init__.py index b71a356..fa8b02f 100644 --- a/__init__.py +++ b/__init__.py @@ -31,6 +31,7 @@ else: from . import butils import bpy +import queue import math import mathutils import io @@ -122,30 +123,69 @@ class FONT3D_OT_Font3D(bpy.types.Operator): class FONT3D_settings(bpy.types.PropertyGroup): - font_path: bpy.props.StringProperty(name="Font path", - description="Where is the font", - default="", - maxlen=1024, - subtype="FILE_PATH") - import_infix: bpy.props.StringProperty(name="Font name import infix", - description="The infix which all font objects to import have", - default="_NM_", - maxlen=1024, - ) - test_text: bpy.props.StringProperty(name="Test Text", - description="the text to test with", - default="Hello", - maxlen=1024, - ) - + font_path: bpy.props.StringProperty( + name="Font path", + description="Where is the font", + default="", + maxlen=1024, + subtype="FILE_PATH") + import_infix: bpy.props.StringProperty( + name="Font name import infix", + description="The infix which all font objects to import have", + default="_NM_", + maxlen=1024, + ) + test_text: bpy.props.StringProperty( + name="Test Text", + description="the text to test with", + default="HELLO", + maxlen=1024, + ) + the_mother_of_typography: bpy.props.PointerProperty( + name="The Mother Of Typography", + description="The target, which will be populated by character children of text.", + type=bpy.types.Object, + ) + letter_spacing: bpy.props.FloatProperty( + name="Letter Spacing", + description="Letter Spacing", + default=0.0, + options={'ANIMATABLE'}, + ) class FONT3D_available_font(bpy.types.PropertyGroup): - font_name: bpy.props.StringProperty(name="whatever") + font_name: bpy.props.StringProperty(name="") + +class FONT3D_glyph_properties(bpy.types.PropertyGroup): + glyph_id: bpy.props.StringProperty(maxlen=1) + glyph_object: bpy.props.PointerProperty(type=bpy.types.Object) + letter_spacing: bpy.props.FloatProperty( + name="Letter Spacing", + description="Letter Spacing", + ) + +class FONT3D_text_properties(bpy.types.PropertyGroup): + def update_callback(self, context): + butils.set_text_on_curve(self) + font_name: bpy.props.StringProperty() + font_face: bpy.props.StringProperty() + text_object: bpy.props.PointerProperty(type=bpy.types.Object) + text: bpy.props.StringProperty() + letter_spacing: bpy.props.FloatProperty( + update=update_callback, + name="Letter Spacing", + description="Letter Spacing", + step=0.01, + ) + distribution_type: bpy.props.StringProperty() + glyphs: bpy.props.CollectionProperty(type=FONT3D_glyph_properties) #TODO: simply, merge, cut cut cut class FONT3D_data(bpy.types.PropertyGroup): - available_fonts: bpy.props.CollectionProperty(type=FONT3D_available_font, name="name of the collection poporotery") + available_fonts: bpy.props.CollectionProperty(type=FONT3D_available_font, name="name of the collection property") active_font_index: bpy.props.IntProperty() + available_texts: bpy.props.CollectionProperty(type=FONT3D_text_properties, name="") + active_text_index: bpy.props.IntProperty() class FONT3D_UL_fonts(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): @@ -158,6 +198,34 @@ class FONT3D_UL_fonts(bpy.types.UIList): def invoke(self, context, event): pass +class FONT3D_UL_texts(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + split = layout.split(factor=0.3) + split.label(text="Index: %d" % (index)) + # custom_icon = "OUTLINER_OB_%s" % item.obj_type + # split.prop(item, "name", text="", emboss=False, translate=False) + split.label(text=f"{item.text}") # avoids renaming the item by accident + + def invoke(self, context, event): + pass + +# TODO: TODO: TODO: TODO: TODO # >>>>>>>>>>>>>>>> +execution_queue = queue.Queue() + + +# This function can safely be called in another thread. +# The function will be executed when the timer runs the next time. +def run_in_main_thread(function): + execution_queue.put(function) + + +def execute_queued_functions(): + while not execution_queue.empty(): + function = execution_queue.get() + function() + return 1.0 + +# <<<<<<<<<<<<<<<<< TODO: TODO: TODO: TODO: TODO # class FONT3D_PT_panel(bpy.types.Panel): bl_label = "Panel for Font3D" @@ -165,6 +233,20 @@ class FONT3D_PT_panel(bpy.types.Panel): bl_space_type = "VIEW_3D" bl_region_type = "UI" + @classmethod + def poll(self, context): + scene = context.scene + font3d = scene.font3d + font3d_data = scene.font3d_data + # TODO: properly include this + def lol(): + font3d_data.active_text_index = -1 + # print(f"{utils.get_timestamp()} ors something") + + run_in_main_thread(lol) + + return True + def draw(self, context): global shared layout = self.layout @@ -181,15 +263,57 @@ class FONT3D_PT_panel(bpy.types.Panel): layout.template_list("FONT3D_UL_fonts", "", font3d_data, "available_fonts", font3d_data, "active_font_index") layout.label(text='DEBUG') layout.row().prop(font3d, "test_text") - layout.row().operator('font3d.testfont', text='Test Font') + layout.row().prop(font3d, "the_mother_of_typography") + layout.row().operator('font3d.testfont', text='Distribute') + layout.label(text="Text Objects") + layout.template_list("FONT3D_UL_texts", "", font3d_data, "available_texts", font3d_data, "active_text_index") + layout.label(text="font properties") + layout.row().prop(font3d, "letter_spacing") layout.label(text="font creation") layout.row().prop(font3d, "import_infix") layout.row().operator('font3d.create_font_from_objects', text='Create Font') layout.row().separator() layout.row().operator('font3d.save_font_to_file', text='Save Font To File') layout.row().operator('font3d.toggle_font3d_collection', text='Toggle Collection') + layout.row().operator('font3d.temporaryhelper', text='Temporary Helper') layout.label(text='DEBUG END') +class FONT3D_PT_TextPropertiesPanel(bpy.types.Panel): + bl_label = "Text Properties" + bl_parent_id = "FONT3D_PT_panel" + bl_category = "Font3D" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + + def get_active_text_properties(self): + if type(bpy.context.object) != type(None) and bpy.context.object.select_get(): + for t in bpy.context.scene.font3d_data.available_texts: + if bpy.context.object == t.text_object: + return t + if bpy.context.object.parent == t.text_object: + return t + return None + + @classmethod + def poll(self,context): + return type(self.get_active_text_properties(self)) != type(None) + + def draw(self, context): + global shared + layout = self.layout + wm = context.window_manager + scene = context.scene + font3d = scene.font3d + font3d_data = scene.font3d_data + + props = self.get_active_text_properties() + + if type(props) == type(None): + layout.label(text="AAAAH") + return + + layout.label(text=f"Mom: {props.text_object.name}") + layout.row().prop(props, "letter_spacing") class FONT3D_OT_LoadFont(bpy.types.Operator): """Load Font 3D""" @@ -244,6 +368,20 @@ class FONT3D_OT_LoadFont(bpy.types.Operator): return {'FINISHED'} +class FONT3D_OT_TemporaryHelper(bpy.types.Operator): + """Temp Font 3D""" + bl_idname = "font3d.temporaryhelper" + bl_label = "Temp Font" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + global shared + scene = bpy.context.scene + font3d_data = scene.font3d_data + + font3d_data.available_texts.clear() + + return {'FINISHED'} class FONT3D_OT_TestFont(bpy.types.Operator): """Test Font 3D""" @@ -257,9 +395,25 @@ class FONT3D_OT_TestFont(bpy.types.Operator): selected = bpy.context.view_layer.objects.active + font3d = scene.font3d + font3d_data = scene.font3d_data + + if font3d.the_mother_of_typography: + selected = font3d.the_mother_of_typography + if selected: font_name = "NM_Origin" font_face = "Tender" + + # text_text_object = bpy.data.objects.new(f"{selected.name}_text", None) + # text_text_object.empty_display_type = 'PLAIN_AXES' + # selected.users_collection[0].objects.link(text_text_object) + + distribution_type = 'DEFAULT' + + bpy.ops.object.select_all(action='DESELECT') + t = font3d_data.available_texts.add() + offset = mathutils.Vector((0.0, 0.0, 0.0)) advance = 0 for i, c in enumerate(scene.font3d.test_text): @@ -269,18 +423,51 @@ class FONT3D_OT_TestFont(bpy.types.Operator): if glyph == None: self.report({'ERROR'}, f"Glyph not found for {font_name} {font_face} {glyph_id}") continue - elif glyph.type == "CURVE": - print("is curve") - - glyph_advance = (-1 * glyph.bound_box[0][0] + glyph.bound_box[4][0]) * 0.01 - offset.x = advance ob = bpy.data.objects.new(f"{glyph_id}", glyph.data) - ob.location = selected.location + offset - ob.scale = (0.01, 0.01, 0.01) + + if selected.type == "CURVE": + distribution_type = "FOLLOW_PATH" + curve_length = butils.get_curve_length(selected) + + ob.constraints.new(type='FOLLOW_PATH') + ob.constraints["Follow Path"].target = selected + ob.constraints["Follow Path"].use_fixed_location = True + ob.constraints["Follow Path"].offset_factor = advance / curve_length + ob.constraints["Follow Path"].use_curve_follow = True + ob.constraints["Follow Path"].forward_axis = "FORWARD_X" + ob.constraints["Follow Path"].up_axis = "UP_Y" + # butils.ShowMessageBox("WHAT","INFO","I don't really know what you mean, lsaidry") + else: + offset.x = advance + ob.location = selected.location + offset + + scalor = 0.001 + + glyph_advance = (-1 * glyph.bound_box[0][0] + glyph.bound_box[4][0]) * scalor + + ob.scale = (scalor, scalor, scalor) selected.users_collection[0].objects.link(ob) - ob.parent = selected + advance = advance + glyph_advance + tc = t.glyphs.add() + tc.glyph_id = c + tc.glyph_object = ob + tc.letter_spacing = 0 + + ob.select_set(True) + + selected.select_set(True) + bpy.context.view_layer.objects.active = selected + bpy.ops.object.parent_set(type='OBJECT') + bpy.ops.object.select_all(action='DESELECT') + + t.font_name = font_name + t.font_face = font_face + t.text_object = selected + t.text = scene.font3d.test_text + t.letter_spacing = 0.0 + t.distribution_type = distribution_type else: butils.ShowMessageBox( title="No object selected", @@ -330,68 +517,95 @@ class FONT3D_OT_SaveFontToFile(bpy.types.Operator): fontcollection = bpy.data.collections.get("Font3D") - # needed to restore current state later - fontcollection_was_linked = False - previous_objects = [] - fontcollection_objects = [] - - # hide fontcollection + # check if all is good to proceed if fontcollection is None: self.report({'INFO'}, f"{bl_info['name']}: There is no collection") return {'CANCELLED'} - elif scene.collection.children.find(fontcollection.name) > 0: - scene.collection.children.unlink(fontcollection) - fontcollection_was_linked = True - # collect and hide previous objects - for o in scene.objects: - previous_objects.append(o) - scene.collection.objects.unlink(o) + if font3d_data.active_font_index < 0: + self.report({'INFO'}, f"{bl_info['name']}: There is no active font") + return {'CANCELLED'} - # show fontcollection - # if scene.collection.children.find(fontcollection.name) < 0: + if len(font3d_data.available_fonts) <= font3d_data.active_font_index: + self.report({'INFO'}, f"{bl_info['name']}: Active font is not available") + return {'CANCELLED'} + + # save state to restore later + was_fontcollection_linked = scene.collection.children.find(fontcollection.name) >= 0 + was_selection = [] + for obj in bpy.context.selected_objects: + was_selection.append(obj) + was_active_object = bpy.context.view_layer.objects.active + + # create restore function + # def restore(): + # bpy.ops.object.select_all(action="DESELECT") + # for obj in was_selection: + # obj.select_set(True) + # bpy.context.view_layer.objects.active = was_active_object + # if not was_fontcollection_linked: + # scene.collection.children.unlink(fontcollection) + + # # show fontcollection + # if not was_fontcollection_linked: # scene.collection.children.link(fontcollection) - # link fontcollection - for o in fontcollection.objects: - fontcollection_objects.append(o) - fontcollection.objects.unlink(o) - scene.collection.objects.link(o) + + bpy.ops.object.select_all(action="DESELECT") # get save data selected_font = font3d_data.available_fonts[font3d_data.active_font_index] - if selected_font == "": - butils.ShowMessageBox("Warning", 'ERROR', "no font selected") - return {'CANCELLED'} - print(selected_font.font_name) - # save as gltf - bpy.ops.export_scene.gltf( - filepath="/home/jrkb/Downloads/toast/maker.gltf", - check_existing=False, - export_format='GLTF_SEPARATE', - export_extras=True, - export_hierarchy_full_collections=True, - use_active_collection_with_nested=True, - ) + self.report({'INFO'}, f"{bl_info['name']}: {selected_font.font_name}") + preferences = getPreferences(context) + print(f"assets folder: {preferences.assets_dir}") - # restore from previous state - def restore(): - for o in scene.objects: - scene.collection.objects.unlink(o) + bpy.ops.scene.new(type='FULL_COPY') - for o in previous_objects: - scene.collection.objects.link(o) + linked_collections = bpy.context.scene.collection.children.values() + for c in linked_collections: + bpy.context.scene.collection.children.unlink(c) + bpy.context.scene.collection.children.link(fontcollection) - for o in fontcollection_objects: - fontcollection.objects.link(o) + # select what needs to be selected + export_objects = [] + for obj in fontcollection.objects: + if obj["font_name"] == selected_font.font_name: + obj.select_set(True) + export_objects.append(obj) - if fontcollection_was_linked: - scene.collection.children.link(fontcollection) + context_override = bpy.context.copy() + context_override["selected_objects"] = list(export_objects) + # context_override["scene"] = bpy.context.scene.copy() + with bpy.context.temp_override(**context_override): + # prepare variables before disable addons + filepath = f"/tmp/toast/{selected_font.font_name}.gltf" + del bpy.context.scene["font3d"] + del bpy.context.scene["font3d_data"] + del bpy.context.scene["curve_array_properties"] + del bpy.context.scene["curvetools"] + del bpy.context.scene["ObjectAlongPath_curveName"] - bpy.app.timers.register(restore, first_interval=5) + # save as gltf + lol = bpy.ops.export_scene.gltf( + filepath=filepath, + check_existing=False, + export_format='GLTF_SEPARATE', + export_extras=True, + # export_hierarchy_full_collections=True, + # use_active_collection_with_nested=True, + use_selection=True, + use_active_scene=True, + ) + # bpy.app.timers.register(lambda: bpy.ops.scene.delete(), first_interval=5) + + bpy.ops.scene.delete() + # restore() return {'FINISHED'} +# keep = ['io_anim_bvh', 'io_curve_svg', 'io_mesh_stl', 'io_mesh_uv_layout', 'io_scene_fbx', 'io_scene_gltf2', 'io_scene_x3d', 'cycles', 'pose_library', 'font3d'] +# for addon in keep: + # bpy.ops.preferences.addon_enable(module=addon) class FONT3D_OT_CreateFontFromObjects(bpy.types.Operator): @@ -434,11 +648,13 @@ class FONT3D_OT_CreateFontFromObjects(bpy.types.Operator): glyph_id = Font.name_to_glyph_d[name] if glyph_id != "unknown": - bpy.data.objects[o.name]["glyph"] = glyph_id + o["glyph"] = glyph_id + o["font_name"] = font_name + o["face_name"] = face_name + # butils.apply_all_transforms(o) butils.move_in_fontcollection( o, - fontcollection, - scene) + fontcollection) Font.add_glyph( font_name, face_name, @@ -466,10 +682,15 @@ classes = ( FONT3D_addonPreferences, FONT3D_OT_Font3D, FONT3D_available_font, + FONT3D_glyph_properties, + FONT3D_text_properties, FONT3D_data, FONT3D_settings, FONT3D_UL_fonts, + FONT3D_UL_texts, FONT3D_PT_panel, + FONT3D_PT_TextPropertiesPanel, + FONT3D_OT_TemporaryHelper, FONT3D_OT_TestFont, FONT3D_OT_LoadFont, FONT3D_OT_ToggleFont3DCollection, @@ -482,18 +703,27 @@ def register(): bpy.utils.register_class(cls) bpy.types.Scene.font3d = bpy.props.PointerProperty(type=FONT3D_settings) bpy.types.Scene.font3d_data = bpy.props.PointerProperty(type=FONT3D_data) + bpy.types.Object.__del__ = lambda self: print(f"Bye {self.name}") print(f"REGISTER {bl_info['name']}") + bpy.app.timers.register(execute_queued_functions) # would love to properly auto start this, but IT DOES NOT WORK # if load_handler not in bpy.app.handlers.load_post: # bpy.app.handlers.load_post.append(load_handler) + # clear available fonts + def clear_available_fonts(): + bpy.context.scene.font3d_data.available_fonts.clear() + + run_in_main_thread(clear_available_fonts) + def unregister(): # would love to properly auto start this, but IT DOES NOT WORK # if load_handler in bpy.app.handlers.load_post: # bpy.app.handlers.load_post.remove(load_handler) for cls in classes: bpy.utils.unregister_class(cls) + bpy.app.timers.unregister(execute_queued_functions) del bpy.types.Scene.font3d del bpy.types.Scene.font3d_data diff --git a/butils.py b/butils.py index d3054b9..42afa54 100644 --- a/butils.py +++ b/butils.py @@ -1,5 +1,20 @@ import bpy +import mathutils +import importlib +# then import dependencies for our addon +if "Font" in locals(): + importlib.reload(Font) +else: + from .common import Font + +def apply_all_transforms(obj): + mb = obj.matrix_basis + if hasattr(obj.data, "transform"): + obj.data.transform(mb) + for c in obj.children: + c.matrix_local = mb @ c.matrix_local + obj.matrix_basis.identity() def get_parent_collection_names(collection, parent_names): for parent_collection in bpy.data.collections: @@ -8,29 +23,127 @@ def get_parent_collection_names(collection, parent_names): get_parent_collection_names(parent_collection, parent_names) return +# Ensure it's a curve object +# TODO: no raising, please +def get_curve_length(obj, num_samples = 100): + total_length = 0 + + if obj.type != 'CURVE': + raise TypeError("The selected object is not a curve") + + curve = obj.data + + # Loop through all splines in the curve + for spline in curve.splines: + total_length = total_length + spline.calc_length() + + return total_length + +# def get_objects_by_name(name, startswith="", endswith=""): + # return [obj for obj in bpy.context.scene.objects if obj.name.startswith(startswith) and if obj.name.endswith(endswith)] + +def find_objects_by_name( + objects, + equals="", + contains="", + startswith="", + endswith=""): + # handle equals + if equals != "": + index = objects.find(equals) + if index >= 0: + return [objects[index]] + return [] + # handle others is more permissive + return [obj for obj in objects if obj.name.startswith(startswith) and obj.name.endswith(endswith) and obj.name.find(contains) >= 0] + +def find_objects_by_custom_property( + objects, + property_name="", + property_value=""): + return [obj for obj in objects if property_name in obj and obj[property_name] == property_value] def turn_collection_hierarchy_into_path(obj): - parent_collection = obj.users_collection[0] - parent_names = [] - parent_names.append(parent_collection.name) - get_parent_collection_names(parent_collection, parent_names) - parent_names.reverse() - return '\\'.join(parent_names) + parent_collection = obj.users_collection[0] + parent_names = [] + parent_names.append(parent_collection.name) + get_parent_collection_names(parent_collection, parent_names) + parent_names.reverse() + return '\\'.join(parent_names) -def move_in_fontcollection(obj, fontcollection, scene): +def find_font_object(fontcollection, font_name): + fonts = find_objects_by_custom_property(fontcollection.objects, + "is_font", + True) + for font in fonts: + if font["font_name"] == font_name and font.parent == None: + return font + return None + +def find_font_face_object(font_obj, face_name): + faces = find_objects_by_custom_property(font_obj.children, + "is_face", + True) + for face in faces: + if face["face_name"] == face_name: + return face + return None + +def move_in_fontcollection(obj, fontcollection): # print(turn_collection_hierarchy_into_path(obj)) - if scene.collection.objects.find(obj.name) >= 0: - scene.collection.objects.unlink(obj) + # if scene.collection.objects.find(obj.name) >= 0: + # scene.collection.objects.unlink(obj) + for c in obj.users_collection: + c.objects.unlink(obj) if fontcollection.objects.find(obj.name) < 0: fontcollection.objects.link(obj) - # TODO: move in glyphs - # if fontcollection.objects.find("glyphs") < 0: - # empty = bpy.data.objects.new("glyphs", None) - # empty.empty_display_type = 'PLAIN_AXES' - # fontcollection.objects.link(empty) - # glyphs = fontcollection.objects.get("glyphs") - # if obj.parent != glyphs: - # obj.parent = glyphs + + # parent nesting structure + # the font object + font_obj = find_font_object(fontcollection, + obj["font_name"]) + if font_obj == None: + font_obj = bpy.data.objects.new(obj["font_name"], None) + font_obj.empty_display_type = 'PLAIN_AXES' + fontcollection.objects.link(font_obj) + + # ensure custom properties are set + font_obj["font_name"] = obj["font_name"] + font_obj["is_font"] = True + + # the face object as a child of font object + face_obj = find_font_face_object(font_obj, + obj["face_name"]) + if face_obj == None: + face_obj = bpy.data.objects.new(obj["face_name"], None) + face_obj.empty_display_type = 'PLAIN_AXES' + face_obj["is_face"] = True + fontcollection.objects.link(face_obj) + # ensure custom properties are set + face_obj["face_name"] = obj["face_name"] + face_obj["font_name"] = obj["font_name"] + + if face_obj.parent != font_obj: + face_obj.parent = font_obj + + # create glyphs if it does not exist + glyphs_objs = find_objects_by_name(face_obj.children, startswith="glyphs") + if len(glyphs_objs) <= 0: + glyphs_obj = bpy.data.objects.new("glyphs", None) + glyphs_obj.empty_display_type = 'PLAIN_AXES' + fontcollection.objects.link(glyphs_obj) + glyphs_obj.parent = face_obj + elif len(glyphs_objs) > 1: + print(f"found more glyphs objects than expected") + + # now it must exist + glyphs_obj = find_objects_by_name(face_obj.children, startswith="glyphs")[0] + glyphs_obj["face_name"] = obj["face_name"] + glyphs_obj["font_name"] = obj["font_name"] + + # and now parent it! + if obj.parent != glyphs_obj: + obj.parent = glyphs_obj def ShowMessageBox(title = "Message Box", icon = 'INFO', message=""): @@ -66,3 +179,79 @@ def ShowMessageBox(title = "Message Box", icon = 'INFO', message=""): for n in myLines: self.layout.label(text=n) bpy.context.window_manager.popup_menu(draw, title = title, icon = icon) + +def set_text_on_curve(text_properties): + mom = text_properties.text_object + if mom.type != "CURVE": + return False + + glyph_objects = [] + for g in text_properties.glyphs: + glyph_objects.append(g.glyph_object) + + context_override = bpy.context.copy() + context_override["selected_objects"] = list(glyph_objects) + with bpy.context.temp_override(**context_override): + bpy.ops.object.delete() + # bpy.ops.object.delete({"selected_objects": glyph_objects}) + text_properties.glyphs.clear() + +#TODO: fix selection with context_override + previous_selection = bpy.context.selected_objects + bpy.ops.object.select_all(action='DESELECT') + selected_objects = [] + + curve_length = get_curve_length(mom) + advance = 0 + for i, c in enumerate(text_properties.text): + glyph_id = c + glyph = Font.get_glyph(text_properties.font_name, + text_properties.font_face, + glyph_id) + + if glyph == None: + self.report({'ERROR'}, f"Glyph not found for {font_name} {font_face} {glyph_id}") + continue + + ob = bpy.data.objects.new(f"{glyph_id}", glyph.data) + + ob.constraints.new(type='FOLLOW_PATH') + ob.constraints["Follow Path"].target = mom + ob.constraints["Follow Path"].use_fixed_location = True + ob.constraints["Follow Path"].offset_factor = advance / curve_length + ob.constraints["Follow Path"].use_curve_follow = True + ob.constraints["Follow Path"].forward_axis = "FORWARD_X" + ob.constraints["Follow Path"].up_axis = "UP_Y" + + scalor = 0.001 + + glyph_advance = (-1 * glyph.bound_box[0][0] + glyph.bound_box[4][0]) * scalor + text_properties.letter_spacing + + ob.scale = (scalor, scalor, scalor) + mom.users_collection[0].objects.link(ob) + + advance = advance + glyph_advance + + glyph_data = text_properties.glyphs.add() + glyph_data.glyph_id = glyph_id + glyph_data.glyph_object = ob + glyph_data.letter_spacing = 0 + ob.select_set(True) + # selected_objects.append(ob) + # selected_objects.append(mom) + + mom.select_set(True) + bpy.context.view_layer.objects.active = mom + bpy.ops.object.parent_set(type='OBJECT') + # bpy.ops.object.select_all(action='DESELECT') + # for o in previous_selection: + # o.select_set(True) + + # context_override = bpy.context.copy() + # context_override["selected_objects"] = selected_objects + # context_override["active_object"] = mom + # with bpy.context.temp_override(**context_override): + # bpy.ops.object.parent_set(type='OBJECT') + + + return True diff --git a/common/Font.py b/common/Font.py index 24e20fc..5a9ca17 100644 --- a/common/Font.py +++ b/common/Font.py @@ -73,13 +73,13 @@ def add_glyph(font_name, face_name, glyph_id, glyph_object): if not fonts.keys().__contains__(font_name): fonts[font_name] = Font({}) - print("is it has been added", fonts.keys()) + # print("is it has been added", fonts.keys()) if fonts[font_name].faces.get(face_name) == None: fonts[font_name].faces[face_name] = FontFace({}) - print("is it has been added faces", fonts[font_name].faces[face_name]) + # print("is it has been added faces", fonts[font_name].faces[face_name]) if fonts[font_name].faces[face_name].glyphs.get(glyph_id) == None: fonts[font_name].faces[face_name].glyphs[glyph_id] = [] - print("is it has been added glyph", fonts[font_name].faces[face_name].glyphs[glyph_id]) + # print("is it has been added glyph", fonts[font_name].faces[face_name].glyphs[glyph_id]) fonts[font_name].faces[face_name].glyphs.get(glyph_id).append(glyph_object)