From e592b52b182ad45cb77f7f71ad51ce6c83163888 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Tue, 7 Oct 2025 11:08:09 +0200 Subject: [PATCH] wip --- font_api/font.lua | 125 ++++++++++++++++++++--------------- font_api/fontform.lua | 12 +++- font_api/tools/README.md | 5 ++ font_api/tools/make_font.lua | 22 +++--- 4 files changed, 99 insertions(+), 65 deletions(-) create mode 100644 font_api/tools/README.md diff --git a/font_api/font.lua b/font_api/font.lua index df50a9d..202c198 100644 --- a/font_api/font.lua +++ b/font_api/font.lua @@ -58,6 +58,7 @@ end --- Font class local Font = {} +Font.__index = Font font_api.Font = Font function Font:new(def) @@ -70,31 +71,53 @@ function Font:new(def) local font = table.copy(def) - if font.height == nil or font.height <= 0 then - minetest.log("error", - "[font_api] Font definition must have a positive height.") - return nil - end - - if type(font.widths) == "table" then - if type(font.glyphs) == "table" then - minetest.log("warning", - "[font_api] Ingonring `widths` array in font definition as there is also a `glyphs` array.") - else - minetest.log("warning", - "[font_api] Use of `widths` array in font definition is deprecated, please upgrade to `glyphs`.") - font.glyphs = {} - for codepoint, width in pairs(font.widths) do - font.glyphs[codepoint] = { w = width, c = codepoint } - end - font.widths = nil + -- Version 1 is with one texture per glyph + if font.version == nil or font.version == 1 then + if font.height == nil or font.height <= 0 then + minetest.log("error", + "[font_api] Font definition must have a positive height.") + return nil end + + if type(font.widths) ~= "table" then + minetest.log("error", + "[font_api] Font definition must have a `widths` array.") + return nil + end + + -- Kind of convert to version 2 + + font.glyphs = {} + for codepoint, width in pairs(font.widths) do + font.glyphs[codepoint] = { width, codepoint } + end + font.widths = nil + + font.glyphs_height = font.height + font.height = nil end - if type(font.glyphs) ~= "table" then - minetest.log("error", - "[font_api] Font definition must have a `glyphs` array.") - return nil + -- Version 2 is with one only texture for all glyphs, using `[sheet` textures + if font.version == 2 then + if type(font.glyphs) ~= "table" then + minetest.log("error", + "[font_api] Font definition must have a `glyphs` array.") + return nil + end + + if font.texture_height == nil or font.texture_height <= 0 then + minetest.log("error", + "[font_api] Font definition must have a positive `texture_height`.") + return nil + end + + if font.glyphs_height == nil or font.glyphs_height <= 0 then + minetest.log("error", + "[font_api] Font definition must have a positive `glyphs_height`.") + return nil + end + + font.vertical_number_of_tiles = math.floor(font.texture_height / font.glyphs_height) end if font.glyphs[0] == nil then @@ -103,8 +126,7 @@ function Font:new(def) return nil end - setmetatable(font, self) - self.__index = self + setmetatable(font, self) return font end @@ -149,7 +171,8 @@ end -- @return Char width function Font:get_char_width(codepoint) - return (self.glyphs[codepoint] or self.glyphs[0]).w + -- [1] is char width + return (self.glyphs[codepoint] or self.glyphs[0])[1] end --- Returns texture for a given glyph @@ -157,22 +180,18 @@ end -- @return Texture function Font:get_glyph_texture(glyph) - if glyph.c then + if #glyph == 4 then + -- Actual version with one texture for all glyphs + return string.format("font_%s.png^[sheet:%dx%d:%d,%d", + self.name, glyph[2], self.vertical_number_of_tiles, glyph[3], glyph[4]) + end + if #glyph == 2 then -- Former version with one texture per glyph return string.format("font_%s_%04x.png", - self.name, glyph.c) - end - - if glyph.x == nil or glyph.y == nil then - -- Case of invisible chars like space (no need to add any texture) - return "" + self.name, glyph[2]) end - - --le 5x15 est le nombre de tuiles, pas la taille des tuiles. - return string.format("font_%s.png^[sheet:40x5:%d,%d", - self.name, glyph.x, glyph.y) --- return string.format("font_%s.png^[sheet:%dx%d:%d,%d", --- self.name, glyph.w, self.height, glyph.x, glyph.y) + -- Case of invisible chars like space (no need to add any texture) + return "" end --- Text height for multiline text including margins and line spacing @@ -185,7 +204,7 @@ function Font:get_height(nb_of_lines) if nb_of_lines > 0 then return ( - (self.height or 0) + + self.glyphs_height + (self.margintop or 0) + (self.marginbottom or 0) ) * nb_of_lines + @@ -227,8 +246,8 @@ end --- Render text with the font in a view -- @param text Text to be rendered --- @param texturew Width (in pixels) of the texture (extra text will be truncated) --- @param textureh Height (in pixels) of the texture (extra text will be truncated) +-- @param width Width (in pixels) of the texture (extra text will be truncated) +-- @param height Height (in pixels) of the texture (extra text will be truncated) -- @param style Style of the rendering: -- - lines: maximum number of text lines (if text is limited) -- - halign: horizontal align ("left"/"center"/"right") @@ -236,7 +255,7 @@ end -- - color: color of the text ("#rrggbb") -- @return Texture string -function Font:render(text, texturew, textureh, style) +function Font:render(text, width, height, style) style = style or {} -- Split text into lines (and limit to style.lines # of lines) @@ -256,14 +275,14 @@ function Font:render(text, texturew, textureh, style) local x, y, codepoint local texture = "" - local textheight = self:get_height(#lines) + local text_height = self:get_height(#lines) if style.valign == "top" then y = 0 elseif style.valign == "bottom" then - y = textureh - textheight + y = height - text_height else - y = (textureh - textheight) / 2 + y = (height - text_height) / 2 end y = y + (self.margintop or 0) @@ -272,9 +291,9 @@ function Font:render(text, texturew, textureh, style) if style.halign == "left" then x = 0 elseif style.halign == "right" then - x = texturew - l.width + x = width - l.width else - x = (texturew - l.width) / 2 + x = (width - l.width) / 2 end while l.text ~= '' do @@ -284,17 +303,19 @@ function Font:render(text, texturew, textureh, style) local glyph = self.glyphs[codepoint] -- Add image only if it is visible (at least partly) - if x + glyph.w >= 0 and x <= texturew then - texture = string.format("%s:%d,%d=%s", - texture, x, y, self:get_glyph_texture(glyph):gsub("[\\^:]", "\\%0")) + if x + glyph[1] >= 0 and x <= width then + local glyph_texture = self:get_glyph_texture(glyph):gsub("[\\^:]", "\\%0") + if glyph_texture ~= '' then + texture = string.format("%s:%d,%d=%s", texture, x, y, glyph_texture) + end end - x = x + glyph.w + x = x + glyph[1] end y = y + self:get_height() + (self.linespacing or 0) end - texture = string.format("[combine:%dx%d%s", texturew, textureh, texture) + texture = string.format("[combine:%dx%d%s", width, height, texture) if style.color then texture = texture.."^[colorize:"..style.color end diff --git a/font_api/fontform.lua b/font_api/fontform.lua index 8b36101..04e19b3 100644 --- a/font_api/fontform.lua +++ b/font_api/fontform.lua @@ -103,11 +103,17 @@ local function show_font_formspec(playername) for line = 1, #fonts do local font = font_api.get_font(fonts[line]) - local texture = font:make_text_texture(font.name, font:get_height()*5, - font:get_height()*1.2, 1, "center", "top", "#fff") + local texture = font:render(font.name, + font:get_height()*5, font:get_height()*1.2, { + lines = 1, + valign = "middle", + halign = "center", + color = "#fff" + } + ) fs = string.format( "%simage[0.1,%s;4.5,0.8;%s]button_exit[0,%s;4,1;font_%s;]", - fs, line-0.9, texture, line-1, font.name) + fs, line - 0.9, texture, line - 1, font.name) end minetest.show_formspec(context.playername, modname..':font_list', fs) end diff --git a/font_api/tools/README.md b/font_api/tools/README.md new file mode 100644 index 0000000..34b466f --- /dev/null +++ b/font_api/tools/README.md @@ -0,0 +1,5 @@ +# Debian installation + +```shell +apt install lua5.4 imagemagick fonttools +``` diff --git a/font_api/tools/make_font.lua b/font_api/tools/make_font.lua index 10a14bf..d2f4ad5 100644 --- a/font_api/tools/make_font.lua +++ b/font_api/tools/make_font.lua @@ -79,8 +79,8 @@ local function compute_tile_sizes(texture_size) return results end --- This will give enough combinations (720 is 2 * 2 * 2 * 2 * 3 * 3 * 5) -tile_widths = compute_tile_sizes(720) +-- This will give enough combinations (360 is 2 * 2 * 2 * 3 * 3 * 5) +tile_widths = compute_tile_sizes(360) -- Table width has to be sorted table.sort(tile_widths) @@ -177,9 +177,11 @@ local glyph_xs = {} -- x for each glyph local glyph_ys = {} -- y for each glyph local glyph_ns = {} -- n of tiles in sheet for each glyph (=texturewidth / tilewidth) +local texture_height + local function make_final_texture(filename) - local texture_height = font_height + texture_height = font_height local x = 0 -- cursor x local glyph_y = 0 @@ -209,7 +211,6 @@ local function make_final_texture(filename) "convert -channel alpha -colorspace gray -size %dx%d xc:transparent %s", texture_width, texture_height, filename )) - print(texture_width, texture_height) for codepoint, n in pairs(glyph_ns) do local w = texture_width // n @@ -232,11 +233,10 @@ local function make_final_texture(filename) end -- Place subtexure in texture cmd = string.format("convert %s \\( %s -repage +%d+%d \\) -flatten %s", filename, cmd, x, y, filename) - print (cmd) command(cmd) end - command(string.format("convert %s -threshold 50%% %s", filename, filename)) + command(string.format("convert %s -channel alpha -threshold 50%% %s", filename, filename)) end @@ -307,22 +307,24 @@ file:write(string.format([[ font_api.register_font( '%s', { + version = 2, default = true, margintop = 3, linespacing = -2, - height = %d, + texture_height = %d, + glyphs_height = %d, glyphs = { ]], - fontname, font_height) + fontname, texture_height, font_height) ) for codepoint, w in pairs(glyph_widths) do local x = glyph_xs[codepoint] local y = glyph_ys[codepoint] local n = glyph_ns[codepoint] if x ~= nil and y ~=nil and n ~= nil then - file:write(string.format(" [%d] = { w = %d, n = %f, x = %d, y = %d },\n", codepoint, w, n, x, y)) + file:write(string.format(" [%d] = { %d, %d, %d, %d },\n", codepoint, w, n, x, y)) else - file:write(string.format(" [%d] = { w = %d },\n", codepoint, w)) + file:write(string.format(" [%d] = { %d },\n", codepoint, w)) end end file:write([[