03-19-2025, 10:14 PM
(03-05-2025, 10:44 PM)alvk Wrote:(03-05-2025, 05:26 PM)Ofnuts Wrote: I can help with the API, but not with Scheme... If I had to do something like this, i would rewrite the whole thing in Python...
I agree with you, that Python would be better, but I'm not a programmer and rewriting this code in Python is far beyond my expertise. Anyway, thank you for your suggestions. You helped me a lot.
I took a crack at it, but I have no idea how fonts work in 3.0.
So here's some code that almost works, but can't read fonts or apply vectors

Maybe someone can suggest improvements or fix it from here... 3.0 API is a learning curve.
Also sorry for the bad UI, just threw it together without aesthetics in mind.
Code:
#!/usr/bin/env python3
import sys
from gi.repository import Gimp, GLib
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import Gegl
class text_plugin_window(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Text Label")
self.set_default_size(600, 700) # width, height
# Apply CSS
self.apply_css()
# Main layout
self.main_box = Gtk.VBox(spacing=10)
self.add(self.main_box)
# Scrolled Window
scrolled_window = Gtk.ScrolledWindow()
scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
self.main_box.pack_start(scrolled_window, True, True, 0)
content_box = Gtk.VBox(spacing=10)
content_box.set_border_width(15)
content_box.get_style_context().add_class("content_box")
scrolled_window.add(content_box)
self.input_textview = self.add_labeled_textview(content_box, "Text Input:")
self.font_chooser = Gtk.FontChooserWidget()
self.font_chooser.set_font("Arial")
content_box.pack_start(self.font_chooser, False, False, 0)
self.font_color_button = Gtk.Button(label="Choose Color")
self.font_color_button.connect("clicked", self.on_font_color_button_clicked)
content_box.pack_start(self.font_color_button, False, False, 0)
x_offset_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
x_offset_box.get_style_context().add_class("offset_box")
x_offset_label = Gtk.Label(label="Offset X:")
x_offset_label.get_style_context().add_class("offset_label")
x_offset_box.pack_start(x_offset_label, False, False, 0)
self.x_offset_spin = Gtk.SpinButton.new_with_range(0, 99999, 1)
self.x_offset_spin.set_value(0)
x_offset_box.pack_start(self.x_offset_spin, True, True, 0)
content_box.pack_start(x_offset_box, False, False, 0)
y_offset_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
y_offset_box.get_style_context().add_class("offset_box")
y_offset_label = Gtk.Label(label="Offset Y:")
y_offset_label.get_style_context().add_class("offset_label")
y_offset_box.pack_start(y_offset_label, False, False, 0)
self.y_offset_spin = Gtk.SpinButton.new_with_range(0, 99999, 1)
self.y_offset_spin.set_value(0)
y_offset_box.pack_start(self.y_offset_spin, True, True, 0)
content_box.pack_start(y_offset_box, False, False, 0)
outline_toggle_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
outline_toggle_box.get_style_context().add_class("toggle_box")
outline_label = Gtk.Label(label="Outline:")
outline_toggle_box.pack_start(outline_label, False, False, 0)
self.toggle_outline = Gtk.CheckButton()
outline_toggle_box.pack_start(self.toggle_outline, False, False, 0)
content_box.pack_start(outline_toggle_box, False, False, 0)
self.outline_color_button = Gtk.Button(label="Choose Color")
self.outline_color_button.connect("clicked", self.on_outline_color_button_clicked)
content_box.pack_start(self.outline_color_button, False, False, 0)
outline_thickness_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
x_offset_box.get_style_context().add_class("thick_box")
outline_thickness_label = Gtk.Label(label="Outline Thickness:")
outline_thickness_label.get_style_context().add_class("offset_label")
outline_thickness_box.pack_start(outline_thickness_label, False, False, 0)
self.outline_thickness_spin = Gtk.SpinButton.new_with_range(0, 99999, 1)
self.outline_thickness_spin.set_value(0)
outline_thickness_box.pack_start(self.outline_thickness_spin, True, True, 0)
content_box.pack_start(outline_thickness_box, False, False, 0)
# OK Button
ok_button_box = Gtk.Box(spacing=0)
ok_button_box.get_style_context().add_class("ok_button_box")
ok_button_box.set_border_width(15)
self.ok_button = Gtk.Button(label="OK")
self.ok_button.connect("clicked", self.on_ok_clicked)
self.ok_button.get_style_context().add_class("ok_button")
ok_button_box.pack_start(self.ok_button, True, False, 0)
self.main_box.pack_start(ok_button_box, False, False, 0)
# Set result storage
self.results = []
self.connect("destroy", Gtk.main_quit)
def on_font_color_button_clicked(self, widget):
color_dialog = Gtk.ColorChooserDialog("Select Color", self)
response = color_dialog.run()
if response == Gtk.ResponseType.OK:
rgba = color_dialog.get_rgba()
self.selected_font_color = rgba
color_dialog.destroy()
def on_outline_color_button_clicked(self, widget):
color_dialog = Gtk.ColorChooserDialog("Select Color", self)
response = color_dialog.run()
if response == Gtk.ResponseType.OK:
rgba = color_dialog.get_rgba()
self.selected_outline_color = rgba
color_dialog.destroy()
def apply_css(self):
css = """
window {
background-color: #3C3C3C;
}
label {
color: #DCDCDC;
}
textview {
background-color: #1A1A1A; color: #DCDCDC;
min-height: 100px;
}
.content_box {
padding-left: 20px;
padding-right: 20px;
}
.textview_box {
margin-top: 1rem;
min-height: 100px;
}
.textview_text {
padding: 10px;
border-radius: 3px;
}
.textview_text text {
background-color: #1A1A1A;
color: #DCDCDC;
caret-color: white;
}
.ok_button {
color: #DCDCDC;
background-color: #1A1A1A;
background-image:none;
}
.ok_button:hover {
background-color: #3C3C3C;
}
"""
style_provider = Gtk.CssProvider()
style_provider.load_from_data(css.encode("utf-8"))
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
def add_labeled_textview(self, parent, label_text, default_value=""):
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
box.get_style_context().add_class("textview_box")
label = Gtk.Label(label=label_text)
textview = Gtk.TextView()
textview.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) # Wrap text at characters (ensures horizontal wrapping)
textview.get_style_context().add_class("textview_text")
# Set default text
buffer = textview.get_buffer()
buffer.set_text(default_value)
box.pack_start(label, False, False, 0)
box.pack_start(textview, False, False, 0)
parent.pack_start(box, False, False, 0)
return textview
def get_text_from_textview(self, textview):
buffer = textview.get_buffer()
start_iter = buffer.get_start_iter()
end_iter = buffer.get_end_iter()
return buffer.get_text(start_iter, end_iter, True)
def on_ok_clicked(self, widget):
try:
self.results = {
"text": self.get_text_from_textview(self.input_textview),
# "font": self.font_chooser.get_font() if self.font_chooser.get_font() else "Arial Regular",
"font": self.font_chooser.get_font_desc(),
"font_size": self.font_chooser.get_font_size(),
"font_color": getattr(self, 'selected_font_color', Gdk.RGBA(0, 0, 0, 1)),
"x_offset": self.x_offset_spin.get_value(),
"y_offset": self.y_offset_spin.get_value(),
"outline_toggle": self.toggle_outline.get_active(),
"outline_color": getattr(self, 'selected_outline_color', Gdk.RGBA(1, 1, 1, 1)),
"outline_thickness": self.outline_thickness_spin.get_value()
}
except Exception as e:
Gimp.message(f"Error: {e}")
self.destroy() # Close the window
def run(self):
self.show_all()
Gtk.main()
return self.results
class text_plugin(Gimp.PlugIn):
def do_query_procedures(self):
return [ "nc-text-plugin" ]
def do_set_i18n (self, name):
return False
def do_create_procedure(self, name):
procedure = Gimp.ImageProcedure.new(self, name,
Gimp.PDBProcType.PLUGIN,
self.add_labels, None)
procedure.set_image_types("*")
procedure.set_menu_label("Add Labels...")
procedure.add_menu_path('<Image>/Text Label')
procedure.set_documentation("Adds labels to a selected layer in GIMP",
"Adds labels with optional outline.",
name)
procedure.set_attribution("Nicholas Chenevey", "Nicholas Chenevey", "2025")
return procedure
def add_labels(self, procedure, run_mode, image, drawables, config, run_data):
try:
if run_mode == Gimp.RunMode.INTERACTIVE:
window = text_plugin_window()
result = window.run()
user_text = result["text"]
# I don't know how to get Gimp.Font from this...
try:
font = result["font"]
Gimp.message(f"Family: {font.get_family()}, Style: {font.get_style()}, Weight: {font.get_weight()}, Stretch: {font.get_stretch()}")
return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
except Exception as e:
Gimp.message(f"Error: {e}")
return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
font_parts = result["font"].rsplit(' ', 1)
font_name = font_parts[0]
font_size = int(font_parts[1])
font = Gimp.Font.get_by_name(font_name)
font_color = result["font_color"]
font_color = Gegl.Color.new(font_color.to_string())
x = result["x_offset"]
y = result["y_offset"]
outline_toggle = result["outline_toggle"]
outline_color = result["outline_color"]
outline_color = Gegl.Color.new(outline_color.to_string())
outline_thickness = result["outline_thickness"]
success, off_x, off_y = Gimp.Drawable.get_offsets(drawables[0])
text_layer_x = off_x + x
text_layer_y = off_y + y
Gimp.Image.undo_group_start(image)
Gimp.context_push()
text_layer = Gimp.TextLayer.new(image, user_text, font, font_size, Gimp.Unit.pixel())
image.insert_layer(text_layer, None, -1)
text_layer.set_color(font_color)
if outline_toggle:
text_layer_group = Gimp.GroupLayer.new(image)
text_layer_group.set_name("Text_" + text_layer.get_text())
# text_layer_vectors = image.text_layer_to_vectors(text_layer)
text_layer_outline = text_layer.copy()
text_fg_old = Gimp.Context.get_foreground()
image.insert_layer(text_layer_group, None, -1)
# image.insert_vectors(text_layer_vectors, None, -1)
image.reorder_item(text_layer, text_layer_group, 0)
image.insert_layer(text_layer_outline, text_layer_group, -1)
Gimp.Context.set_foreground(outline_color)
Gimp.Context.set_stroke_method(Gimp.StrokeMethod.PAINT)
Gimp.Context.set_line_width(outline_thickness)
# text_layer_outline.edit_stroke_item(text_layer_vectors)
Gimp.Context.set_foreground(text_fg_old)
text_layer_group.set_expanded(False)
text_layer_outline.set_lock_content(True)
text_layer.set_lock_content(True)
Gimp.Image.undo_group_end(image)
Gimp.displays_flush()
Gimp.context_pop()
except Exception as e:
Gimp.message(f"Error: {font_name} and {e}")
return procedure.new_return_values(Gimp.PDBStatusType.ERROR, GLib.Error())
# return text_layer
return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
Gimp.main(text_plugin.__gtype__, sys.argv)