Compare commits
4 Commits
8413f98f4c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
410fa0a5d3 | ||
|
|
a39851bb61 | ||
|
|
d74a5e1ae4 | ||
| d2d28b0782 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,6 +1,7 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
exports/*
|
.vscode/
|
||||||
|
|
||||||
last_path_cache.txt
|
last_path_cache.txt
|
||||||
*.fontproj
|
guide_values.txt
|
||||||
test export/
|
|
||||||
.vscode/
|
*.fontproj
|
||||||
71
canvas.py
71
canvas.py
@@ -1,15 +1,15 @@
|
|||||||
import base64
|
import base64
|
||||||
import platform
|
|
||||||
import tkinter
|
import tkinter
|
||||||
|
import clipboard
|
||||||
|
|
||||||
GRID_COLOR = "#808080"
|
GRID_COLOR = "#808080"
|
||||||
GRID_GUIDE_COLOR = (128, 128, 255)
|
GRID_GUIDE_COLOR = "#51bbfe"
|
||||||
|
|
||||||
class EditorCanvas(tkinter.Canvas):
|
class EditorCanvas(tkinter.Canvas):
|
||||||
def __init__(self,project,*args,**kwargs):
|
def __init__(self,project,*args,**kwargs):
|
||||||
super().__init__(*args,**kwargs)
|
super().__init__(*args,**kwargs)
|
||||||
self.project=project
|
self.project=project
|
||||||
self.grid_size = 32
|
self.grid_size = 64
|
||||||
self.current_char=33
|
self.current_char=33
|
||||||
self.current_char_pixels=[]
|
self.current_char_pixels=[]
|
||||||
self.current_char_modified=False
|
self.current_char_modified=False
|
||||||
@@ -19,30 +19,32 @@ class EditorCanvas(tkinter.Canvas):
|
|||||||
self.prev_mouse_y=0
|
self.prev_mouse_y=0
|
||||||
self.view_x=0
|
self.view_x=0
|
||||||
self.view_y=0
|
self.view_y=0
|
||||||
|
self.draw_color = 0
|
||||||
|
self.guide_pos = ()
|
||||||
|
|
||||||
|
self.bind("<Button-1>",self.handle_draw_start)
|
||||||
self.bind("<B1-Motion>",self.handle_draw)
|
self.bind("<B1-Motion>",self.handle_draw)
|
||||||
self.bind("<Button-1>",self.handle_draw)
|
|
||||||
self.bind("<Button-2>",self.handle_move_start)
|
self.bind("<Button-2>",self.handle_move_start)
|
||||||
self.bind("<B2-Motion>",self.handle_move)
|
self.bind("<B2-Motion>",self.handle_move)
|
||||||
self.bind("<Button-3>",self.handle_erase)
|
|
||||||
self.bind("<B3-Motion>",self.handle_erase)
|
|
||||||
if platform.system()=="Windows" or platform.system()=="Darwin":
|
|
||||||
self.bind("<MouseWheel>",lambda event: self.handle_zoom(event.delta//120))
|
|
||||||
else:
|
|
||||||
# This will probably work only on X11
|
|
||||||
self.bind("<Button-4>",lambda _: self.handle_zoom(1))
|
|
||||||
self.bind("<Button-5>",lambda _: self.handle_zoom(-1))
|
|
||||||
|
|
||||||
|
def set_guide_pos(self, pos):
|
||||||
|
self.guide_pos = pos
|
||||||
|
self.draw()
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
self.delete("all")
|
self.delete("all")
|
||||||
|
|
||||||
# draw grid
|
# draw grid
|
||||||
for x in range(self.project.char_res[0] + 1):
|
for x in range(self.project.char_res[0] + 1):
|
||||||
x = x * self.grid_size+self.view_x
|
x = x * self.grid_size + self.view_x
|
||||||
self.create_line((x,self.view_y),(x,self.view_y+self.height),width=1,fill=GRID_COLOR)
|
self.create_line((x,self.view_y),(x,self.view_y+self.height),width=1,fill=GRID_COLOR)
|
||||||
|
|
||||||
for y in range(self.project.char_res[1] + 1):
|
for y in range(self.project.char_res[1] + 1):
|
||||||
y = y * self.grid_size+self.view_y
|
temp_color = GRID_GUIDE_COLOR if y in self.guide_pos else GRID_COLOR
|
||||||
self.create_line((self.view_x,y),(self.view_x+self.width,y),width=1,fill=GRID_COLOR)
|
|
||||||
|
y = y * self.grid_size + self.view_y
|
||||||
|
self.create_line((self.view_x,y),(self.view_x+self.width,y),width=1,fill=temp_color)
|
||||||
|
|
||||||
if self.project.does_char_exist(self.current_char) or self.current_char_modified:
|
if self.project.does_char_exist(self.current_char) or self.current_char_modified:
|
||||||
for i in range(len(self.current_char_pixels)):
|
for i in range(len(self.current_char_pixels)):
|
||||||
@@ -55,13 +57,22 @@ class EditorCanvas(tkinter.Canvas):
|
|||||||
self.create_line((self.view_x+self.width,self.view_y),(self.view_x,self.view_y+self.height),width=3,fill="red")
|
self.create_line((self.view_x+self.width,self.view_y),(self.view_x,self.view_y+self.height),width=3,fill="red")
|
||||||
|
|
||||||
|
|
||||||
def handle_draw(self,event):
|
def handle_draw_start(self,event):
|
||||||
pixel_pos=self.cursor_pos_to_pixel((event.x,event.y))
|
pixel_pos = self.cursor_pos_to_pixel((event.x,event.y))
|
||||||
if not pixel_pos:
|
if not pixel_pos:
|
||||||
return
|
return
|
||||||
self.current_char_modified=True
|
self.draw_color = 1 - self.current_char_pixels[pixel_pos[0]*self.project.char_res[1]+pixel_pos[1]]
|
||||||
self.project.modified=True
|
|
||||||
self.current_char_pixels[pixel_pos[0]*self.project.char_res[1]+pixel_pos[1]]=1
|
self.handle_draw(event)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_draw(self,event):
|
||||||
|
pixel_pos = self.cursor_pos_to_pixel((event.x,event.y))
|
||||||
|
if not pixel_pos:
|
||||||
|
return
|
||||||
|
self.current_char_modified = True
|
||||||
|
self.project.modified = True
|
||||||
|
self.current_char_pixels[pixel_pos[0]*self.project.char_res[1]+pixel_pos[1]] = self.draw_color
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
|
|
||||||
@@ -78,18 +89,12 @@ class EditorCanvas(tkinter.Canvas):
|
|||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
|
|
||||||
def handle_erase(self,event):
|
def zoom_by(self,amount):
|
||||||
pixel_pos=self.cursor_pos_to_pixel((event.x,event.y))
|
self.grid_size+=amount
|
||||||
if not pixel_pos:
|
if self.grid_size<12:
|
||||||
return
|
self.grid_size=12
|
||||||
self.current_char_modified=True
|
elif self.grid_size>102:
|
||||||
self.project.modified=True
|
self.grid_size=102
|
||||||
self.current_char_pixels[pixel_pos[0]*self.project.char_res[1]+pixel_pos[1]]=0
|
|
||||||
self.draw()
|
|
||||||
|
|
||||||
|
|
||||||
def handle_zoom(self,delta):
|
|
||||||
self.grid_size+=delta
|
|
||||||
self.width=self.project.char_res[0]*self.grid_size
|
self.width=self.project.char_res[0]*self.grid_size
|
||||||
self.height=self.project.char_res[1]*self.grid_size
|
self.height=self.project.char_res[1]*self.grid_size
|
||||||
self.draw()
|
self.draw()
|
||||||
@@ -174,3 +179,5 @@ class EditorCanvas(tkinter.Canvas):
|
|||||||
self.current_char_pixels=[0 for _ in range(self.project.char_res[0]*self.project.char_res[1])]
|
self.current_char_pixels=[0 for _ in range(self.project.char_res[0]*self.project.char_res[1])]
|
||||||
self.load_char()
|
self.load_char()
|
||||||
|
|
||||||
|
def copy_char(self):
|
||||||
|
clipboard.copy(chr(self.current_char))
|
||||||
|
|||||||
119
main.py
119
main.py
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
import tkinter.filedialog
|
import tkinter.filedialog
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
@@ -15,6 +16,8 @@ WINDOW_SIZE = (
|
|||||||
cross_color = (255, 60, 25)
|
cross_color = (255, 60, 25)
|
||||||
pixel_color = (255,) * 3
|
pixel_color = (255,) * 3
|
||||||
|
|
||||||
|
bg_col = "#1e1e1e"
|
||||||
|
|
||||||
##################
|
##################
|
||||||
|
|
||||||
project=Project()
|
project=Project()
|
||||||
@@ -191,7 +194,7 @@ def button_glyph_search_click():
|
|||||||
|
|
||||||
|
|
||||||
def number_only_validate(val):
|
def number_only_validate(val):
|
||||||
return val.isdigit()
|
return val.isdigit() or val==""
|
||||||
|
|
||||||
|
|
||||||
def hex_only_validate(val):
|
def hex_only_validate(val):
|
||||||
@@ -208,13 +211,26 @@ def update_glyph_preview():
|
|||||||
canvas_preview.delete("all")
|
canvas_preview.delete("all")
|
||||||
canvas_preview.create_text((50,100),text=chr(canvas_editor.current_char),fill="white",font="tkDefaultFont 70")
|
canvas_preview.create_text((50,100),text=chr(canvas_editor.current_char),fill="white",font="tkDefaultFont 70")
|
||||||
name=unicodedata.name(chr(canvas_editor.current_char),"unknown")
|
name=unicodedata.name(chr(canvas_editor.current_char),"unknown")
|
||||||
label_glyph_name.config(text=f"{name} U+{canvas_editor.current_char:04x}")
|
label_glyph_name.config(text=f"{canvas_editor.current_char}\n{name}\nU+{canvas_editor.current_char:04x}")
|
||||||
|
|
||||||
|
|
||||||
|
def canvas_editor_handle_scroll(delta):
|
||||||
|
global canvas_editor
|
||||||
|
global project
|
||||||
|
if not project.loaded:
|
||||||
|
return
|
||||||
|
if delta>0:
|
||||||
|
canvas_editor.prev_glyph()
|
||||||
|
else:
|
||||||
|
canvas_editor.next_glyph()
|
||||||
|
update_glyph_preview()
|
||||||
|
|
||||||
|
|
||||||
window=tkinter.Tk()
|
window=tkinter.Tk()
|
||||||
window.title("fonteditor")
|
window.title("fonteditor")
|
||||||
window.geometry(f"{WINDOW_SIZE[0]}x{WINDOW_SIZE[1]}")
|
window.geometry(f"{WINDOW_SIZE[0]}x{WINDOW_SIZE[1]}")
|
||||||
|
|
||||||
|
|
||||||
menubar=tkinter.Menu(window)
|
menubar=tkinter.Menu(window)
|
||||||
menu_file=tkinter.Menu(menubar,tearoff=False)
|
menu_file=tkinter.Menu(menubar,tearoff=False)
|
||||||
menu_file.add_command(label="New project",command=menu_file_new_project_click)
|
menu_file.add_command(label="New project",command=menu_file_new_project_click)
|
||||||
@@ -225,21 +241,111 @@ menu_file.add_command(label="Save project as",command=lambda: save_project(True)
|
|||||||
menu_export=tkinter.Menu(menubar,tearoff=False)
|
menu_export=tkinter.Menu(menubar,tearoff=False)
|
||||||
menu_export.add_command(label="Export",command=lambda: export_project(False))
|
menu_export.add_command(label="Export",command=lambda: export_project(False))
|
||||||
menu_export.add_command(label="Export as",command=lambda: export_project(True))
|
menu_export.add_command(label="Export as",command=lambda: export_project(True))
|
||||||
|
menu_view=tkinter.Menu(menubar,tearoff=False)
|
||||||
|
menu_view.add_command(label="Zoom in",command=lambda: canvas_editor.zoom_by(10))
|
||||||
|
menu_view.add_command(label="Zoom out",command=lambda: canvas_editor.zoom_by(-10))
|
||||||
menubar.add_cascade(label="File",menu=menu_file)
|
menubar.add_cascade(label="File",menu=menu_file)
|
||||||
menubar.add_cascade(label="Export",menu=menu_export)
|
menubar.add_cascade(label="Export",menu=menu_export)
|
||||||
|
menubar.add_cascade(label="View",menu=menu_view)
|
||||||
|
|
||||||
canvas_editor=EditorCanvas(project,window,bg="black")
|
canvas_editor=EditorCanvas(project,window,bg="black")
|
||||||
canvas_editor.pack(side="left",fill="both",expand=True)
|
canvas_editor.pack(side="left",fill="both",expand=True)
|
||||||
|
if platform.system()=="Windows" or platform.system()=="Darwin":
|
||||||
|
canvas_editor.bind("<MouseWheel>",lambda event: canvas_editor_handle_scroll(event.delta))
|
||||||
|
else:
|
||||||
|
# This will probably work only on X11
|
||||||
|
canvas_editor.bind("<Button-4>",lambda _: canvas_editor_handle_scroll(1))
|
||||||
|
canvas_editor.bind("<Button-5>",lambda _: canvas_editor_handle_scroll(-1))
|
||||||
|
|
||||||
frame_controls=tkinter.Frame(window)
|
|
||||||
|
|
||||||
|
#### Guide pos ####
|
||||||
|
|
||||||
|
frame_label_guide_pos = tkinter.Frame(window, bg=bg_col)
|
||||||
|
frame_label_guide_pos.pack(side="right")
|
||||||
|
|
||||||
|
label_1 = tkinter.Label(frame_label_guide_pos, text="Guide 1 Y", bg=bg_col, fg="#ffffff").pack(side="top")
|
||||||
|
entry_1 = tkinter.Entry(frame_label_guide_pos, validate="all", validatecommand=((window.register(number_only_validate)),"%P"))
|
||||||
|
entry_1.pack(side="top", pady=2)
|
||||||
|
|
||||||
|
label_2 = tkinter.Label(frame_label_guide_pos, text="Guide 2 Y", bg=bg_col, fg="#ffffff").pack(side="top")
|
||||||
|
entry_2 = tkinter.Entry(frame_label_guide_pos, validate="all", validatecommand=((window.register(number_only_validate)),"%P"))
|
||||||
|
entry_2.pack(side="top", pady=2)
|
||||||
|
|
||||||
|
label_3 = tkinter.Label(frame_label_guide_pos, text="Guide 3 Y", bg=bg_col, fg="#ffffff").pack(side="top")
|
||||||
|
entry_3 = tkinter.Entry(frame_label_guide_pos, validate="all", validatecommand=((window.register(number_only_validate)),"%P"))
|
||||||
|
entry_3.pack(side="top", pady=2)
|
||||||
|
|
||||||
|
|
||||||
|
## load last values
|
||||||
|
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
guide_vals_path = os.path.join(cwd, "guide_values.txt")
|
||||||
|
|
||||||
|
|
||||||
|
def get_guide_entries():
|
||||||
|
return (
|
||||||
|
int(entry_1.get()),
|
||||||
|
int(entry_2.get()),
|
||||||
|
int(entry_3.get())
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def update_guide_pos(pos, dont_save=False):
|
||||||
|
canvas_editor.set_guide_pos(pos)
|
||||||
|
|
||||||
|
if dont_save:
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(guide_vals_path, "w") as f:
|
||||||
|
f.write(f"{entry_1.get()}\n{entry_2.get()}\n{entry_3.get()}")
|
||||||
|
|
||||||
|
|
||||||
|
if os.path.exists(guide_vals_path):
|
||||||
|
with open(guide_vals_path, "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
entry_1.insert(0, lines[0].strip())
|
||||||
|
entry_2.insert(0, lines[1].strip())
|
||||||
|
entry_3.insert(0, lines[2].strip())
|
||||||
|
|
||||||
|
update_guide_pos(get_guide_entries(), dont_save=True)
|
||||||
|
|
||||||
|
else:
|
||||||
|
entry_1.insert(0, str(3))
|
||||||
|
entry_2.insert(0, str(5))
|
||||||
|
entry_3.insert(0, str(10))
|
||||||
|
|
||||||
|
update_guide_pos(get_guide_entries())
|
||||||
|
|
||||||
|
|
||||||
|
set_button = tkinter.Button(
|
||||||
|
frame_label_guide_pos,
|
||||||
|
text="Set",
|
||||||
|
command=lambda: update_guide_pos(get_guide_entries())
|
||||||
|
).pack(side="top", pady=[5, 30])
|
||||||
|
|
||||||
|
|
||||||
|
#### Copy ####
|
||||||
|
|
||||||
|
frame = tkinter.Frame(frame_label_guide_pos, bg=bg_col)
|
||||||
|
frame.pack(side="right")
|
||||||
|
|
||||||
|
copy_button = tkinter.Button(frame, text="Copy", command=canvas_editor.copy_char)
|
||||||
|
copy_button.pack(side="top", pady=5)
|
||||||
|
|
||||||
|
|
||||||
|
#### Preview ####
|
||||||
|
frame_controls=tkinter.Frame(frame, bg=bg_col)
|
||||||
frame_controls.pack(side="right")
|
frame_controls.pack(side="right")
|
||||||
|
|
||||||
canvas_preview=tkinter.Canvas(frame_controls,width=100,height=200,bg="black")
|
canvas_preview=tkinter.Canvas(frame_controls,width=100,height=200,bg="black")
|
||||||
canvas_preview.pack(side="top")
|
canvas_preview.pack(side="top")
|
||||||
|
|
||||||
label_glyph_name=tkinter.Label(frame_controls)
|
label_glyph_name=tkinter.Label(frame_controls, bg=bg_col, fg="#ffffff")
|
||||||
label_glyph_name.pack(side="top")
|
label_glyph_name.pack(side="top")
|
||||||
|
|
||||||
|
|
||||||
|
#### Navigation ####
|
||||||
|
|
||||||
frame_nav=tkinter.Frame(frame_controls)
|
frame_nav=tkinter.Frame(frame_controls)
|
||||||
frame_nav.pack(side="top",pady=10)
|
frame_nav.pack(side="top",pady=10)
|
||||||
|
|
||||||
@@ -249,6 +355,9 @@ button_prev_glyph.pack(side="left")
|
|||||||
button_next_glyph=tkinter.Button(frame_nav,width=10,text="Next",command=button_next_glyph_click)
|
button_next_glyph=tkinter.Button(frame_nav,width=10,text="Next",command=button_next_glyph_click)
|
||||||
button_next_glyph.pack(side="left")
|
button_next_glyph.pack(side="left")
|
||||||
|
|
||||||
|
|
||||||
|
#### Glyph search ####
|
||||||
|
|
||||||
frame_glyph_id=tkinter.Frame(frame_controls)
|
frame_glyph_id=tkinter.Frame(frame_controls)
|
||||||
frame_glyph_id.pack(side="top",pady=10)
|
frame_glyph_id.pack(side="top",pady=10)
|
||||||
|
|
||||||
@@ -258,5 +367,5 @@ entry_glyph_id.pack(side="left")
|
|||||||
button_glyph_search=tkinter.Button(frame_glyph_id,width=10,text="Search",command=button_glyph_search_click)
|
button_glyph_search=tkinter.Button(frame_glyph_id,width=10,text="Search",command=button_glyph_search_click)
|
||||||
button_glyph_search.pack(side="left")
|
button_glyph_search.pack(side="left")
|
||||||
|
|
||||||
window.config(menu=menubar)
|
window.config(menu=menubar, bg=bg_col)
|
||||||
window.mainloop()
|
window.mainloop()
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class Project:
|
|||||||
|
|
||||||
|
|
||||||
def load(self,path):
|
def load(self,path):
|
||||||
with open(path, "r") as f:
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
project_data = json.load(f)
|
project_data = json.load(f)
|
||||||
if not "char_width" in project_data or not "char_height" in project_data:
|
if not "char_width" in project_data or not "char_height" in project_data:
|
||||||
raise AttributeError("Character metrics information not found")
|
raise AttributeError("Character metrics information not found")
|
||||||
@@ -37,12 +37,12 @@ class Project:
|
|||||||
|
|
||||||
def save(self,path):
|
def save(self,path):
|
||||||
# save project data to file
|
# save project data to file
|
||||||
with open(path, "w") as f:
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
json.dump({
|
json.dump({
|
||||||
"char_width": self.char_res[0],
|
"char_width": self.char_res[0],
|
||||||
"char_height": self.char_res[1],
|
"char_height": self.char_res[1],
|
||||||
"chars": self.chars
|
"chars": self.chars
|
||||||
}, f, indent=4)
|
}, f, indent=4, ensure_ascii=False)
|
||||||
self.path=path
|
self.path=path
|
||||||
self.loaded=True
|
self.loaded=True
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user