Added a python tool to create and edit fonts and added diacritics to french translations (#168)
* Update translations.h Adding diacritics File encoding is Latin-1 (ISO 8859-1) * rg_gui: adding missing translations * rg_gui: adding missing translations * moving converter to tool folder Creating basic UI * font_converter : upgrading UI adding settings fields for the user * fonts: adding source fonts I might delete some of them later... * font_converter: updated the tool it's now working properly (sort of) ! * create basic C font renderer I now have to code the part where the user can edit the C file using the canvas like some sort of Windows Paint * font_converter: few tweaks made the code a bit shorter and more readable * font_converter: reverse change that caused an issue * C_font_editor: Now work !!! * font_converter: some tweaks on file output comments * Font tools: solved issue when exporting char with empty data (such as space) * font_converter: Added zoom function + made the tools full screen * font_converter: now can handle 'space' character * font toolss: added a way to exclude some characters and updated file header * font_tool: fixing error with file output * rg_gui: new font renderer for LVGL type font * Removing old font and adding "LVGL" verabold * rg_gui: removing bit per pixel option * rg_gui: renaming a struct to match the rest of the code * font_tools: replaced treshold by bit shifting * fonttool: updated the generator to match the new font format * Font_converter: now generate decent results * fonttools: adding some fonts + somes tweaks * font_editor: works again with the new format ! * font_tools: small tweaks * rg_gui: added back vertical stretching * rg_gui: moved output specific data inside the if(output) * rg_gui: small tweak * font_tools: small tweaks on advance_width * font_editor: new gui * font_editor: improved ergonomics * Renamed edit_c_font to font_editor * Add support for old format to font_converter.py * Missing commas in font_converter.py's output * Fixed memory usage calculation in font_converter * Auto generate data when selecting a font file Automatically generate the canvas when loading a font. Also fixed: Cancelling the file selector will no longer clear the font name * Draw bounding box after the pixels, so that it's fully visible * Do not save empty bitmaps * Added save dialog to font converter * Replaced the blue dot with a box representing the real footprint of the glyph * Converted translations.h to UTF-8 * Made font_editor save in UTF-8 * Removed new format support in font_converter I'm still working on adapting font_editor... * Add option to ensure that font size is respected * font_converter can now load C fonts, and font_editor now imports its load/save abilities * Fixed typo * Fixed max height and memory calculations in font_editor * Blue bounding box should show the actual height taken by the glyph (aka the max height) * Preserve the real padding in the rendered glyph * Reduce ofs_y when possible * Regenerated fonts with font_converter.py I'm still tweaking font_converter.py but the result is pretty close to the previous fonts. In some cases the line height is increased because of diatrics unfortunately... * Restore upstream fonts to resolve conflict * Resolving conflicts * Fixed some glyphs were clipped on the left * Fix conflicts * More logic to reduce max_height * Renamed Original_fonts to just fonts * Removed the mention of the old tool now that we have our own --------- Co-authored-by: Raphael Texier <157415568+raphatex@users.noreply.github.com> Co-authored-by: Alex Duchesne <ducalex007@gmail.com>
This commit is contained in:
parent
f357884a28
commit
f6fb3be2ca
@ -2,12 +2,7 @@
|
||||
|
||||
/**
|
||||
* This file can be edited to add fonts to retro-go.
|
||||
* There is a tool to convert ttf to prop fonts there:
|
||||
* https://github.com/loboris/ESP32_TFT_library/tree/master/tools
|
||||
*
|
||||
* But you will need to modify its output:
|
||||
* - The header must be removed (the first 4 bytes of data)
|
||||
* - All codepoints must be changed to 16bits (insert a 0x00 byte after the first byte of each character)
|
||||
* To create new fonts you can use font_converter.py located in the tools folder.
|
||||
*/
|
||||
|
||||
extern const rg_font_t font_basic8x8;
|
||||
|
||||
@ -48,10 +48,6 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
[RG_LANG_EN] = "Select file",
|
||||
[RG_LANG_FR] = "Choisissez un fichier",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Off",
|
||||
[RG_LANG_FR] = "Off",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Language",
|
||||
[RG_LANG_FR] = "Langue",
|
||||
@ -62,11 +58,11 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "For these changes to take effect you must restart your device.\nrestart now?",
|
||||
[RG_LANG_FR] = "Pour que ces changements prennent effet, vous devez redemmarer votre appareil.\nRedemmarer maintenant ?",
|
||||
[RG_LANG_FR] = "Pour que ces changements prennent effet, vous devez redémmarer votre appareil.\nRedémmarer maintenant ?",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Wi-Fi profile",
|
||||
[RG_LANG_FR] = "Profile Wi-Fi",
|
||||
[RG_LANG_FR] = "Profil Wi-Fi",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Language",
|
||||
@ -82,7 +78,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Reset all settings?",
|
||||
[RG_LANG_FR] = "Reset tous les parametres",
|
||||
[RG_LANG_FR] = "Reset tous les paramètres",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Initializing...",
|
||||
@ -138,7 +134,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Input",
|
||||
[RG_LANG_FR] = "Entree",
|
||||
[RG_LANG_FR] = "Entrée",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Crop",
|
||||
@ -178,19 +174,19 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Download complete!",
|
||||
[RG_LANG_FR] = "Telechargement termine",
|
||||
[RG_LANG_FR] = "Téléchargement terminé",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Reboot to flash?",
|
||||
[RG_LANG_FR] = "Redemarrer",
|
||||
[RG_LANG_FR] = "Redémarrer",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Available Releases",
|
||||
[RG_LANG_FR] = "Maj disponibles",
|
||||
[RG_LANG_FR] = "Maj disponible",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Received empty list!",
|
||||
[RG_LANG_FR] = "Liste vide recue",
|
||||
[RG_LANG_FR] = "Liste vide reçue",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Gamma Boost",
|
||||
@ -226,7 +222,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "RTC config",
|
||||
[RG_LANG_FR] = "Congig RTC",
|
||||
[RG_LANG_FR] = "Config RTC",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "SRAM autosave",
|
||||
@ -262,7 +258,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Crop sides",
|
||||
[RG_LANG_FR] = "Couper les cotes",
|
||||
[RG_LANG_FR] = "Couper les côtés",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Sprite limit",
|
||||
@ -278,11 +274,15 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Profile",
|
||||
[RG_LANG_FR] = "Profile",
|
||||
[RG_LANG_FR] = "Profil",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "<profile name>",
|
||||
[RG_LANG_FR] = "<nom du profil>",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Controls",
|
||||
[RG_LANG_FR] = "Controles",
|
||||
[RG_LANG_FR] = "Contrôles",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Audio enable",
|
||||
@ -347,23 +347,19 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "System activity",
|
||||
[RG_LANG_FR] = "Activite systeme",
|
||||
[RG_LANG_FR] = "Activité système",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Disk activity",
|
||||
[RG_LANG_FR] = "Activite stockage",
|
||||
[RG_LANG_FR] = "Activité stockage",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Low battery",
|
||||
[RG_LANG_FR] = "Battery basse",
|
||||
[RG_LANG_FR] = "Batterie basse",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Default",
|
||||
[RG_LANG_FR] = "Default",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "none",
|
||||
[RG_LANG_FR] = "Aucun",
|
||||
[RG_LANG_FR] = "Défaut",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "<None>",
|
||||
@ -373,7 +369,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
// Wifi
|
||||
{
|
||||
[RG_LANG_EN] = "Not connected",
|
||||
[RG_LANG_FR] = "Non connecte",
|
||||
[RG_LANG_FR] = "Non connecté",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Connecting...",
|
||||
@ -393,7 +389,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Start access point?\n\nSSID: retro-go\nPassword: retro-go\n\nBrowse: http://192.168.4.1/",
|
||||
[RG_LANG_FR] = "Demarrer point d'acces?\n\nSSID: retro-go\nPassword: retro-go\n\nAdresse: http://192.168.4.1/",
|
||||
[RG_LANG_FR] = "Démarrer point d'accès?\n\nSSID: retro-go\nPassword: retro-go\n\nAdresse: http://192.168.4.1/",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Wi-Fi enable",
|
||||
@ -401,11 +397,11 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Wi-Fi access point",
|
||||
[RG_LANG_FR] = "Point d'acces WiFi",
|
||||
[RG_LANG_FR] = "Point d'accès WiFi",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Network",
|
||||
[RG_LANG_FR] = "Reseau",
|
||||
[RG_LANG_FR] = "Réseau",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "IP address",
|
||||
@ -415,7 +411,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
// retro-go settings
|
||||
{
|
||||
[RG_LANG_EN] = "Brightness",
|
||||
[RG_LANG_FR] = "Luminosite",
|
||||
[RG_LANG_FR] = "Luminosité",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Volume",
|
||||
@ -431,7 +427,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Theme",
|
||||
[RG_LANG_FR] = "Theme",
|
||||
[RG_LANG_FR] = "Thème",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Show clock",
|
||||
@ -479,7 +475,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Target",
|
||||
[RG_LANG_FR] = "Cible",
|
||||
[RG_LANG_FR] = "Appareil",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Website",
|
||||
@ -499,7 +495,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Reset settings",
|
||||
[RG_LANG_FR] = "Reset parametres",
|
||||
[RG_LANG_FR] = "Reset paramètres",
|
||||
},
|
||||
|
||||
// save slot
|
||||
@ -582,6 +578,10 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
[RG_LANG_EN] = "Hide",
|
||||
[RG_LANG_FR] = "Cacher",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Tabs Visibility",
|
||||
[RG_LANG_FR] = "Visibilitée onglets",
|
||||
},
|
||||
|
||||
// scroll modes
|
||||
{
|
||||
@ -640,21 +640,25 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
|
||||
// launcher options
|
||||
{
|
||||
[RG_LANG_EN] = "Launcher Options",
|
||||
[RG_LANG_FR] = "Options du lanceur",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Color theme",
|
||||
[RG_LANG_FR] = "Couleurs",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Preview",
|
||||
[RG_LANG_FR] = "Apercu",
|
||||
[RG_LANG_FR] = "Aperçu",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Scroll mode",
|
||||
[RG_LANG_FR] = "Mode defilement",
|
||||
[RG_LANG_FR] = "Mode défilement",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Start screen",
|
||||
[RG_LANG_FR] = "Ecran demarrage",
|
||||
[RG_LANG_FR] = "Ecran démarrage",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Hide tabs",
|
||||
@ -666,7 +670,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Startup app",
|
||||
[RG_LANG_FR] = "App demarrage",
|
||||
[RG_LANG_FR] = "App démarrage",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Build CRC cache",
|
||||
@ -674,7 +678,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Check for updates",
|
||||
[RG_LANG_FR] = "Check for updates",
|
||||
[RG_LANG_FR] = "Verifier mise à jour",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "HTTP Server Busy...",
|
||||
@ -686,7 +690,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Storage mount failed.\nMake sure the card is FAT32.",
|
||||
[RG_LANG_FR] = "Erreur montage SD.\nVerifiez que la carte est en FAT32.",
|
||||
[RG_LANG_FR] = "Erreur carte SD.\nLa carte est bien en FAT32 ?",
|
||||
},
|
||||
// end of main.c
|
||||
|
||||
@ -719,7 +723,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "File not found",
|
||||
[RG_LANG_FR] = "Fichier non present",
|
||||
[RG_LANG_FR] = "Fichier non présent",
|
||||
},
|
||||
|
||||
// rom options
|
||||
@ -749,7 +753,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "File properties",
|
||||
[RG_LANG_FR] = "Propriete",
|
||||
[RG_LANG_FR] = "Propriétés fichier",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Delete selected file?",
|
||||
@ -780,7 +784,7 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Properties",
|
||||
[RG_LANG_FR] = "Proprietes",
|
||||
[RG_LANG_FR] = "Propriétés",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Resume",
|
||||
@ -804,11 +808,11 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Reset all settings",
|
||||
[RG_LANG_FR] = "Reset parametres",
|
||||
[RG_LANG_FR] = "Reset paramètres",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Reboot to factory ",
|
||||
[RG_LANG_FR] = "Redemarrer usine",
|
||||
[RG_LANG_FR] = "Redémarrer usine",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Reboot to launcher",
|
||||
@ -816,11 +820,11 @@ static const char *translations[][RG_LANG_MAX] =
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Recovery mode",
|
||||
[RG_LANG_FR] = "Mode de recuperation",
|
||||
[RG_LANG_FR] = "Mode de recupération",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "System Panic!",
|
||||
[RG_LANG_FR] = "Plantage systeme!",
|
||||
[RG_LANG_FR] = "Plantage système!",
|
||||
},
|
||||
{
|
||||
[RG_LANG_EN] = "Save failed",
|
||||
|
||||
463
tools/font_converter.py
Normal file
463
tools/font_converter.py
Normal file
@ -0,0 +1,463 @@
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from tkinter import Tk, Label, Entry, StringVar, Button, Frame, Canvas, filedialog, ttk, Checkbutton, IntVar
|
||||
import os
|
||||
import re
|
||||
|
||||
################################ - Font format - ################################
|
||||
#
|
||||
# font:
|
||||
# |
|
||||
# ├── glyph_bitmap[] -> 8 bit array containing the bitmap data for all glyph
|
||||
# |
|
||||
# └── glyph_data[] -> struct that contains all the data to correctly draw the glyph
|
||||
#
|
||||
######################## - Explanation of glyph_bitmap[] - #######################
|
||||
# First, let's see an example : '!'
|
||||
#
|
||||
# we are going to convert glyph_bitmap[] bytes to binary :
|
||||
# 11111111,
|
||||
# 11111111,
|
||||
# 11000111,
|
||||
# 11100000,
|
||||
#
|
||||
# then we rearrange them :
|
||||
# [3 bits wide]
|
||||
# 111
|
||||
# 111
|
||||
# 111
|
||||
# [9 111 We clearly reconize '!' character
|
||||
# bits 111
|
||||
# tall] 111
|
||||
# 000
|
||||
# 111
|
||||
# 111
|
||||
# (000000)
|
||||
#
|
||||
# Second example with '0' :
|
||||
# 0x30,0x04,0x07,0x09,0x00,0x07,
|
||||
# 0x7D,0xFB,0xBF,0x7E,0xFD,0xFB,0xFF,0x7C,
|
||||
#
|
||||
# - width = 0x07 = 7
|
||||
# - height = 0x09 = 9
|
||||
# - data[n] = 0x7D,0xFB,0xBF,0x7E,0xFD,0xFB,0xFF,0x7C
|
||||
#
|
||||
# in binary :
|
||||
# 1111101
|
||||
# 11111011
|
||||
# 10111111
|
||||
# 1111110
|
||||
# 11111101
|
||||
# 11111011
|
||||
# 11111111
|
||||
# 1111100
|
||||
#
|
||||
# We see that everything is not aligned so we add zeros ON THE LEFT :
|
||||
# ->01111101
|
||||
# 11111011
|
||||
# 10111111
|
||||
# ->01111110
|
||||
# 11111101
|
||||
# 11111011
|
||||
# 11111111
|
||||
# ->01111100
|
||||
|
||||
# Next, we rearrange the bits :
|
||||
# [ 7 bits wide]
|
||||
# 0111110
|
||||
# 1111110
|
||||
# 1110111
|
||||
# [9 1110111
|
||||
# bits 1110111 we can reconize '0' (if you squint a loooot)
|
||||
# tall] 1110111
|
||||
# 1110111
|
||||
# 1111111
|
||||
# 0111110
|
||||
# (0)
|
||||
#
|
||||
# And that's basically how characters are encoded using this tool
|
||||
|
||||
# Example usage (defaults parameters)
|
||||
list_char_ranges_init = "32-126, 160-255"
|
||||
font_size_init = 11
|
||||
|
||||
font_path = ("arial.ttf") # Replace with your TTF font path
|
||||
|
||||
# Variables to track panning
|
||||
start_x = 0
|
||||
start_y = 0
|
||||
|
||||
def get_char_list():
|
||||
list_char = []
|
||||
for intervals in list_char_ranges.get().split(','):
|
||||
first = intervals.split('-')[0]
|
||||
# we check if we the user input is a single char or an interval
|
||||
try:
|
||||
second = intervals.split('-')[1]
|
||||
except IndexError:
|
||||
list_char.append(int(first))
|
||||
else:
|
||||
second = intervals.split('-')[1]
|
||||
for char in range(int(first), int(second) + 1):
|
||||
list_char.append(char)
|
||||
return list_char
|
||||
|
||||
def find_bounding_box(image):
|
||||
pixels = image.load()
|
||||
width, height = image.size
|
||||
x_min, y_min = width, height
|
||||
x_max, y_max = 0, 0
|
||||
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if pixels[x, y] >= 1: # Looking for 'on' pixels
|
||||
x_min = min(x_min, x)
|
||||
y_min = min(y_min, y)
|
||||
x_max = max(x_max, x)
|
||||
y_max = max(y_max, y)
|
||||
|
||||
if x_min > x_max or y_min > y_max: # No target pixels found
|
||||
return None
|
||||
return (x_min, y_min, x_max+1, y_max+1)
|
||||
|
||||
def load_ttf_font(font_path, font_size):
|
||||
# Load the TTF font
|
||||
enforce_font_size = enforce_font_size_bool.get()
|
||||
pil_font = ImageFont.truetype(font_path, font_size)
|
||||
|
||||
font_name = ' '.join(pil_font.getname())
|
||||
font_data = []
|
||||
|
||||
for char_code in get_char_list():
|
||||
char = chr(char_code)
|
||||
|
||||
image = Image.new("1", (font_size * 2, font_size * 2), 0) # generate mono bmp, 0 = black color
|
||||
draw = ImageDraw.Draw(image)
|
||||
# Draw at pos 1 otherwise some glyphs are clipped. we remove the added offset below
|
||||
draw.text((1, 0), char, font=pil_font, fill=255)
|
||||
|
||||
bbox = find_bounding_box(image) # Get bounding box
|
||||
|
||||
if bbox is None: # control character / space
|
||||
width, height = 0, 0
|
||||
offset_x, offset_y = 0, 0
|
||||
else:
|
||||
x0, y0, x1, y1 = bbox
|
||||
width, height = x1 - x0, y1 - y0
|
||||
offset_x, offset_y = x0, y0
|
||||
if offset_x:
|
||||
offset_x -= 1
|
||||
|
||||
try: # Get the real glyph width including padding on the right that the box will remove
|
||||
adv_w = int(draw.textlength(char, font=pil_font))
|
||||
adv_w = max(adv_w, width + offset_x)
|
||||
except:
|
||||
adv_w = width + offset_x
|
||||
|
||||
# Shift or crop glyphs that would be drawn beyond font_size. Most glyphs are not affected by this.
|
||||
# If enforce_font_size is false, then max_height will be calculated at the end and the font might
|
||||
# be taller than requested.
|
||||
if enforce_font_size and offset_y + height > font_size:
|
||||
print(f" font_size exceeded: {offset_y+height}")
|
||||
if font_size - height >= 0:
|
||||
offset_y = font_size - height
|
||||
else:
|
||||
offset_y = 0
|
||||
height = font_size
|
||||
|
||||
# Extract bitmap data
|
||||
cropped_image = image.crop(bbox)
|
||||
bitmap = []
|
||||
row = 0
|
||||
i = 0
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if i == 8:
|
||||
bitmap.append(row)
|
||||
row = 0
|
||||
i = 0
|
||||
pixel = 1 if cropped_image.getpixel((x, y)) else 0
|
||||
row = (row << 1) | pixel
|
||||
i += 1
|
||||
bitmap.append(row << 8-i) # to "fill" with zero the remaining empty bits
|
||||
bitmap = bitmap[0:int((width * height + 7) / 8)]
|
||||
|
||||
# Create glyph entry
|
||||
glyph_data = {
|
||||
"char_code": char_code,
|
||||
"ofs_y": int(offset_y),
|
||||
"box_w": int(width),
|
||||
"box_h": int(height),
|
||||
"ofs_x": int(offset_x),
|
||||
"adv_w": int(adv_w),
|
||||
"bitmap": bitmap,
|
||||
}
|
||||
font_data.append(glyph_data)
|
||||
|
||||
# The font render glyphs at font_size but they can shift them up or down which will cause the max_height
|
||||
# to exceed font_size. It's not desirable to remove the padding entirely (the "enforce" option above),
|
||||
# but there are some things we can do to reduce the discrepency without affecting the look.
|
||||
max_height = max(g["ofs_y"] + g["box_h"] for g in font_data)
|
||||
if max_height > font_size:
|
||||
min_ofs_y = min((g["ofs_y"] if g["box_h"] > 0 else 1000) for g in font_data)
|
||||
for key, glyph in enumerate(font_data):
|
||||
offset = glyph["ofs_y"]
|
||||
# If there's a consistent excess of top padding across all glyphs, we can remove it
|
||||
if min_ofs_y > 0 and offset >= min_ofs_y:
|
||||
offset -= min_ofs_y
|
||||
# In some fonts like Vera and DejaVu we can shift _ and | to gain an extra pixel
|
||||
if chr(glyph["char_code"]) in ["_", "|"] and offset + glyph["box_h"] > font_size and offset > 0:
|
||||
offset -= 1
|
||||
font_data[key]["ofs_y"] = offset
|
||||
|
||||
max_height = max(g["ofs_y"] + g["box_h"] for g in font_data)
|
||||
|
||||
print(f"Glyphs: {len(font_data)}, font_size: {font_size}, max_height: {max_height}")
|
||||
|
||||
return (font_name, font_size, font_data)
|
||||
|
||||
def load_c_font(file_path):
|
||||
# Load the C font
|
||||
font_name = "Unknown"
|
||||
font_size = 0
|
||||
font_data = []
|
||||
|
||||
with open(file_path, 'r', encoding='UTF-8') as file:
|
||||
text = file.read()
|
||||
text = re.sub('//.*?$|/\*.*?\*/', '', text, flags=re.S|re.MULTILINE)
|
||||
text = re.sub('[\n\r\t\s]+', ' ', text)
|
||||
# FIXME: Handle parse errors...
|
||||
if m := re.search('\.name\s*=\s*"(.+)",', text):
|
||||
font_name = m.group(1)
|
||||
if m := re.search('\.height\s*=\s*(\d+),', text):
|
||||
font_size = int(m.group(1))
|
||||
if m := re.search('\.data\s*=\s*\{(.+?)\}', text):
|
||||
hexdata = [int(h, base=16) for h in re.findall('0x[0-9A-Fa-f]{2}', text)]
|
||||
|
||||
while len(hexdata):
|
||||
char_code = hexdata[0] | (hexdata[1] << 8)
|
||||
if not char_code:
|
||||
break
|
||||
ofs_y = hexdata[2]
|
||||
box_w = hexdata[3]
|
||||
box_h = hexdata[4]
|
||||
ofs_x = hexdata[5]
|
||||
adv_w = hexdata[6]
|
||||
bitmap = hexdata[7:int((box_w * box_h + 7) / 8) + 7]
|
||||
|
||||
glyph_data = {
|
||||
"char_code": char_code,
|
||||
"ofs_y": ofs_y,
|
||||
"box_w": box_w,
|
||||
"box_h": box_h,
|
||||
"ofs_x": ofs_x,
|
||||
"adv_w": adv_w,
|
||||
"bitmap": bitmap,
|
||||
}
|
||||
font_data.append(glyph_data)
|
||||
|
||||
hexdata = hexdata[7 + len(bitmap):]
|
||||
|
||||
return (font_name, font_size, font_data)
|
||||
|
||||
def generate_font_data():
|
||||
if font_path.endswith(".c"):
|
||||
font_name, font_size, font_data = load_c_font(font_path)
|
||||
else:
|
||||
font_name, font_size, font_data = load_ttf_font(font_path, int(font_height_input.get()))
|
||||
|
||||
window.title(f"Font preview: {font_name} {font_size}")
|
||||
font_height_input.set(font_size)
|
||||
|
||||
max_height = max(font_size, max(g["ofs_y"] + g["box_h"] for g in font_data))
|
||||
bounding_box = bounding_box_bool.get()
|
||||
|
||||
canvas.delete("all")
|
||||
offset_x_1 = 1
|
||||
offset_y_1 = 1
|
||||
|
||||
for glyph_data in font_data:
|
||||
offset_y = glyph_data["ofs_y"]
|
||||
width = glyph_data["box_w"]
|
||||
height = glyph_data["box_h"]
|
||||
offset_x = glyph_data["ofs_x"]
|
||||
adv_w = glyph_data["adv_w"]
|
||||
|
||||
if offset_x_1+adv_w+1 > canva_width:
|
||||
offset_x_1 = 1
|
||||
offset_y_1 += max_height + 1
|
||||
|
||||
byte_index = 0
|
||||
byte_value = 0
|
||||
bit_index = 0
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if bit_index == 0:
|
||||
byte_value = glyph_data["bitmap"][byte_index]
|
||||
byte_index += 1
|
||||
if byte_value & (1 << 7-bit_index):
|
||||
canvas.create_rectangle((x+offset_x_1+offset_x)*p_size, (y+offset_y_1+offset_y)*p_size, (x+offset_x_1+offset_x)*p_size+p_size, (y+offset_y_1+offset_y)*p_size+p_size,fill="white")
|
||||
bit_index += 1
|
||||
bit_index %= 8
|
||||
|
||||
if bounding_box:
|
||||
canvas.create_rectangle((offset_x_1+offset_x)*p_size, (offset_y_1+offset_y)*p_size, (width+offset_x_1+offset_x)*p_size, (height+offset_y_1+offset_y)*p_size, width=1, outline="red", fill='') # bounding box
|
||||
canvas.create_rectangle((offset_x_1)*p_size, (offset_y_1)*p_size, (offset_x_1+adv_w)*p_size, (offset_y_1+max_height)*p_size, width=1, outline='blue', fill='')
|
||||
|
||||
offset_x_1 += adv_w + 1
|
||||
|
||||
return (font_name, font_size, font_data)
|
||||
|
||||
def save_font_data():
|
||||
font_name, font_size, font_data = generate_font_data()
|
||||
|
||||
filename = filedialog.asksaveasfilename(
|
||||
title='Save Font',
|
||||
initialdir=os.getcwd(),
|
||||
initialfile=f"{font_name.replace('-', '_').replace(' ', '')}{font_size}",
|
||||
defaultextension=".c",
|
||||
filetypes=(('Retro-Go Font', '*.c'), ('All files', '*.*')))
|
||||
|
||||
if filename:
|
||||
with open(filename, 'w', encoding='UTF-8') as f:
|
||||
f.write(generate_c_font(font_name, font_size, font_data))
|
||||
|
||||
def generate_c_font(font_name, font_size, font_data):
|
||||
normalized_name = f"{font_name.replace('-', '_').replace(' ', '')}{font_size}"
|
||||
max_height = max(font_size, max(g["ofs_y"] + g["box_h"] for g in font_data))
|
||||
memory_usage = sum(len(g["bitmap"]) + 8 for g in font_data)
|
||||
|
||||
file_data = "#include \"../rg_gui.h\"\n\n"
|
||||
file_data += "// File generated with font_converter.py (https://github.com/ducalex/retro-go/tree/dev/tools)\n\n"
|
||||
file_data += f"// Font : {font_name}\n"
|
||||
file_data += f"// Point Size : {font_size}\n"
|
||||
file_data += f"// Memory usage : {memory_usage} bytes\n"
|
||||
file_data += f"// # characters : {len(font_data)}\n\n"
|
||||
file_data += f"const rg_font_t font_{normalized_name} = {{\n"
|
||||
file_data += f" .name = \"{font_name}\",\n"
|
||||
file_data += f" .type = 1,\n"
|
||||
file_data += f" .width = 0,\n"
|
||||
file_data += f" .height = {max_height},\n"
|
||||
file_data += f" .chars = {len(font_data)},\n"
|
||||
file_data += f" .data = {{\n"
|
||||
for glyph in font_data:
|
||||
char_code = glyph['char_code']
|
||||
header_data = [char_code & 0xFF, char_code >> 8, glyph['ofs_y'], glyph['box_w'],
|
||||
glyph['box_h'], glyph['ofs_x'], glyph['adv_w']]
|
||||
file_data += f" /* U+{char_code:04X} '{chr(char_code)}' */\n "
|
||||
file_data += ", ".join([f"0x{byte:02X}" for byte in header_data])
|
||||
file_data += f",\n "
|
||||
if len(glyph["bitmap"]) > 0:
|
||||
file_data += ", ".join([f"0x{byte:02X}" for byte in glyph["bitmap"]])
|
||||
file_data += f","
|
||||
file_data += "\n"
|
||||
file_data += "\n"
|
||||
file_data += " // Terminator\n"
|
||||
file_data += " 0x00, 0x00,\n"
|
||||
file_data += " },\n"
|
||||
file_data += "};\n"
|
||||
|
||||
return file_data
|
||||
|
||||
def select_file():
|
||||
filename = filedialog.askopenfilename(
|
||||
title='Load Font',
|
||||
initialdir=os.getcwd(),
|
||||
filetypes=(('True Type Font', '*.ttf'), ('Retro-Go Font', '*.c'), ('All files', '*.*')))
|
||||
|
||||
if filename:
|
||||
global font_path
|
||||
font_path = filename
|
||||
generate_font_data()
|
||||
|
||||
# Function to zoom in and out on the canvas
|
||||
def zoom(event):
|
||||
scale = 1.0
|
||||
if event.delta > 0: # Scroll up to zoom in
|
||||
scale = 1.2
|
||||
elif event.delta < 0: # Scroll down to zoom out
|
||||
scale = 0.8
|
||||
|
||||
# Get the canvas size and adjust scale based on cursor position
|
||||
canvas.scale("all", event.x, event.y, scale, scale)
|
||||
|
||||
# Update the scroll region to reflect the new scale
|
||||
canvas.configure(scrollregion=canvas.bbox("all"))
|
||||
|
||||
def start_pan(event):
|
||||
global start_x, start_y
|
||||
# Record the current mouse position
|
||||
start_x = event.x
|
||||
start_y = event.y
|
||||
|
||||
def pan_canvas(event):
|
||||
global start_x, start_y
|
||||
# Calculate the distance moved
|
||||
dx = start_x - event.x
|
||||
dy = start_y - event.y
|
||||
|
||||
# Scroll the canvas
|
||||
canvas.move("all", -dx, -dy)
|
||||
|
||||
# Update the starting position
|
||||
start_x = event.x
|
||||
start_y = event.y
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
window = Tk()
|
||||
window.title("Retro-Go Font Converter")
|
||||
|
||||
# Get screen width and height
|
||||
screen_width = window.winfo_screenwidth()
|
||||
screen_height = window.winfo_screenheight()
|
||||
# Set the window size to fill the entire screen
|
||||
window.geometry(f"{screen_width}x{screen_height}")
|
||||
|
||||
p_size = 8 # pixel size on the renderer
|
||||
|
||||
canva_width = screen_width//p_size
|
||||
canva_height = screen_height//p_size-16
|
||||
|
||||
frame = Frame(window)
|
||||
frame.pack(anchor="center", padx=10, pady=2)
|
||||
|
||||
# choose font button (file picker)
|
||||
choose_font_button = ttk.Button(frame, text='Choose font', command=select_file)
|
||||
choose_font_button.pack(side="left", padx=5)
|
||||
|
||||
# Label and Entry for Font height
|
||||
Label(frame, text="Font height").pack(side="left", padx=5)
|
||||
font_height_input = StringVar(value=str(font_size_init))
|
||||
Entry(frame, textvariable=font_height_input, width=4).pack(side="left", padx=5)
|
||||
|
||||
# Variable to hold the state of the checkbox
|
||||
enforce_font_size_bool = IntVar() # 0 for unchecked, 1 for checked
|
||||
Checkbutton(frame, text="Enforce size", variable=enforce_font_size_bool).pack(side="left", padx=5)
|
||||
|
||||
# Label and Entry for Char ranges to include
|
||||
Label(frame, text="Ranges to include").pack(side="left", padx=5)
|
||||
list_char_ranges = StringVar(value=str(list_char_ranges_init))
|
||||
Entry(frame, textvariable=list_char_ranges, width=30).pack(side="left", padx=5)
|
||||
|
||||
# Variable to hold the state of the checkbox
|
||||
bounding_box_bool = IntVar(value=1) # 0 for unchecked, 1 for checked
|
||||
Checkbutton(frame, text="Bounding box", variable=bounding_box_bool).pack(side="left", padx=10)
|
||||
|
||||
# Button to launch the font generation function
|
||||
b1 = Button(frame, text="Preview", width=14, height=2, background="blue", foreground="white", command=generate_font_data)
|
||||
b1.pack(side="left", padx=5)
|
||||
|
||||
# Button to launch the font exporting function
|
||||
b1 = Button(frame, text="Save", width=14, height=2, background="blue", foreground="white", command=save_font_data)
|
||||
b1.pack(side="left", padx=5)
|
||||
|
||||
frame = Frame(window).pack(anchor="w", padx=2, pady=2)
|
||||
canvas = Canvas(frame, width=canva_width*p_size, height=canva_height*p_size, bg="black")
|
||||
canvas.configure(scrollregion=(0, 0, canva_width*p_size, canva_height*p_size))
|
||||
canvas.bind("<MouseWheel>", zoom)
|
||||
canvas.bind("<ButtonPress-1>", start_pan) # Start panning
|
||||
canvas.bind("<B1-Motion>",pan_canvas)
|
||||
canvas.focus_set()
|
||||
canvas.pack(fill="both", expand=True)
|
||||
|
||||
window.mainloop()
|
||||
391
tools/font_editor.py
Normal file
391
tools/font_editor.py
Normal file
@ -0,0 +1,391 @@
|
||||
from tkinter import Tk, Button, Frame, Canvas, filedialog, Checkbutton, IntVar, Label, StringVar, Entry, DISABLED, NORMAL
|
||||
from font_converter import load_c_font, generate_c_font
|
||||
import os
|
||||
|
||||
font_size = 14
|
||||
char_code_edit = ord('R')
|
||||
selected_glyph = 0
|
||||
|
||||
list_bbox = [] # ((cc, x0, y0, x1, y1), (cc, x0, y0, x1, y1), ...) used to find the correct glyph on the canva
|
||||
list_glyph_data = dict() # contain font data for all glyphs
|
||||
|
||||
lastrect_xy = (0,0) # used for sliding function
|
||||
|
||||
def renderCfont():
|
||||
canvas.delete("all")
|
||||
global select_box
|
||||
select_box = canvas.create_rectangle(0,0,p_size,p_size, width=2, outline="blue")
|
||||
|
||||
global list_bbox
|
||||
list_bbox = []
|
||||
|
||||
offset_x_1 = 1
|
||||
offset_y_1 = 1
|
||||
|
||||
max_height_local = 1
|
||||
|
||||
global list_glyph_data
|
||||
|
||||
# we get the char list to render
|
||||
if list_char_render.get() != "":
|
||||
list_char_code_render = [ord(i) for i in (list_char_render.get())]
|
||||
else:
|
||||
list_char_code_render = list(list_glyph_data.keys())
|
||||
|
||||
for char_code in list_char_code_render:
|
||||
offset_y = list_glyph_data[char_code]['ofs_y']
|
||||
width = list_glyph_data[char_code]['box_w']
|
||||
height = list_glyph_data[char_code]['box_h']
|
||||
offset_x = list_glyph_data[char_code]['ofs_x']
|
||||
xDelta = list_glyph_data[char_code]['adv_w']
|
||||
|
||||
max_height_local = max(max_height_local, offset_y + height)
|
||||
|
||||
offset_x_1 += offset_x
|
||||
|
||||
if bounding_box_bool.get():
|
||||
canvas.create_rectangle((offset_x_1)*p_size, (offset_y_1+offset_y)*p_size, (width+offset_x_1)*p_size, (height+offset_y_1+offset_y)*p_size, width=1, outline="red",fill='') # bounding box
|
||||
|
||||
bbox = (
|
||||
char_code,
|
||||
offset_x_1,
|
||||
offset_y_1+offset_y,
|
||||
width+offset_x_1,
|
||||
height+offset_y_1+offset_y
|
||||
)
|
||||
|
||||
byte_list = list_glyph_data[char_code]["bitmap"]
|
||||
if byte_list:
|
||||
bitmap_index = 0
|
||||
bit_index = 0
|
||||
byte = byte_list[bitmap_index]
|
||||
modulo_8 = (True if width*height%8 == 0 else False)
|
||||
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if byte & 0b10000000: # Pixel(x,y) = 1
|
||||
canvas.create_rectangle((x+offset_x_1)*p_size, (y+offset_y_1+offset_y)*p_size, (x+offset_x_1)*p_size+p_size, (y+offset_y_1+offset_y)*p_size+p_size,fill="white")
|
||||
if bit_index == 7:
|
||||
bit_index = 0
|
||||
bitmap_index += 1
|
||||
if not (modulo_8 and y == height-1):
|
||||
byte = byte_list[bitmap_index]
|
||||
else:
|
||||
byte = byte << 1 # we shift data[n] to get the next pixel on the most significant bit
|
||||
bit_index += 1
|
||||
|
||||
if offset_x_1+3*xDelta <= canva_width:
|
||||
offset_x_1 += xDelta
|
||||
else:
|
||||
offset_x_1 = 1
|
||||
offset_y_1 += max_height_local
|
||||
max_height_local = 1
|
||||
|
||||
list_bbox.append(bbox)
|
||||
|
||||
|
||||
def render_single_char():
|
||||
canvas_1.delete("all")
|
||||
|
||||
# we also have to clear the matrix
|
||||
global rect_ids
|
||||
rect_ids = []
|
||||
for y in range(40):
|
||||
line = [-1] * 40
|
||||
rect_ids.append(line)
|
||||
|
||||
global char_code_edit
|
||||
char_code_edit = ord(char_to_edit.get())
|
||||
global list_glyph_data
|
||||
|
||||
offset_y = list_glyph_data[char_code_edit]['ofs_y']
|
||||
width = list_glyph_data[char_code_edit]['box_w']
|
||||
height = list_glyph_data[char_code_edit]['box_h']
|
||||
offset_x = list_glyph_data[char_code_edit]['ofs_x']
|
||||
advance_width = list_glyph_data[char_code_edit]['adv_w']
|
||||
|
||||
max_size = max(width+offset_x, height+offset_y)
|
||||
canvas_width = (screen_width // 4)
|
||||
canvas_height = (screen_height // 2)
|
||||
|
||||
global p_size_c
|
||||
p_size_c = min(canvas_width, canvas_height) // max_size - 1
|
||||
|
||||
# ov is the small pixel that stick to the mouse, we want to keep this one
|
||||
global ov
|
||||
ov = canvas_1.create_rectangle(0,0,p_size_c,p_size_c,fill="white")
|
||||
|
||||
glyph_data_text = (
|
||||
'width : ' + str(width) + '\n' +
|
||||
'height : ' + str(height) + '\n' +
|
||||
'offset_x : ' + str(offset_x) + '\n' +
|
||||
'offset_y : ' + str(offset_y) + '\n' +
|
||||
'advance_width : ' + str(advance_width))
|
||||
output.config(text = glyph_data_text)
|
||||
|
||||
global single_bbox
|
||||
single_bbox = (offset_x, offset_y, offset_x+width, offset_y+height)
|
||||
|
||||
bit_index = 0
|
||||
|
||||
if bounding_box_bool.get():
|
||||
canvas_1.create_rectangle((offset_x)*p_size_c, (offset_y)*p_size_c, (width+offset_x)*p_size_c, (height+offset_y)*p_size_c, width=1, outline="red",fill='') # bounding box
|
||||
|
||||
byte_list = list_glyph_data[char_code_edit]["bitmap"]
|
||||
bitmap_index = 0
|
||||
byte = byte_list[bitmap_index]
|
||||
modulo_8 = (True if width*height%8 == 0 else False)
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if byte & 0b10000000: # Pixel(x,y) = 1
|
||||
rect_ids[y+offset_y][x+offset_x] = canvas_1.create_rectangle(
|
||||
(x+offset_x)*p_size_c,
|
||||
(y+offset_y)*p_size_c,
|
||||
(x+offset_x)*p_size_c+p_size_c,
|
||||
(y+offset_y)*p_size_c+p_size_c,fill="white")
|
||||
|
||||
if bit_index == 7:
|
||||
bit_index = 0
|
||||
bitmap_index += 1
|
||||
if not (modulo_8 and y == height-1):
|
||||
byte = byte_list[bitmap_index]
|
||||
|
||||
else:
|
||||
byte = byte << 1 # we shift data[n] to get the next pixel on the most significant bit
|
||||
bit_index += 1
|
||||
|
||||
|
||||
def update_glyph_data():
|
||||
x0, y0, x1, y1 = single_bbox
|
||||
height = y1 - y0
|
||||
width = x1 - x0
|
||||
|
||||
global char_code_edit
|
||||
|
||||
list_glyph_data[char_code_edit]["bitmap"] = []
|
||||
|
||||
row = 0
|
||||
i = 0
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
pixel = (1 if rect_ids[y + y0][x + x0] != -1 else 0)
|
||||
if i == 8:
|
||||
list_glyph_data[char_code_edit]["bitmap"].append(row)
|
||||
row = 0
|
||||
i = 0
|
||||
row = (row << 1) | pixel
|
||||
i += 1
|
||||
|
||||
row = row << 8-i # to "fill" with zero the remaining empty bits
|
||||
list_glyph_data[char_code_edit]["bitmap"].append(row)
|
||||
|
||||
save_font()
|
||||
renderCfont()
|
||||
|
||||
|
||||
def save_font():
|
||||
global list_glyph_data
|
||||
global font_path
|
||||
font_name = os.path.splitext(os.path.basename(font_path))[0]
|
||||
with open(font_path, 'w', encoding='UTF-8') as f:
|
||||
f.write(generate_c_font(font_name, font_size, list_glyph_data.values()))
|
||||
|
||||
|
||||
def extract_data():
|
||||
global list_glyph_data
|
||||
list_glyph_data.clear()
|
||||
|
||||
font_name, font_size, font_data = load_c_font(font_path)
|
||||
for glyph in font_data:
|
||||
list_glyph_data[glyph["char_code"]] = glyph
|
||||
|
||||
b1.config(state=NORMAL)
|
||||
b3.config(state=NORMAL)
|
||||
b4.config(state=NORMAL)
|
||||
renderCfont()
|
||||
|
||||
|
||||
def select_file():
|
||||
filetypes = (
|
||||
('c font', '*.c'),
|
||||
('All files', '*.*')
|
||||
)
|
||||
|
||||
filename = filedialog.askopenfilename(
|
||||
title='Open a c Font',
|
||||
initialdir=os.getcwd(),
|
||||
filetypes=filetypes)
|
||||
|
||||
global font_path
|
||||
font_path = filename
|
||||
extract_data()
|
||||
|
||||
|
||||
def motion(event):
|
||||
x = event.x // p_size
|
||||
y = event.y // p_size
|
||||
|
||||
global selected_glyph
|
||||
|
||||
for bbox in list_bbox:
|
||||
cc, x0, y0, x1, y1 = bbox
|
||||
if x >= x0 and y >= y0 and x <= x1 and y <= y1:
|
||||
canvas.coords(select_box, x0*p_size-1, y0*p_size-1, x1*p_size+1, y1*p_size+1)
|
||||
selected_glyph = cc
|
||||
|
||||
|
||||
def click(event):
|
||||
global char_to_edit
|
||||
global selected_glyph
|
||||
char_to_edit.set(chr(selected_glyph))
|
||||
render_single_char()
|
||||
|
||||
|
||||
def motion_1(event):
|
||||
global x
|
||||
global y
|
||||
x = event.x
|
||||
y = event.y
|
||||
if x%p_size_c != 0:
|
||||
x-= x%p_size_c
|
||||
if y%p_size_c != 0:
|
||||
y-= y%p_size_c
|
||||
canvas_1.itemconfig(ov, fill="White")
|
||||
canvas_1.coords(ov, x, y, x+p_size_c, y+p_size_c)
|
||||
|
||||
|
||||
def click_1(event):
|
||||
x_pixel = x//p_size_c
|
||||
y_pixel = y//p_size_c
|
||||
if rect_ids[y_pixel][x_pixel] == -1:
|
||||
rect_ids[y_pixel][x_pixel] = canvas_1.create_rectangle(x, y, x + p_size_c, y + p_size_c, fill="white")
|
||||
else:
|
||||
canvas_1.delete(rect_ids[y_pixel][x_pixel])
|
||||
rect_ids[y_pixel][x_pixel] = -1
|
||||
canvas_1.itemconfig(ov, fill="Black") # Changes the fill color to black to "hide" it
|
||||
|
||||
|
||||
def slide_1(event):
|
||||
global x
|
||||
global y
|
||||
global lastrect_xy
|
||||
x = event.x
|
||||
y = event.y
|
||||
if x%p_size_c != 0:
|
||||
x-= x%p_size_c
|
||||
if y%p_size_c != 0:
|
||||
y-= y%p_size_c
|
||||
canvas_1.coords(ov, x, y, x+p_size_c, y+p_size_c)
|
||||
|
||||
x_pixel = x//p_size_c
|
||||
y_pixel = y//p_size_c
|
||||
if rect_ids[y_pixel][x_pixel] != lastrect_xy:
|
||||
if rect_ids[y_pixel][x_pixel] == -1:
|
||||
rect_ids[y_pixel][x_pixel] = canvas_1.create_rectangle(x, y, x + p_size_c, y + p_size_c, fill="white")
|
||||
else:
|
||||
canvas_1.delete(rect_ids[y_pixel][x_pixel])
|
||||
rect_ids[y_pixel][x_pixel] = -1
|
||||
lastrect_xy = (y_pixel,x_pixel)
|
||||
|
||||
|
||||
window = Tk()
|
||||
window.title("C font editor")
|
||||
|
||||
# TODO : make it dynamic
|
||||
p_size = 6 # pixel size on the global renderer
|
||||
p_size_c = 24 # pixel size on the single char renderer
|
||||
|
||||
# Get screen width and height
|
||||
screen_width = window.winfo_screenwidth()
|
||||
screen_height = window.winfo_screenheight()
|
||||
# Set the window size to fill the entire screen
|
||||
window.geometry(f"{screen_width}x{screen_height}")
|
||||
|
||||
char_edit_windows_width = (screen_width // 4)//p_size_c
|
||||
char_edit_windows_height = (screen_height // 2)//p_size_c
|
||||
|
||||
canva_width = (screen_width-screen_width // 4)//p_size
|
||||
canva_height = screen_height//p_size-16
|
||||
|
||||
frame = Frame(window)
|
||||
frame.pack(anchor="center", padx=10, pady=2)
|
||||
|
||||
rect_ids = [] # This is gonna be used to store rectangles ids
|
||||
for y in range(40):
|
||||
line = [-1] * 40 # Create a new list for each row
|
||||
rect_ids.append(line)
|
||||
|
||||
########## top ##########
|
||||
# choose font button
|
||||
choose_font_button = Button(frame, text='Choose C font', width=16, height=2, background="blue", foreground="white", command=select_file)
|
||||
choose_font_button.pack(side="left", padx=5)
|
||||
|
||||
# Variable to hold the state of the checkbox
|
||||
bounding_box_bool = IntVar() # 0 for unchecked, 1 for checked
|
||||
checkbox = Checkbutton(frame, text="Bounding box", variable=bounding_box_bool)
|
||||
checkbox.pack(side="left", padx=5)
|
||||
|
||||
# Label and Entry for String to render
|
||||
Label(frame, text="String to render:").pack(side="left", padx=5)
|
||||
list_char_render = StringVar(value=str(""))
|
||||
Entry(frame, textvariable=list_char_render, width=50).pack(side="left", padx=5)
|
||||
|
||||
b1 = Button(frame, text="Render", width=12, height=2, background="blue", foreground="white", command=renderCfont)
|
||||
b1.pack(side="left", padx=5)
|
||||
########## end of top ##########
|
||||
|
||||
########## bottom ##########
|
||||
frame_bottom = Frame(window)
|
||||
frame_bottom.pack(anchor="s", padx=2, pady=2)
|
||||
|
||||
##### left side #####
|
||||
frame_left = Frame(frame_bottom)
|
||||
frame_left.pack(anchor="center", side="left", padx=2, pady=2)
|
||||
|
||||
# Label and Entry for Chars to render
|
||||
Label(frame_left, text="Character to edit").pack(side="top", pady=2)
|
||||
char_to_edit = StringVar(value=str("R"))
|
||||
Entry(frame_left, textvariable=char_to_edit, width=4).pack(side="top", pady=2)
|
||||
|
||||
b3 = Button(frame_left, text="render", width=12, height=2, background="blue", foreground="white", command=render_single_char)
|
||||
b3.pack(side="top", padx=5)
|
||||
|
||||
# display the glyph data
|
||||
Label(frame_left, text="Glyph data :").pack(side="top", pady=2)
|
||||
output = Label(frame_left, height = 5, width = 25, bg = "light cyan")
|
||||
output.pack(side="top", padx=5)
|
||||
|
||||
b4 = Button(frame_left, text="save", width=12, height=2, background="green", foreground="white", command=update_glyph_data)
|
||||
b4.pack(side="top", padx=5)
|
||||
|
||||
# disable buttons until a font is loaded
|
||||
b1.config(state=DISABLED)
|
||||
b3.config(state=DISABLED)
|
||||
b4.config(state=DISABLED)
|
||||
|
||||
canvas_1 = Canvas(frame_left, width=char_edit_windows_width*p_size_c, height=char_edit_windows_height*p_size_c, bg="black")
|
||||
canvas_1.pack(side="left", padx=5)
|
||||
|
||||
# ov is the small pixel that 'stick' to the mouse
|
||||
ov = canvas_1.create_rectangle(0,0,p_size_c,p_size_c,fill="white")
|
||||
|
||||
canvas_1.focus_set()
|
||||
canvas_1.bind('<Motion>', motion_1)
|
||||
canvas_1.bind("<Button 1>",click_1)
|
||||
canvas_1.bind("<B1-Motion>",slide_1)
|
||||
##### end of left side #####
|
||||
|
||||
##### right side #####
|
||||
frame_right = Frame(frame_bottom)
|
||||
frame_right.pack(side="right", padx=2, pady=2)
|
||||
canvas = Canvas(frame_right, width=canva_width*p_size, height=canva_height*p_size, bg="black")
|
||||
canvas.pack(anchor="n", side="left", padx=5)
|
||||
canvas.focus_set()
|
||||
canvas.bind('<Motion>', motion)
|
||||
canvas.bind("<Button 1>",click)
|
||||
|
||||
select_box = canvas.create_rectangle(0,0,p_size,p_size, width=2, outline="blue")
|
||||
##### end of right side #####
|
||||
########## end of bottom ##########
|
||||
|
||||
window.mainloop()
|
||||
201
tools/fonts/Apache License.txt
Normal file
201
tools/fonts/Apache License.txt
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
124
tools/fonts/COPYRIGHT.TXT
Normal file
124
tools/fonts/COPYRIGHT.TXT
Normal file
@ -0,0 +1,124 @@
|
||||
Bitstream Vera Fonts Copyright
|
||||
|
||||
The fonts have a generous copyright, allowing derivative works (as
|
||||
long as "Bitstream" or "Vera" are not in the names), and full
|
||||
redistribution (so long as they are not *sold* by themselves). They
|
||||
can be be bundled, redistributed and sold with any software.
|
||||
|
||||
The fonts are distributed under the following copyright:
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
|
||||
Vera is a trademark of Bitstream, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the fonts accompanying this license ("Fonts") and associated
|
||||
documentation files (the "Font Software"), to reproduce and distribute
|
||||
the Font Software, including without limitation the rights to use,
|
||||
copy, merge, publish, distribute, and/or sell copies of the Font
|
||||
Software, and to permit persons to whom the Font Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright and trademark notices and this permission notice
|
||||
shall be included in all copies of one or more of the Font Software
|
||||
typefaces.
|
||||
|
||||
The Font Software may be modified, altered, or added to, and in
|
||||
particular the designs of glyphs or characters in the Fonts may be
|
||||
modified and additional glyphs or characters may be added to the
|
||||
Fonts, only if the fonts are renamed to names not containing either
|
||||
the words "Bitstream" or the word "Vera".
|
||||
|
||||
This License becomes null and void to the extent applicable to Fonts
|
||||
or Font Software that has been modified and is distributed under the
|
||||
"Bitstream Vera" names.
|
||||
|
||||
The Font Software may be sold as part of a larger software package but
|
||||
no copy of one or more of the Font Software typefaces may be sold by
|
||||
itself.
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
||||
BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
|
||||
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
|
||||
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the names of Gnome, the Gnome
|
||||
Foundation, and Bitstream Inc., shall not be used in advertising or
|
||||
otherwise to promote the sale, use or other dealings in this Font
|
||||
Software without prior written authorization from the Gnome Foundation
|
||||
or Bitstream Inc., respectively. For further information, contact:
|
||||
fonts at gnome dot org.
|
||||
|
||||
Copyright FAQ
|
||||
=============
|
||||
|
||||
1. I don't understand the resale restriction... What gives?
|
||||
|
||||
Bitstream is giving away these fonts, but wishes to ensure its
|
||||
competitors can't just drop the fonts as is into a font sale system
|
||||
and sell them as is. It seems fair that if Bitstream can't make money
|
||||
from the Bitstream Vera fonts, their competitors should not be able to
|
||||
do so either. You can sell the fonts as part of any software package,
|
||||
however.
|
||||
|
||||
2. I want to package these fonts separately for distribution and
|
||||
sale as part of a larger software package or system. Can I do so?
|
||||
|
||||
Yes. A RPM or Debian package is a "larger software package" to begin
|
||||
with, and you aren't selling them independently by themselves.
|
||||
See 1. above.
|
||||
|
||||
3. Are derivative works allowed?
|
||||
Yes!
|
||||
|
||||
4. Can I change or add to the font(s)?
|
||||
Yes, but you must change the name(s) of the font(s).
|
||||
|
||||
5. Under what terms are derivative works allowed?
|
||||
|
||||
You must change the name(s) of the fonts. This is to ensure the
|
||||
quality of the fonts, both to protect Bitstream and Gnome. We want to
|
||||
ensure that if an application has opened a font specifically of these
|
||||
names, it gets what it expects (though of course, using fontconfig,
|
||||
substitutions could still could have occurred during font
|
||||
opening). You must include the Bitstream copyright. Additional
|
||||
copyrights can be added, as per copyright law. Happy Font Hacking!
|
||||
|
||||
6. If I have improvements for Bitstream Vera, is it possible they might get
|
||||
adopted in future versions?
|
||||
|
||||
Yes. The contract between the Gnome Foundation and Bitstream has
|
||||
provisions for working with Bitstream to ensure quality additions to
|
||||
the Bitstream Vera font family. Please contact us if you have such
|
||||
additions. Note, that in general, we will want such additions for the
|
||||
entire family, not just a single font, and that you'll have to keep
|
||||
both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
|
||||
glyphs to the font, they must be stylistically in keeping with Vera's
|
||||
design. Vera cannot become a "ransom note" font. Jim Lyles will be
|
||||
providing a document describing the design elements used in Vera, as a
|
||||
guide and aid for people interested in contributing to Vera.
|
||||
|
||||
7. I want to sell a software package that uses these fonts: Can I do so?
|
||||
|
||||
Sure. Bundle the fonts with your software and sell your software
|
||||
with the fonts. That is the intent of the copyright.
|
||||
|
||||
8. If applications have built the names "Bitstream Vera" into them,
|
||||
can I override this somehow to use fonts of my choosing?
|
||||
|
||||
This depends on exact details of the software. Most open source
|
||||
systems and software (e.g., Gnome, KDE, etc.) are now converting to
|
||||
use fontconfig (see www.fontconfig.org) to handle font configuration,
|
||||
selection and substitution; it has provisions for overriding font
|
||||
names and subsituting alternatives. An example is provided by the
|
||||
supplied local.conf file, which chooses the family Bitstream Vera for
|
||||
"sans", "serif" and "monospace". Other software (e.g., the XFree86
|
||||
core server) has other mechanisms for font substitution.
|
||||
|
||||
BIN
tools/fonts/DejaVuSans.ttf
Normal file
BIN
tools/fonts/DejaVuSans.ttf
Normal file
Binary file not shown.
BIN
tools/fonts/OpenSans-Bold.ttf
Normal file
BIN
tools/fonts/OpenSans-Bold.ttf
Normal file
Binary file not shown.
BIN
tools/fonts/OpenSans-Regular.ttf
Normal file
BIN
tools/fonts/OpenSans-Regular.ttf
Normal file
Binary file not shown.
BIN
tools/fonts/OpenSans-Semibold.ttf
Normal file
BIN
tools/fonts/OpenSans-Semibold.ttf
Normal file
Binary file not shown.
BIN
tools/fonts/Vera-Bold.ttf
Normal file
BIN
tools/fonts/Vera-Bold.ttf
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user