From f3970f641eb60bf92492b6715084273cc1bceb74 Mon Sep 17 00:00:00 2001 From: Thomas--S Date: Thu, 1 Feb 2018 16:54:55 +0100 Subject: Seperate signs API from signs definitions Change modnames from *_lib to *_api --- font_api/init.lua | 343 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 font_api/init.lua (limited to 'font_api/init.lua') diff --git a/font_api/init.lua b/font_api/init.lua new file mode 100644 index 0000000..f407f8b --- /dev/null +++ b/font_api/init.lua @@ -0,0 +1,343 @@ +--[[ + font_api mod for Minetest - Library to add font display capability + to display_api mod. + (c) Pierre-Yves Rollo + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +--]] + +-- Global variables +------------------- + +font_api = {} +font_api.name = minetest.get_current_modname() +font_api.path = minetest.get_modpath(font_api.name) +font_api.registered_fonts = {} + +-- Local variables +------------------ + +local default_font = false + +-- Local functions +------------------ + +-- Split multiline text into array of lines, with maximum lines. + +local function split_lines(text, maxlines) + local splits = text:split("\n") + if maxlines then + local lines = {} + for num = 1,maxlines do + lines[num] = splits[num] + end + return lines + else + return splits + end +end + +-- Gets a default (settings or fist font) + +local function get_default_font() + -- First call + if default_font == false then + default_font = nil + + -- First, try with settings + local settings_font = minetest.settings:get("default_font") + + if settings_font ~= nil and settings_font ~= "" then + default_font = font_api.registered_fonts[settings_font] + + if default_font == nil then + minetest.log("warning", "Default font in settings (\"".. + settings_font.."\") is not registered.") + end + end + + -- If failed, choose first font + if default_font == nil then + for _, font in pairs(font_api.registered_fonts) do + default_font = font + break + end + end + + -- Error, no font registered + if default_font == nil then + minetest.log("error", + "No font registred, unable to choose a default font.") + end + end + + return default_font +end + +-- Returns font properties to be used according to font_name + +local function get_font(font_name) + local font = font_api.registered_fonts[font_name] + + if font == nil then + local message + + if font_name == nil then + message = "No font given" + else + message = "Font \""..font_name.."\" unregistered" + end + + font = get_default_font() + + if font ~= nil then + minetest.log("info", message..", using font \""..font.name.."\".") + end + end + + return font +end + +-- Returns next char, managing ascii and unicode plane 0 (0000-FFFF). + +local function get_next_char(text, pos) + + local msb = text:byte(pos) + -- 1 byte char, ascii equivalent codepoints + if msb < 0x80 then + return msb, pos + 1 + end + + -- 4 bytes char not managed (Only 16 bits codepoints are managed) + if msb >= 0xF0 then + return 0, pos + 4 + end + + -- 3 bytes char + if msb >= 0xE0 then + return (msb - 0xE0) * 0x1000 + + text:byte(pos + 1) % 0x40 * 0x40 + + text:byte(pos + 2) % 0x40, + pos + 3 + end + + -- 2 bytes char (little endian) + if msb >= 0xC2 then + return (msb - 0xC2) * 0x40 + text:byte(pos + 1), + pos + 2 + end + + -- Not an UTF char + return 0, pos + 1 +end + +-- API functions +---------------- + +-- Computes text size for a given font and text (ignores new lines) +-- @param font_name Font to be used +-- @param text Text to be rendered +-- @return Rendered text (width, height) + +function font_api.get_text_size(font_name, text) + local char + local width = 0 + local pos = 1 + local font = get_font(font_name) + + if font == nil then + return 0, 0 + else + while pos <= #text do + char, pos = get_next_char(text, pos) + -- Replace chars with no texture by the NULL(0) char + if font.widths[char] ~= nil then + width = width + font.widths[char] + else + width = width + font.widths[0] + end + end + end + + return width, font.height +end + +--- Builds texture part for a text line +-- @param font_name Font to be used +-- @param text Text to be rendered +-- @param width Width of the texture (extra text is not rendered) +-- @param x Starting x position in texture +-- @param y Vertical position of the line in texture +-- @return Texture string + +function font_api.make_line_texture(font_name, text, width, x, y) + local texture = "" + local char + local pos = 1 + local font = get_font(font_name) + + if font ~= nil then + while pos <= #text do + char, pos = get_next_char(text, pos) + + -- Replace chars with no texture by the NULL(0) char + if font.widths[char] == nil then + print(string.format("["..font_api.name + .."] Missing char %d (%04x)",char,char)) + char = 0 + end + + -- Add image only if it is visible (at least partly) + if x + font.widths[char] >= 0 and x <= width then + texture = texture.. + string.format(":%d,%d=font_%s_%04x.png", + x, y, font.name, char) + end + x = x + font.widths[char] + end + end + + return texture +end + +--- Builds texture for a multiline colored text +-- @param font_name Font to be used +-- @param text Text to be rendered +-- @param texturew Width of the texture (extra text will be truncated) +-- @param textureh Height of the texture +-- @param maxlines Maximum number of lines +-- @param halign Horizontal text align ("left"/"center"/"right") (optional) +-- @param valign Vertical text align ("top"/"center"/"bottom") (optional) +-- @param color Color of the text (optional) +-- @return Texture string + +function font_api.make_multiline_texture(font_name, text, width, height, + maxlines, halign, valign, color) + local texture = "" + local lines = {} + local textheight = 0 + local y, w, h + + for num, line in pairs(split_lines(text, maxlines)) do + w, h = font_api.get_text_size(font_name, line) + lines[num] = { text = line, width = w, height = h, } + textheight = textheight + h + end + + if #lines then + if valign == "top" then + y = 0 + elseif valign == "bottom" then + y = height - textheight + else + y = (height - textheight) / 2 + end + end + + for _, line in pairs(lines) do + if halign == "left" then + texture = texture.. + font_api.make_line_texture(font_name, line.text, width, + 0, y) + elseif halign == "right" then + texture = texture.. + font_api.make_line_texture(font_name, line.text, width, + width - line.width, y) + else + texture = texture.. + font_api.make_line_texture(font_name, line.text, width, + (width - line.width) / 2, y) + end + y = y + line.height + end + + texture = string.format("[combine:%dx%d", width, height)..texture + if color then texture = texture.."^[colorize:"..color end + return texture +end + +--- Register a new font +-- Textures corresponding to the font should be named after following patern : +-- font__.png +-- : name of the font +-- : 4 digit hexadecimal unicode of the char +-- @param font_name Name of the font to register +-- If registering different sizes of the same font, add size in the font name +-- (e.g. times_10, times_12...). +-- @param height Font height in pixels +-- @param widths Array of character widths in pixels, indexed by UTF codepoints + +function font_api.register_font(font_name, height, widths) + + if font_api.registered_fonts[font_name] ~= nil then + minetest.log("error", "Font \""..font_name.."\" already registered.") + return + end + + if height == nil or height <= 0 then + minetest.log("error", "Font \""..font_name.. + "\" must have a positive height.") + return + end + + if type(widths) ~= "table" then + minetest.log("error", "Font \""..font_name.. + "\" must have a widths array.") + return + end + + if widths[0] == nil then + minetest.log("error", "Font \""..font_name.. + "\" must have a char with codepoint 0 (=unknown char).") + return + end + + font_api.registered_fonts[font_name] = + { name = font_name, height = height, widths = widths } + + -- Force to choose again default font + -- (allows use of fonts registered after start) + default_font = false +end + +--- Standard on_display_update entity callback. +-- Node should have a corresponding display_entity with size, resolution and +-- maxlines fields and optionally halign, valign and color fields +-- @param pos Node position +-- @param objref Object reference of entity + +function font_api.on_display_update(pos, objref) + local meta = minetest.get_meta(pos) + local text = meta:get_string("display_text") + local ndef = minetest.registered_nodes[minetest.get_node(pos).name] + local entity = objref:get_luaentity() + + if entity and ndef.display_entities[entity.name] then + local def = ndef.display_entities[entity.name] + local font = get_font(def.font_name) + + objref:set_properties({ + textures={font_api.make_multiline_texture( + def.font_name, text, + def.size.x * def.resolution.x * font.height, + def.size.y * def.resolution.y * font.height, + def.maxlines, def.halign, def.valign, def.color)}, + visual_size = def.size + }) + end +end + +-- Compatibility +font_lib = font_api + -- cgit v1.2.3 From c6cad702bcea7f7836153b9b7f6ad847e3bd605e Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Sun, 8 Jul 2018 20:36:34 +0200 Subject: Creation of Font class and code update accordingly --- font_api/init.lua | 295 ++---------------------------------------------------- 1 file changed, 6 insertions(+), 289 deletions(-) (limited to 'font_api/init.lua') diff --git a/font_api/init.lua b/font_api/init.lua index f407f8b..2dc38ec 100644 --- a/font_api/init.lua +++ b/font_api/init.lua @@ -23,293 +23,12 @@ font_api = {} font_api.name = minetest.get_current_modname() font_api.path = minetest.get_modpath(font_api.name) -font_api.registered_fonts = {} --- Local variables ------------------- +-- Inclusions +------------- -local default_font = false - --- Local functions ------------------- - --- Split multiline text into array of lines, with maximum lines. - -local function split_lines(text, maxlines) - local splits = text:split("\n") - if maxlines then - local lines = {} - for num = 1,maxlines do - lines[num] = splits[num] - end - return lines - else - return splits - end -end - --- Gets a default (settings or fist font) - -local function get_default_font() - -- First call - if default_font == false then - default_font = nil - - -- First, try with settings - local settings_font = minetest.settings:get("default_font") - - if settings_font ~= nil and settings_font ~= "" then - default_font = font_api.registered_fonts[settings_font] - - if default_font == nil then - minetest.log("warning", "Default font in settings (\"".. - settings_font.."\") is not registered.") - end - end - - -- If failed, choose first font - if default_font == nil then - for _, font in pairs(font_api.registered_fonts) do - default_font = font - break - end - end - - -- Error, no font registered - if default_font == nil then - minetest.log("error", - "No font registred, unable to choose a default font.") - end - end - - return default_font -end - --- Returns font properties to be used according to font_name - -local function get_font(font_name) - local font = font_api.registered_fonts[font_name] - - if font == nil then - local message - - if font_name == nil then - message = "No font given" - else - message = "Font \""..font_name.."\" unregistered" - end - - font = get_default_font() - - if font ~= nil then - minetest.log("info", message..", using font \""..font.name.."\".") - end - end - - return font -end - --- Returns next char, managing ascii and unicode plane 0 (0000-FFFF). - -local function get_next_char(text, pos) - - local msb = text:byte(pos) - -- 1 byte char, ascii equivalent codepoints - if msb < 0x80 then - return msb, pos + 1 - end - - -- 4 bytes char not managed (Only 16 bits codepoints are managed) - if msb >= 0xF0 then - return 0, pos + 4 - end - - -- 3 bytes char - if msb >= 0xE0 then - return (msb - 0xE0) * 0x1000 - + text:byte(pos + 1) % 0x40 * 0x40 - + text:byte(pos + 2) % 0x40, - pos + 3 - end - - -- 2 bytes char (little endian) - if msb >= 0xC2 then - return (msb - 0xC2) * 0x40 + text:byte(pos + 1), - pos + 2 - end - - -- Not an UTF char - return 0, pos + 1 -end - --- API functions ----------------- - --- Computes text size for a given font and text (ignores new lines) --- @param font_name Font to be used --- @param text Text to be rendered --- @return Rendered text (width, height) - -function font_api.get_text_size(font_name, text) - local char - local width = 0 - local pos = 1 - local font = get_font(font_name) - - if font == nil then - return 0, 0 - else - while pos <= #text do - char, pos = get_next_char(text, pos) - -- Replace chars with no texture by the NULL(0) char - if font.widths[char] ~= nil then - width = width + font.widths[char] - else - width = width + font.widths[0] - end - end - end - - return width, font.height -end - ---- Builds texture part for a text line --- @param font_name Font to be used --- @param text Text to be rendered --- @param width Width of the texture (extra text is not rendered) --- @param x Starting x position in texture --- @param y Vertical position of the line in texture --- @return Texture string - -function font_api.make_line_texture(font_name, text, width, x, y) - local texture = "" - local char - local pos = 1 - local font = get_font(font_name) - - if font ~= nil then - while pos <= #text do - char, pos = get_next_char(text, pos) - - -- Replace chars with no texture by the NULL(0) char - if font.widths[char] == nil then - print(string.format("["..font_api.name - .."] Missing char %d (%04x)",char,char)) - char = 0 - end - - -- Add image only if it is visible (at least partly) - if x + font.widths[char] >= 0 and x <= width then - texture = texture.. - string.format(":%d,%d=font_%s_%04x.png", - x, y, font.name, char) - end - x = x + font.widths[char] - end - end - - return texture -end - ---- Builds texture for a multiline colored text --- @param font_name Font to be used --- @param text Text to be rendered --- @param texturew Width of the texture (extra text will be truncated) --- @param textureh Height of the texture --- @param maxlines Maximum number of lines --- @param halign Horizontal text align ("left"/"center"/"right") (optional) --- @param valign Vertical text align ("top"/"center"/"bottom") (optional) --- @param color Color of the text (optional) --- @return Texture string - -function font_api.make_multiline_texture(font_name, text, width, height, - maxlines, halign, valign, color) - local texture = "" - local lines = {} - local textheight = 0 - local y, w, h - - for num, line in pairs(split_lines(text, maxlines)) do - w, h = font_api.get_text_size(font_name, line) - lines[num] = { text = line, width = w, height = h, } - textheight = textheight + h - end - - if #lines then - if valign == "top" then - y = 0 - elseif valign == "bottom" then - y = height - textheight - else - y = (height - textheight) / 2 - end - end - - for _, line in pairs(lines) do - if halign == "left" then - texture = texture.. - font_api.make_line_texture(font_name, line.text, width, - 0, y) - elseif halign == "right" then - texture = texture.. - font_api.make_line_texture(font_name, line.text, width, - width - line.width, y) - else - texture = texture.. - font_api.make_line_texture(font_name, line.text, width, - (width - line.width) / 2, y) - end - y = y + line.height - end - - texture = string.format("[combine:%dx%d", width, height)..texture - if color then texture = texture.."^[colorize:"..color end - return texture -end - ---- Register a new font --- Textures corresponding to the font should be named after following patern : --- font__.png --- : name of the font --- : 4 digit hexadecimal unicode of the char --- @param font_name Name of the font to register --- If registering different sizes of the same font, add size in the font name --- (e.g. times_10, times_12...). --- @param height Font height in pixels --- @param widths Array of character widths in pixels, indexed by UTF codepoints - -function font_api.register_font(font_name, height, widths) - - if font_api.registered_fonts[font_name] ~= nil then - minetest.log("error", "Font \""..font_name.."\" already registered.") - return - end - - if height == nil or height <= 0 then - minetest.log("error", "Font \""..font_name.. - "\" must have a positive height.") - return - end - - if type(widths) ~= "table" then - minetest.log("error", "Font \""..font_name.. - "\" must have a widths array.") - return - end - - if widths[0] == nil then - minetest.log("error", "Font \""..font_name.. - "\" must have a char with codepoint 0 (=unknown char).") - return - end - - font_api.registered_fonts[font_name] = - { name = font_name, height = height, widths = widths } - - -- Force to choose again default font - -- (allows use of fonts registered after start) - default_font = false -end +dofile(font_api.path.."/font.lua") +dofile(font_api.path.."/registry.lua") --- Standard on_display_update entity callback. -- Node should have a corresponding display_entity with size, resolution and @@ -325,11 +44,9 @@ function font_api.on_display_update(pos, objref) if entity and ndef.display_entities[entity.name] then local def = ndef.display_entities[entity.name] - local font = get_font(def.font_name) - + local font = font_api.get_font(meta:get_string("font") or def.font_name) objref:set_properties({ - textures={font_api.make_multiline_texture( - def.font_name, text, + textures={font:make_text_texture(text, def.size.x * def.resolution.x * font.height, def.size.y * def.resolution.y * font.height, def.maxlines, def.halign, def.valign, def.color)}, -- cgit v1.2.3 From 8c7557e45d4744fe35ad058950062cf771640126 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Fri, 13 Jul 2018 20:41:53 +0200 Subject: Rework all nodes displaying text according to new font_api --- font_api/init.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'font_api/init.lua') diff --git a/font_api/init.lua b/font_api/init.lua index 2dc38ec..06619be 100644 --- a/font_api/init.lua +++ b/font_api/init.lua @@ -45,10 +45,14 @@ function font_api.on_display_update(pos, objref) if entity and ndef.display_entities[entity.name] then local def = ndef.display_entities[entity.name] local font = font_api.get_font(meta:get_string("font") or def.font_name) - objref:set_properties({ - textures={font:make_text_texture(text, - def.size.x * def.resolution.x * font.height, - def.size.y * def.resolution.y * font.height, + + local maxlines = def.maxlines or 1 -- TODO:How to do w/o maxlines ? + + objref:set_properties({ + textures={font:make_text_texture(text, + font:get_height(maxlines) * def.size.x / def.size.y + / (def.aspect_ratio or 1), + font:get_height(maxlines), def.maxlines, def.halign, def.valign, def.color)}, visual_size = def.size }) -- cgit v1.2.3 From 4b02cfdfca95ec62c30f7f0fdd225df235aae1de Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Mon, 16 Jul 2018 10:18:08 +0200 Subject: Fix default font chosing when multiple fonts --- font_api/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'font_api/init.lua') diff --git a/font_api/init.lua b/font_api/init.lua index 06619be..dc3a3e0 100644 --- a/font_api/init.lua +++ b/font_api/init.lua @@ -44,7 +44,8 @@ function font_api.on_display_update(pos, objref) if entity and ndef.display_entities[entity.name] then local def = ndef.display_entities[entity.name] - local font = font_api.get_font(meta:get_string("font") or def.font_name) + local font = font_api.get_font(meta:get_string("font") ~= "" + and meta:get_string("font") or def.font_name) local maxlines = def.maxlines or 1 -- TODO:How to do w/o maxlines ? -- cgit v1.2.3 From b8357a505cb64582868aedc60620be5452e20570 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Sat, 10 Nov 2018 22:35:47 +0100 Subject: New font selection formspec --- font_api/init.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'font_api/init.lua') diff --git a/font_api/init.lua b/font_api/init.lua index dc3a3e0..27cba99 100644 --- a/font_api/init.lua +++ b/font_api/init.lua @@ -29,9 +29,10 @@ font_api.path = minetest.get_modpath(font_api.name) dofile(font_api.path.."/font.lua") dofile(font_api.path.."/registry.lua") +dofile(font_api.path.."/fontform.lua") --- Standard on_display_update entity callback. --- Node should have a corresponding display_entity with size, resolution and +-- Node should have a corresponding display_entity with size, resolution and -- maxlines fields and optionally halign, valign and color fields -- @param pos Node position -- @param objref Object reference of entity @@ -49,8 +50,8 @@ function font_api.on_display_update(pos, objref) local maxlines = def.maxlines or 1 -- TODO:How to do w/o maxlines ? - objref:set_properties({ - textures={font:make_text_texture(text, + objref:set_properties({ + textures={font:make_text_texture(text, font:get_height(maxlines) * def.size.x / def.size.y / (def.aspect_ratio or 1), font:get_height(maxlines), @@ -62,4 +63,3 @@ end -- Compatibility font_lib = font_api - -- cgit v1.2.3