pokemon-base/addons/ui_design_tool/scripts/FontManager.gd

241 lines
7.3 KiB
GDScript3
Raw Normal View History

2025-01-26 19:23:56 +00:00
extends Object
const FONT_FILE_PATTERN = "\\.ttf$"
const FONT_WEIGHT_PATTERNS = {
"thin": "(?i)(-|_)thin",
"extra_light": "(?i)(-|_)extralight",
"light": "(?i)(-|_)light",
"regular": "(?i)(-|_)regular",
"medium": "(?i)(-|_)medium",
"semi_bold": "(?i)(-|_)semibold",
"bold": "(?i)(-|_)bold",
"extra_bold": "(?i)(-|_)extrabold",
"black": "(?i)(-|_)black",
"extra_black": "(?i)(-|_)extrablack"
}
const FONT_ITALIC_PATTERN = "(?i)italic"
const FONT_ITALIC_ONLY_PATTERN = "(?i)(-|_)italic"
const FONT_VARIABLE_PATTERN = "(?i)(-|_)variable"
var FONT_FORMATTINGS = {
"Heading 1": FontFormatting.new("light", 96, -3),
"Heading 2": FontFormatting.new("light", 60, -2),
"Heading 3": FontFormatting.new("regular", 48),
"Heading 4": FontFormatting.new("regular", 34, 1),
"Heading 5": FontFormatting.new("regular", 24),
"Heading 6": FontFormatting.new("medium", 20, 1),
"Subtitle 1": FontFormatting.new("regular", 16),
"Subtitle 2": FontFormatting.new("medium", 14, 1),
"Body 1": FontFormatting.new("regular", 16, 1),
"Body 2": FontFormatting.new("regular", 14, 1),
"Button": FontFormatting.new("medium", 14, 1),
"Caption": FontFormatting.new("regular", 12, 1),
"Overline": FontFormatting.new("regular", 10)
} # Typography hierarchy presets, see https://material.io/design/typography/the-type-system.html#type-scale
const DIR_FOLDER_PATTERN = "\\w+(?!.*\\w)"
var font_families = {}
var _font_file_regex = RegEx.new()
var _font_weight_regexes = {
"thin": RegEx.new(),
"extra_light": RegEx.new(),
"light": RegEx.new(),
"regular": RegEx.new(),
"medium": RegEx.new(),
"semi_bold": RegEx.new(),
"bold": RegEx.new(),
"extra_bold": RegEx.new(),
"black": RegEx.new(),
"extra_black": RegEx.new()
}
var _font_italic_regex = RegEx.new()
var _font_italic_only_regex = RegEx.new()
var _font_variable_regex = RegEx.new()
var _dir_folder_regex = RegEx.new()
func _init():
if _font_file_regex.compile(FONT_FILE_PATTERN):
print("Failed to compile ", FONT_FILE_PATTERN)
for font_weight in _font_weight_regexes.keys():
if _font_weight_regexes[font_weight].compile(FONT_WEIGHT_PATTERNS[font_weight]):
print("Failed to compile ", FONT_WEIGHT_PATTERNS[font_weight])
if _font_italic_regex.compile(FONT_ITALIC_PATTERN):
print("Failed to compile ", FONT_ITALIC_PATTERN)
if _font_italic_only_regex.compile(FONT_ITALIC_ONLY_PATTERN):
print("Failed to compile ", FONT_ITALIC_ONLY_PATTERN)
if _font_variable_regex.compile(FONT_VARIABLE_PATTERN):
print("Failed to compile ", FONT_VARIABLE_PATTERN)
if _dir_folder_regex.compile(DIR_FOLDER_PATTERN):
print("Failed to compile ", DIR_FOLDER_PATTERN)
# Load root dir of font resources, check Readme for directory structure
func load_root_dir(root_dir):
var directory = DirAccess.open(root_dir)
var result = DirAccess.get_open_error()
if result == OK:
font_families.clear()
directory.list_dir_begin() # Skip . and .. directory and hidden# TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547
var dir = directory.get_next()
while dir != "":
if not directory.current_is_dir():
dir = directory.get_next()
continue
load_fonts(directory.get_current_dir() + "/" + dir)
dir = directory.get_next()
directory.list_dir_end()
else:
push_warning("UI Design Tool: An error occurred when trying to access %s, ERROR: %d" % [root_dir, result])
return false
return true
# Load fonts data from directory, check Readme for filename pattern
func load_fonts(dir):
var directory = DirAccess.open(dir)
var result = DirAccess.get_open_error()
if result == OK:
var font_family_name = _dir_folder_regex.search(dir).get_string()
var font_family = FontFamily.new(font_family_name)
directory.list_dir_begin()
var filename = directory.get_next()
while filename != "":
if directory.current_is_dir():
filename = directory.get_next()
continue
if _font_file_regex.search(filename):
for font_weight in _font_weight_regexes.keys():
if _font_variable_regex.search(filename): # Godot doesn't support variable font
continue
var abs_dir = directory.get_current_dir() + "/" + filename
if _font_weight_regexes[font_weight].search(filename):
var font_data = load(abs_dir)
if _font_italic_regex.search(filename):
font_family.set_font_face(FontFace.new(font_family.name, font_weight, font_data, FONT_STYLE.ITALIC))
else:
font_family.set_font_face(FontFace.new(font_family.name, font_weight, font_data))
break
else:
# Capture regular italic from {font-name}-italic.ttf
if _font_italic_only_regex.search(filename):
var font_data = load(abs_dir)
font_family.set_font_face(FontFace.new(font_family.name, "regular", font_data, FONT_STYLE.ITALIC))
break
filename = directory.get_next()
directory.list_dir_end()
if not font_family.is_empty():
font_families[font_family.name] = font_family
else:
push_warning("UI Design Tool: Unable to locate usable .ttf files from %s, check README.md for proper directory/filename structure" % dir)
else:
push_warning("UI Design Tool: An error occurred when trying to access %s, ERROR: %d" % [dir, result])
return false
return true
func get_font_face(font_data):
for res in font_families.values():
for font_weight in FONT_WEIGHT.keys():
var font_faces = res.get(font_weight)
for font_face in font_faces.values():
if font_face.data and font_data:
if font_face.data.resource_path == font_data.resource_path:
return font_face
return null
# Find font resource with font name
func get_font_family(font_family_name):
return font_families.get(font_family_name)
static func get_font_style_str(font_style):
return FONT_STYLE.keys()[font_style].to_lower()
# Declaration of font type with font_faces
class FontFamily:
var name = ""
var thin = {}
var extra_light = {}
var light = {}
var regular = {}
var medium = {}
var semi_bold = {}
var bold = {}
var extra_bold = {}
var black = {}
var extra_black = {}
func _init(n):
name = n
func set_font_face(font_face):
var font_faces = get(font_face.font_weight.replace('-', '_'))
font_faces[FONT_STYLE.keys()[font_face.font_style].to_lower()] = font_face
func is_empty():
for font_weight in FONT_WEIGHT.keys():
var font_faces = get(font_weight)
if not font_faces.values().is_empty():
return false
return true
func get_class():
return "FontFamily"
# Font face data, see (https://developer.mozilla.org/my/docs/Web/CSS/@font-face)
class FontFace:
var font_family = ""
var font_weight = ""
var font_style = FONT_STYLE.NORMAL
var data
func _init(ff, fw, d, fs=FONT_STYLE.NORMAL):
font_family = ff
font_weight = fw
font_style = fs
data = d
func get_class():
return "FontFace"
# Declaration of font style TODO: Custom resource to define font style
class FontFormatting:
var font_weight = "regular"
var font_style = 0 # FONT_STYLE.NORMAL
var size = 16
var letter_spacing = 0
func _init(fw, s, ls=0):
font_weight = fw
size = s
letter_spacing = ls
# List of font style, see (https://developer.mozilla.org/my/docs/Web/CSS/font-style)
enum FONT_STYLE {
NORMAL,
ITALIC,
OBLIQUE
}
# List of font weights, see (https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass)
const FONT_WEIGHT = {
"thin": 100,
"extra_light": 200,
"light": 300,
"regular": 400,
"medium": 500,
"semi_bold": 600,
"bold": 700,
"extra_bold": 800,
"black": 900
}