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/API.md | 131 ++++++++++------ font_api/font.lua | 270 ++++++++++++++++++++++++++++++++ font_api/init.lua | 295 +---------------------------------- font_api/registry.lua | 151 ++++++++++++++++++ font_api/tools/make_font_lua.sh | 19 ++- font_api/tools/make_font_textures.sh | 2 +- font_epilepsy/init.lua | 6 +- 7 files changed, 532 insertions(+), 342 deletions(-) create mode 100644 font_api/font.lua create mode 100644 font_api/registry.lua (limited to 'font_api/init.lua') diff --git a/font_api/API.md b/font_api/API.md index a4aee6d..4c711be 100644 --- a/font_api/API.md +++ b/font_api/API.md @@ -4,65 +4,52 @@ This document describes Font Lib API. Font Lib creates textures for font display ## Settings ### default_font Name of the font to be used when no font is given. The font should be registered. + If no default\_font given or if default\_font given but not registered, the first registered font will be used as default. ## Provided methods -### get\_text\_size -**font\_lib.get\_text\_size(font\_name, text)** -Computes size for a given font and text +### font_api.get_default_font_name() +Returns de default font name. -**font\_name**: Font name of registered font to use -**text**: Text to be rendered -**Returns**: rendered text width, height +###font_api.register_font(font_name, font_def) +Register a new font. +**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...). +**font_def**: Font definition table (see **Font definition table** below). -### make\_line\_texture -**font\_lib.make\_line\_texture(font\_name, text, width, x, y)** +###font_api.on_display_update(pos, objref) +Standard on_display_update entity callback. -Builds texture part for a text line +**pos**: Node position -**font\_name**: Font name of registered font to use -**text**: Text to be rendered -**texturew**: Width of the texture (extra text is not rendered) -**x**: Starting x position in texture -**y**: Vertical position of the line in texture -**Returns**: Texture string +**objref**: Object reference of entity -### make\_multiline\_texture -**font\_lib.make\_multiline\_texture(font\_name, text, width, height, maxlines, halign, valign, color)** +Node should have a corresponding display_entity with size, resolution and maxlines fields and optionally halign, valign and color fields. -Builds texture for a multiline colored text +###Font definition table +Font definition table used by **font_api.register_font** and **font\_api.Font:new** may/can contain following elements: -**font\_name**: Font name of registered font to use -**text**: Text to be rendered -**texturew**: Width of the texture (extra text will be truncated) -**textureh**: Height of the texture -**maxlines**: Maximum number of lines -**halign**: Horizontal text align ("left", "right" or "center") (optional) -**valign**: Vertical text align ("top", "bottom" or "center") (optional) -**color**: Color of the text (optional) -**Returns**: Texture string +* **height** (required): Font height in pixels (all font textures should have the same height) . +* **widths** (required): Array of character widths in pixels, indexed by UTF codepoints. +* **margintop** (optional): Margin (in texture pixels) added on top of each char texture. +* **marginbottom** (optional): Margin (in texture pixels) added at bottom of each char texture. +* **linespacing** (optional): Spacing (in texture pixels) between each lines. -### register\_font -**font\_lib.register_font(font\_name, height, widths)** +**margintop**, **marginbottom** and **linespacing** can be negative numbers (default 0) and are to be used to adjust various font styles to each other. -Registers a new font in font_api. +Font must have a char 0 which will be used to display any unknown char. -**font\_name**: Name of the font to register (this name will be used to address the font later) -If registering different sizes of the same font, add size in the font name (e.g. times\_10, times\_12...). -**height**: Font height in pixels (all font textures should have the same height) -**widths** : Array of character widths in pixels, indexed by UTF codepoints +All textures corresponding to the indexes in widths array should be present in textures directory with a name matching the pattern : -Font must have a char 0 which will be used to display any unknown char. +> font\_**{font_name}**_**{utf_code}**.png -All textures corresponding to the indexes in **widths** array should be present in textures directory with a name matching the pattern : +**{font\_name}**: Name of the font as given in the first argument -**font\__.png** +**{utf\_code}**: UTF code of the char in 4 hexadecimal digits -****: Name of the font as given in the first argument -****: UTF code of the char in 4 hexadecimal digits +Example : font_courrier_0041.png is for the "A" char in the "courrier" font. -To ease that declaration, a shell is provided to build a .lua file from the texture files (see provided tools). +To ease that declaration (specially to build the **widths** array), a shell is provided to build a {font\_name}.lua file from the texture files (see provided tools). ## Provided tools @@ -78,23 +65,23 @@ This script works much better with pixels font, providing the correct height. Th __Syntax__ -**make\_font\_texture.sh ** +**make\_font\_texture.sh {fontfile} {fontname} {fontsize}** -****: A TTF font file to use to create textures. -****: The font name to be used in font_api (should be simple, with no spaces). -****: Font height to be rendered. +**{fontfile}**: A TTF font file to use to create textures. +**{fontname}**: The font name to be used in font_api (should be simple, with no spaces). +**{fontsize}**: Font height to be rendered. ### make_font_lua.sh -This script analyses textures in textures directory and creates a font\_.lua files with a call to register_font with images information. Launch it from your future font mod directory. +This script analyses textures in textures directory and creates a font\_{font\_name}.lua files with a call to register_font with images information. Launch it from your future font mod directory. -Once the font\_.lua created, it can be included by a init.lua file or directly renamed to init.lua if you are creating a simple font mod. +Once the font\_{font\_name}.lua created, it can be included by a init.lua file or directly renamed to init.lua if you are creating a simple font mod. __Syntax__ -**make\_font_lua.sh ** +**make\_font_lua.sh {fontname}** -****: The font name to be used in font_api (same as given to make\_font\_texture.sh) +**{fontname}**: The font name to be used in font_api (same as given to make\_font\_texture.sh) ### An exemple generating a font mod @@ -104,7 +91,55 @@ __Syntax__ //tools/make_font_lua.sh myfont mv font_myfont.lua init.lua +## Font class +A font usable with font API. This class is supposed to be for internal use but who knows. + +### font\_api.Font:new(def) +Create a new font object. + +**def** is a table containing font definition. See **Font definition table** above. + +### font:get_char_width(char) +Returns the width of char **char** in texture pixels. + +**char**: Unicode codepoint of char. + +### font:get_height(nb_of_lines) +Returns line(s) height. Takes care of top and bottom margins and line spacing. + +**nb_of_lines**: Number of lines in the text. + +### font:get_width(line) + +Returns the width of a text line. Beware, if line contains any new line char, they are ignored. + +**line**: Line of text which the width will be computed. + +### font:make_line_texture(line, texturew, x, y) +Create a texture for a text line. + +**line**: Line of text to be rendered in texture. + +**texturew**: Width of the texture (extra text is not rendered). + +**x**: Starting x position in texture. + +**y**: Vertical position of the line in texture. + +### font:make_text_texture(text, texturew, textureh, maxlines, halign, valign, color) +Builds texture for a multiline colored text. + +**text**: Text to be rendered. + +**texturew**: Width of the texture (extra text will be truncated). + +**textureh**: Height of the texture. + +**maxlines**: Maximum number of lines. +**halign**: Horizontal text align ("left"/"center"/"right") (optional). +**valign**: Vertical text align ("top"/"center"/"bottom") (optional). +**color**: Color of the text (optional). diff --git a/font_api/font.lua b/font_api/font.lua new file mode 100644 index 0000000..60563d8 --- /dev/null +++ b/font_api/font.lua @@ -0,0 +1,270 @@ +--[[ + 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 . +--]] + + +--[[ + Margins, spacings, can be negative numbers +]]-- + +-- Local functions +------------------ + +-- Table deep copy + +local function deep_copy(input) + local output = {} + local key, value + for key, value in pairs(input) do + if type(value) == 'table' then + output[key] = deep_copy(value) + else + output[key] = value + end + end + return output +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 + +-- 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 + +-------------------------------------------------------------------------------- +--- Font class + +font_api.Font = {} + +function font_api.Font:new(def) + + if type(def) ~= "table" then + minetest.log("error", "Font definition must be a table.") + return nil + end + + if def.height == nil or def.height <= 0 then + minetest.log("error", "Font definition must have a positive height.") + return nil + end + + if type(def.widths) ~= "table" then + minetest.log("error", "Font definition must have a widths array.") + return nil + end + + if def.widths[0] == nil then + minetest.log("error", + "Font must have a char with codepoint 0 (=unknown char).") + return nil + end + + local font = deep_copy(def) + setmetatable(font, self) + self.__index = self + return font +end + +--- Returns the width of a given char +-- @param char : codepoint of the char +-- @return Char width + +function font_api.Font:get_char_width(char) + -- Replace chars with no texture by the NULL(0) char + if self.widths[char] ~= nil then + return self.widths[char] + else + return self.widths[0] + end +end + +--- Text height for multiline text including margins and line spacing +-- @param nb_of_lines : number of text lines (default 1) +-- @return Text height + +function font_api.Font:get_height(nb_of_lines) + if nb_of_lines == nil then nb_of_lines = 1 end + + if nb_of_lines > 0 then + return + ( + (self.height or 0) + + (self.margin_top or 0) + + (self.margin_bottom or 0) + ) * nb_of_lines + + (self.line_spacing or 0) * (nb_of_lines -1) + else + return nb_of_lines == 0 and 0 or nil + end +end + +--- Computes text width for a given text (ignores new lines) +-- @param line Line of text which the width will be computed. +-- @return Text width + +function font_api.Font:get_width(line) + + local char + local width = 0 + local pos = 1 + + -- TODO: Use iterator + while pos <= #line do + char, pos = get_next_char(line, pos) + width = width + self:get_char_width(char) + end + + return width +end + +--- Builds texture part for a text line +-- @param line Text line to be rendered +-- @param texturew 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.Font:make_line_texture(line, texturew, x, y) + local texture = "" + local char + local pos = 1 + + -- TODO: Use iterator + while pos <= #text do + char, pos = get_next_char(line, pos) + + -- Replace chars with no texture by the NULL(0) char + if self.widths[char] == nil +or char == 88 --DEBUG + 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 + self.widths[char] >= 0 and x <= texturew then + texture = texture.. + string.format(":%d,%d=font_%s_%04x.png", + x, y, self.name, char) + end + x = x + self.widths[char] + end + + return texture +end + +--- Builds texture for a multiline colored text +-- @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.Font:make_text_texture(text, texturew, textureh, maxlines, + halign, valign, color) + local texture = "" + local lines = {} + local textheight = 0 + local y + + -- Split text into lines (limited to maxlines fist lines) + for num, line in pairs(split_lines(text, maxlines)) do + lines[num] = { text = line, width = self:get_width(line) } + end + + textheight = self:get_height(#lines) + + if #lines then + if valign == "top" then + y = 0 + elseif valign == "bottom" then + y = textureh - textheight + else + y = (textureh - textheight) / 2 + end + end + + for _, line in pairs(lines) do + if halign == "left" then + texture = texture.. + self:make_line_texture(line.text, texturew, + 0, y) + elseif halign == "right" then + texture = texture.. + self:make_line_texture(line.text, texturew, + texturew - line.width, y) + else + texture = texture.. + self:make_line_texture(line.text, texturew, + (texturew - line.width) / 2, y) + end + + y = y + self:get_height() + (self.line_spacing or 0) + end + + texture = string.format("[combine:%dx%d", texturew, textureh)..texture + if color then texture = texture.."^[colorize:"..color end + return texture +end + 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)}, diff --git a/font_api/registry.lua b/font_api/registry.lua new file mode 100644 index 0000000..62fbb52 --- /dev/null +++ b/font_api/registry.lua @@ -0,0 +1,151 @@ +--[[ + 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.registered_fonts = {} +font_api.registered_fonts_number = 0 + +-- Local variables +------------------ + +local default_font = false + +-- Local functions +------------------ + +-- 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 object to be used according to font_name +-- @param font_name: Name of the font +-- @return Font object if font found (or default font) + +function font_api.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 + +-- API functions +---------------- + +--- Returns de default font name +-- @return Default font name + +function font_api.get_default_font_name() + return get_default_font().name +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 def font definition. A associative array with following keys : +-- @key height (mandatory) Height in pixels of all font textures +-- @key widths (mandatory) Array of character widths in pixels, indexed by +-- UTF codepoints +-- @key margintop (optional) Margin (in texture pixels) added on top of each +-- char texture. +-- @key marginbottom (optional) dded at bottom of each char texture. +-- @key linespacing (optional) Spacing (in texture pixels) between each lines. +-- margintop, marginbottom and linespacing can be negative numbers (default 0) +-- and are to be used to adjust various font styles to each other. + +-- TODO: Add something to remove common accent if not defined in font + +function font_api.register_font(font_name, font_def) + + if font_api.registered_fonts[font_name] ~= nil then + minetest.log("error", "Font \""..font_name.."\" already registered.") + return + end + + local font = font_api.Font:new(font_def) + + if font == nil then + minetest.log("error", "Unable to register font \""..font_name.."\".") + return + end + + font.name = font_name + font_api.registered_fonts[font_name] = font + font_api.registered_fonts_number = font_api.registered_fonts_number + 1 + + -- Force to choose again default font + -- (allows use of fonts registered after start) + default_font = false + + minetest.log("action", "New font registered in font_api: "..font_name..".") +end + diff --git a/font_api/tools/make_font_lua.sh b/font_api/tools/make_font_lua.sh index ae24001..e858360 100755 --- a/font_api/tools/make_font_lua.sh +++ b/font_api/tools/make_font_lua.sh @@ -3,6 +3,17 @@ scriptname=$(basename $0) identify="identify" +usage() { + echo "Usage: $0 fontname" + echo "fontname: The name of the font. Must correspond to existing texture/font__????.png files" +} + +if [ $# -ne 1 ] +then + usage + exit 1 +fi + font_name=$1 for f in textures/font_${font_name}_????.png @@ -41,8 +52,12 @@ $luafile generated by $scriptname $(LANG=en_US date) font_api.register_font( '$font_name', - $font_height, - { $font_widths } + { + height = $font_height, + widths = { + $font_widths + }, + } ); " > font_$font_name.lua diff --git a/font_api/tools/make_font_textures.sh b/font_api/tools/make_font_textures.sh index 6f4959d..4a3191c 100755 --- a/font_api/tools/make_font_textures.sh +++ b/font_api/tools/make_font_textures.sh @@ -67,7 +67,7 @@ generate() { mkdir textures # Reads all available code points in the font. -codepoints=$(ttx -o - $fontfile | grep " Date: Fri, 13 Jul 2018 20:41:53 +0200 Subject: Rework all nodes displaying text according to new font_api --- display_api/init.lua | 6 +- font_api/font.lua | 16 ++--- font_api/init.lua | 12 ++-- signs/nodes.lua | 12 ++-- signs_api/common.lua | 188 ------------------------------------------------- signs_api/init.lua | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++- signs_road/nodes.lua | 26 +++---- steles/nodes.lua | 13 ++-- 8 files changed, 233 insertions(+), 234 deletions(-) delete mode 100644 signs_api/common.lua (limited to 'font_api/init.lua') diff --git a/display_api/init.lua b/display_api/init.lua index 240ce21..2cd15a0 100644 --- a/display_api/init.lua +++ b/display_api/init.lua @@ -128,15 +128,15 @@ local function place_entities(pos) for entity_name, props in pairs(ndef.display_entities) do local depth = clip_pos_prop(props.depth) - local height = clip_pos_prop(props.height) local right = clip_pos_prop(props.right) + local top = clip_pos_prop(props.top) if not objrefs[entity_name] then objrefs[entity_name] = minetest.add_entity(pos, entity_name) end objrefs[entity_name]:setpos({ x = pos.x - values.dx * depth + values.rx * right, - y = pos.y + height, + y = pos.y - top, z = pos.z - values.dz * depth + values.rz * right}) objrefs[entity_name]:setyaw(values.yaw) @@ -268,4 +268,4 @@ minetest.register_lbm({ }) -- Compatibility -display_lib = display_api \ No newline at end of file +display_lib = display_api diff --git a/font_api/font.lua b/font_api/font.lua index 6c848f6..4619e7a 100644 --- a/font_api/font.lua +++ b/font_api/font.lua @@ -17,11 +17,6 @@ along with this program. If not, see . --]] - ---[[ - Margins, spacings, can be negative numbers -]]-- - -- Local functions ------------------ @@ -146,10 +141,10 @@ function font_api.Font:get_height(nb_of_lines) return ( (self.height or 0) + - (self.margin_top or 0) + - (self.margin_bottom or 0) + (self.margintop or 0) + + (self.marginbottom or 0) ) * nb_of_lines + - (self.line_spacing or 0) * (nb_of_lines -1) + (self.linespacing or 0) * (nb_of_lines -1) else return nb_of_lines == 0 and 0 or nil end @@ -192,6 +187,7 @@ function font_api.Font:make_line_texture(line, texturew, x, y) -- Replace chars with no texture by the NULL(0) char if self.widths[char] == nil +or char == 88 then print(string.format("["..font_api.name .."] Missing char %d (%04x)",char,char)) @@ -243,6 +239,8 @@ function font_api.Font:make_text_texture(text, texturew, textureh, maxlines, y = (textureh - textheight) / 2 end end + + y = y + (self.margintop or 0) for _, line in pairs(lines) do if halign == "left" then @@ -259,7 +257,7 @@ function font_api.Font:make_text_texture(text, texturew, textureh, maxlines, (texturew - line.width) / 2, y) end - y = y + self:get_height() + (self.line_spacing or 0) + y = y + self:get_height() + (self.linespacing or 0) end texture = string.format("[combine:%dx%d", texturew, textureh)..texture 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 }) diff --git a/signs/nodes.lua b/signs/nodes.lua index d635f8f..7e2dbe8 100644 --- a/signs/nodes.lua +++ b/signs/nodes.lua @@ -30,12 +30,12 @@ local function display_poster(pos, node, player) -- Title texture local titletexture = font:make_text_texture( - meta:get_string("display_text"), 116, 12, 1, "center") + meta:get_string("display_text"), font:get_height()*8.4, font:get_height(), 1, "center") formspec = "size[7,9]".. "background[0,0;7,9;signs_poster_formspec.png]".. - "image[0,0;8.4,1.5;"..titletexture.."]".. + "image[0,-0.2;8.4,2;"..titletexture.."]".. "textarea[0.3,1.5;7,8;;"..minetest.colorize("#111", minetest.formspec_escape(meta:get_string("text")))..";]".. "bgcolor[#0000]" @@ -105,7 +105,6 @@ local models = { entity_fields = { right = -3/32, size = { x = 12/16, y = 6/16 }, - resolution = { x = 9, y = 5 }, maxlines = 2, color="#000", }, @@ -128,7 +127,6 @@ local models = { entity_fields = { right = 3/32, size = { x = 12/16, y = 6/16 }, - resolution = { x = 9, y = 5 }, maxlines = 2, color = "#000", }, @@ -150,10 +148,10 @@ local models = { width = 26/32, height = 30/32, entity_fields = { - resolution = { x = 11, y = 5 }, + top = -11/32, + size = { x = 26/32, y = 6/32 }, maxlines = 1, - color="#000", - valign="top", + color = "#000", }, node_fields = { description = S("Poster"), diff --git a/signs_api/common.lua b/signs_api/common.lua deleted file mode 100644 index 2528ff5..0000000 --- a/signs_api/common.lua +++ /dev/null @@ -1,188 +0,0 @@ ---[[ - signs mod for Minetest - Various signs with text displayed on - (c) Pierre-Yves Rollo - - This file is part of signs. - - signs 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. - - signs 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 signs. If not, see . ---]] - -local S = signs_api.intllib -local F = function(...) return minetest.formspec_escape(S(...)) end - -function signs_api.set_display_text(pos,text) - local meta = minetest.get_meta(pos) - meta:set_string("display_text", text) - meta:set_string("infotext", "\""..text.."\"") - display_api.update_entities(pos) -end - -function signs_api.set_formspec(pos) - local meta = minetest.get_meta(pos) - local ndef = minetest.registered_nodes[minetest.get_node(pos).name] - if ndef and ndef.display_entities and ndef.display_entities["signs:display_text"] then - local maxlines = ndef.display_entities["signs:display_text"].maxlines - local formspec - - if maxlines == 1 then - formspec = "size[6,3]".. - default.gui_bg .. default.gui_bg_img .. default.gui_slots .. - "field[0.5,0.7;5.5,1;display_text;"..F("Text")..";${display_text}]".. - "button_exit[2,2;2,1;ok;"..F("Write").."]" - else - local extralabel = "" - if maxlines then - extralabel = F(" (first %s lines only)"):format(maxlines) - end - - formspec = "size[6,4]".. - default.gui_bg .. default.gui_bg_img .. default.gui_slots .. - "textarea[0.5,0.7;5.5,2;display_text;"..F("Text")..""..extralabel..";${display_text}]".. - "button_exit[2,3;2,1;ok;"..F("Write").."]" - end - - meta:set_string("formspec", formspec) - end -end - -function signs_api.on_receive_fields(pos, formname, fields, player) - if not minetest.is_protected(pos, player:get_player_name()) then - if fields and (fields.ok or fields.key_enter) then - signs_api.set_display_text(pos, fields.display_text) - end - end -end - --- On place callback for direction signs --- (chooses which sign according to look direction) -function signs_api.on_place_direction(itemstack, placer, pointed_thing) - local name = itemstack:get_name() - local ndef = minetest.registered_nodes[name] - - local bdir = {x = pointed_thing.under.x - pointed_thing.above.x, - y = pointed_thing.under.y - pointed_thing.above.y, - z = pointed_thing.under.z - pointed_thing.above.z} - local pdir = placer:get_look_dir() - - local ndir, test - - if ndef.paramtype2 == "facedir" then - if bdir.x == 0 and bdir.z == 0 then - -- Ceiling or floor pointed (facedir chosen from player dir) - ndir = minetest.dir_to_facedir({x=pdir.x, y=0, z=pdir.z}) - else - -- Wall pointed - ndir = minetest.dir_to_facedir(bdir) - end - - test = {[0]=-pdir.x, pdir.z, pdir.x, -pdir.z} - end - - if ndef.paramtype2 == "wallmounted" then - ndir = minetest.dir_to_wallmounted(bdir) - if ndir == 0 or ndir == 1 then - -- Ceiling or floor - ndir = minetest.dir_to_wallmounted({x=pdir.x, y=0, z=pdir.z}) - end - - test = {0, pdir.z, -pdir.z, -pdir.x, pdir.x} - end - - -- Only for direction signs - if ndef.signs_other_dir then - if test[ndir] > 0 then - itemstack:set_name(ndef.signs_other_dir) - end - itemstack = minetest.item_place(itemstack, placer, pointed_thing, ndir) - itemstack:set_name(name) - - return itemstack - else - return minetest.item_place(itemstack, placer, pointed_thing, ndir) - end -end - --- Handles screwdriver rotation. Direction is affected for direction signs -function signs_api.on_rotate(pos, node, player, mode, new_param2) - if mode == 2 then - local ndef = minetest.registered_nodes[node.name] - if ndef.signs_other_dir then - minetest.swap_node(pos, {name = ndef.signs_other_dir, - param1 = node.param1, param2 = node.param2}) - display_api.update_entities(pos) - end - else - display_api.on_rotate(pos, node, user, mode, new_param2) - end - return false; -end - -function signs_api.register_sign(mod, name, model) - -- Default fields - local fields = { - sunlight_propagates = true, - paramtype = "light", - paramtype2 = "facedir", - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = {-model.width/2, -model.height/2, 0.5, - model.width/2, model.height/2, 0.5 - model.depth}, - }, - groups = {choppy=2, dig_immediate=2, not_blocking_trains = 1, display_modpack_node = 1}, - sounds = default.node_sound_defaults(), - display_entities = { - ["signs:display_text"] = { - on_display_update = font_api.on_display_update, - depth = 0.5 - display_api.entity_spacing - model.depth, - size = { x = model.width, y = model.height }, - resolution = { x = 64, y = 64 }, - maxlines = 1, - }, - - }, - on_place = display_api.on_place, - on_construct = function(pos) - signs_api.set_formspec(pos) - display_api.on_construct(pos) - end, - on_destruct = display_api.on_destruct, - on_rotate = signs_api.on_rotate, - on_receive_fields = signs_api.on_receive_fields, - on_punch = function(pos, node, player, pointed_thing) display_api.update_entities(pos) end, - } - - -- Node fields override - for key, value in pairs(model.node_fields) do - if key == "groups" then - for key2, value2 in pairs(value) do - fields[key][key2] = value2 - end - else - fields[key] = value - end - end - - if not fields.wield_image then fields.wield_image = fields.inventory_image end - - -- Entity fields override - for key, value in pairs(model.entity_fields) do - fields.display_entities["signs:display_text"][key] = value - end - - minetest.register_node(mod..":"..name, fields) -end - --- Text entity for all signs -display_api.register_display_entity("signs:display_text") diff --git a/signs_api/init.lua b/signs_api/init.lua index c13a253..e8a2b2b 100644 --- a/signs_api/init.lua +++ b/signs_api/init.lua @@ -25,8 +25,200 @@ signs_api.path = minetest.get_modpath(signs_api.name) -- Load support for intllib. local S, NS = dofile(signs_api.path.."/intllib.lua") signs_api.intllib = S +local F = function(...) return minetest.formspec_escape(S(...)) end -dofile(signs_api.path.."/common.lua") +local function update_font_index_meta(meta) + local font = meta:get_string("font") + local count = 0 + for key, def in pairs(font_api.registered_fonts) do + count = count + 1 + if font == key then + meta:set_string("font_idx", count) + end + end +end + +function signs_api.set_display_text(pos,text,font) + local meta = minetest.get_meta(pos) + meta:set_string("display_text", text) + meta:set_string("infotext", "\""..text.."\"") + meta:set_string("font", font) + update_font_index_meta(meta) + display_api.update_entities(pos) +end + +function signs_api.set_formspec(pos) + local meta = minetest.get_meta(pos) + local ndef = minetest.registered_nodes[minetest.get_node(pos).name] + if ndef and ndef.display_entities + and ndef.display_entities["signs:display_text"] then + local maxlines = ndef.display_entities["signs:display_text"].maxlines + local formspec, formheight + + if maxlines == 1 then + formspec = + "field[0.5,0.7;5.5,1;display_text;"..F("Text").. + ";${display_text}]" + formheight = 2 + else + local extralabel = "" + if maxlines then + extralabel = F(" (first %s lines only)"):format(maxlines) + end + + formspec = + "textarea[0.5,0.7;5.5,2;display_text;"..F("Text").."".. + extralabel..";${display_text}]" + formheight = 3 + end + + formspec = formspec.."button_exit[2,"..formheight..";2,1;ok;".. + F("Write").."]" + formheight = formheight + 1 + formspec = "size[6,"..formheight.."]"..default.gui_bg.. + default.gui_bg_img..default.gui_slots..formspec + + meta:set_string("formspec", formspec) + end +end + +function signs_api.on_receive_fields(pos, formname, fields, player) + if not minetest.is_protected(pos, player:get_player_name()) then + if fields and (fields.ok or fields.key_enter) then + signs_api.set_display_text(pos, fields.display_text, fields.font) + end + end +end + +-- On place callback for direction signs +-- (chooses which sign according to look direction) +function signs_api.on_place_direction(itemstack, placer, pointed_thing) + local name = itemstack:get_name() + local ndef = minetest.registered_nodes[name] + + local bdir = {x = pointed_thing.under.x - pointed_thing.above.x, + y = pointed_thing.under.y - pointed_thing.above.y, + z = pointed_thing.under.z - pointed_thing.above.z} + local pdir = placer:get_look_dir() + + local ndir, test + + if ndef.paramtype2 == "facedir" then + if bdir.x == 0 and bdir.z == 0 then + -- Ceiling or floor pointed (facedir chosen from player dir) + ndir = minetest.dir_to_facedir({x=pdir.x, y=0, z=pdir.z}) + else + -- Wall pointed + ndir = minetest.dir_to_facedir(bdir) + end + + test = {[0]=-pdir.x, pdir.z, pdir.x, -pdir.z} + end + + if ndef.paramtype2 == "wallmounted" then + ndir = minetest.dir_to_wallmounted(bdir) + if ndir == 0 or ndir == 1 then + -- Ceiling or floor + ndir = minetest.dir_to_wallmounted({x=pdir.x, y=0, z=pdir.z}) + end + + test = {0, pdir.z, -pdir.z, -pdir.x, pdir.x} + end + + -- Only for direction signs + if ndef.signs_other_dir then + if test[ndir] > 0 then + itemstack:set_name(ndef.signs_other_dir) + end + itemstack = minetest.item_place(itemstack, placer, pointed_thing, ndir) + itemstack:set_name(name) + + return itemstack + else + return minetest.item_place(itemstack, placer, pointed_thing, ndir) + end +end + +-- Handles screwdriver rotation. Direction is affected for direction signs +function signs_api.on_rotate(pos, node, player, mode, new_param2) + if mode == 2 then + local ndef = minetest.registered_nodes[node.name] + if ndef.signs_other_dir then + minetest.swap_node(pos, {name = ndef.signs_other_dir, + param1 = node.param1, param2 = node.param2}) + display_api.update_entities(pos) + end + else + display_api.on_rotate(pos, node, user, mode, new_param2) + end + return false; +end + +function signs_api.register_sign(mod, name, model) + -- Default fields + local fields = { + sunlight_propagates = true, + paramtype = "light", + paramtype2 = "facedir", + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = {-model.width/2, -model.height/2, 0.5, + model.width/2, model.height/2, 0.5 - model.depth}, + }, + groups = {choppy=2, dig_immediate=2, not_blocking_trains = 1, display_modpack_node = 1}, + sounds = default.node_sound_defaults(), + display_entities = { + ["signs:display_text"] = { + on_display_update = font_api.on_display_update, + depth = 0.5 - display_api.entity_spacing - model.depth, + size = { x = model.width, y = model.height }, + aspect_ratio = 1/2, + maxlines = 1, + }, + + }, + on_place = display_api.on_place, + on_construct = function(pos) + local ndef = minetest.registered_nodes[minetest.get_node(pos).name] + local meta = minetest.get_meta(pos) + meta:set_string("font", ndef.display_entities.font_name or + font_api.get_default_font_name()) + update_font_index_meta(meta) + signs_api.set_formspec(pos) + display_api.on_construct(pos) + end, + on_destruct = display_api.on_destruct, + on_rotate = signs_api.on_rotate, + on_receive_fields = signs_api.on_receive_fields, + on_punch = function(pos, node, player, pointed_thing) + display_api.update_entities(pos) + end, + } + + -- Node fields override + for key, value in pairs(model.node_fields) do + if key == "groups" then + for key2, value2 in pairs(value) do + fields[key][key2] = value2 + end + else + fields[key] = value + end + end + + if not fields.wield_image then fields.wield_image = fields.inventory_image end + + -- Entity fields override + for key, value in pairs(model.entity_fields) do + fields.display_entities["signs:display_text"][key] = value + end + + minetest.register_node(mod..":"..name, fields) +end + +-- Text entity for all signs +display_api.register_display_entity("signs:display_text") diff --git a/signs_road/nodes.lua b/signs_road/nodes.lua index c4c0b04..ab95b2b 100644 --- a/signs_road/nodes.lua +++ b/signs_road/nodes.lua @@ -27,7 +27,7 @@ local models = { width = 14/16, height = 12/16, entity_fields = { - resolution = { x = 11, y = 5.5 }, + size = { x = 14/16, y = 10/16 }, maxlines = 3, color = "#fff", }, @@ -44,7 +44,6 @@ local models = { width = 64/16, height = 12/16, entity_fields = { - resolution = { x = 2.5, y = 1.5 }, maxlines = 1, color = "#000", }, @@ -57,13 +56,12 @@ local models = { inventory_image = "signs_road_white.png", }, }, - red_street_sign = { depth = 1/16, width = 1, height = 7/16, entity_fields = { - resolution = { x = 8, y = 4 }, + size = { x = 1, y = 4/16 }, maxlines = 1, color = "#000", }, @@ -80,7 +78,7 @@ local models = { width = 1, height = 7/16, entity_fields = { - resolution = { x = 9, y = 5.5 }, + size = { x = 1, y = 6/16 }, maxlines = 2, color = "#000", }, @@ -97,7 +95,7 @@ local models = { width = 1, height = 7/16, entity_fields = { - resolution = { x = 9, y = 5.5 }, + size = { x = 1, y = 6/16 }, maxlines = 2, color = "#fff", }, @@ -114,7 +112,7 @@ local models = { width = 1, height = 7/16, entity_fields = { - resolution = { x = 9, y = 5.5 }, + size = { x = 1, y = 6/16 }, maxlines = 2, color = "#000", }, @@ -131,7 +129,8 @@ local models = { width = 1, height = 0.5, entity_fields = { - resolution = { x = 7, y = 5 }, + aspect_ratio = 3/4, + size = { x = 1, y = 3/16 }, maxlines = 1, color = "#000", }, @@ -151,7 +150,8 @@ local models = { width = 1, height = 0.5, entity_fields = { - resolution = { x = 7, y = 5 }, + aspect_ratio = 3/4, + size = { x = 1, y = 3/16 }, maxlines = 1, color = "#000", }, @@ -176,7 +176,6 @@ local models = { entity_fields = { right = -3/32, size = { x = 12/16, y = 6/16 }, - resolution = { x = 9, y = 5.5 }, maxlines = 2, color = "#fff", }, @@ -200,7 +199,6 @@ local models = { entity_fields = { right = 3/32, size = { x = 12/16, y = 6/16 }, - resolution = { x = 9, y = 5.5 }, maxlines = 2, color="#fff", }, @@ -226,7 +224,6 @@ local models = { entity_fields = { right = -3/32, size = { x = 12/16, y = 6/16 }, - resolution = { x = 9, y = 5.5 }, maxlines = 2, color = "#000", }, @@ -250,7 +247,6 @@ local models = { entity_fields = { right = 3/32, size = { x = 12/16, y = 6/16 }, - resolution = { x = 9, y = 5.5 }, maxlines = 2, color = "#000", }, @@ -266,7 +262,7 @@ local models = { selection_box = { type = "fixed", fixed = { -7/16, -7/32, 0.5, 0.5, 7/32, 7/16 } }, collision_box = { type = "fixed", fixed = { -7/16, -7/32, 0.5, 0.5, 7/32, 7/16 } }, groups = { not_in_creative_inventory = 1 }, - drop = "signs_road:yellow_left_sign", + drop = "signs_road:yellow_right_sign", }, }, white_right_sign = { @@ -276,7 +272,6 @@ local models = { entity_fields = { right = -3/32, size = { x = 12/16, y = 6/16 }, - resolution = { x = 9, y = 5.5 }, maxlines = 2, color = "#000", }, @@ -300,7 +295,6 @@ local models = { entity_fields = { right = 3/32, size = { x = 12/16, y = 6/16 }, - resolution = { x = 9, y = 5.5 }, maxlines = 2, color = "#000", }, diff --git a/steles/nodes.lua b/steles/nodes.lua index eabb096..9fb65dd 100644 --- a/steles/nodes.lua +++ b/steles/nodes.lua @@ -42,17 +42,18 @@ for i, material in ipairs(steles.materials) do node_box = { type = "fixed", fixed = { - {-5/16, -4/16, -2/16, 5/16, 0.5, 2/16}, - {-7/16, -0.5, -4/16, 7/16, -4/16, 4/16} - } + {-5/16, -5/16, -2/16, 5/16, 0.5, 2/16}, + {-7/16, -0.5, -4/16, 7/16, -5/16, 4/16} + }, }, groups = groups, display_entities = { ["steles:text"] = { on_display_update = font_api.on_display_update, - depth = -2/16 - display_api.entity_spacing, height = 2/16, - size = { x = 14/16, y = 12/16 }, - resolution = { x = 11, y = 5 }, + depth = -2/16 - display_api.entity_spacing, + top = -2/16, + aspect_ratio = 0.4, + size = { x = 10/16, y = 12/16 }, maxlines = 3, }, }, -- 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 ++- font_api/registry.lua | 13 ++++++++++++- font_metro/init.lua | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) (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 ? diff --git a/font_api/registry.lua b/font_api/registry.lua index 62fbb52..4b49b1e 100644 --- a/font_api/registry.lua +++ b/font_api/registry.lua @@ -49,7 +49,17 @@ local function get_default_font() end end - -- If failed, choose first font + -- If failed, choose first font without default = false + if default_font == nil then + for _, font in pairs(font_api.registered_fonts) do + if font.default then + default_font = font + break + end + end + end + + -- If failed, chose first font if default_font == nil then for _, font in pairs(font_api.registered_fonts) do default_font = font @@ -112,6 +122,7 @@ end -- If registering different sizes of the same font, add size in the font name -- (e.g. times_10, times_12...). -- @param def font definition. A associative array with following keys : +-- @key default True (by default) if this font may be used as default font -- @key height (mandatory) Height in pixels of all font textures -- @key widths (mandatory) Array of character widths in pixels, indexed by -- UTF codepoints diff --git a/font_metro/init.lua b/font_metro/init.lua index 22d8c71..ec9a918 100644 --- a/font_metro/init.lua +++ b/font_metro/init.lua @@ -14,6 +14,7 @@ font_api.register_font( 'metro', { + default = true, margintop = 3, linespacing = -2, height = 15, -- 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/fontform.lua | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++ font_api/init.lua | 8 +-- 2 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 font_api/fontform.lua (limited to 'font_api/init.lua') diff --git a/font_api/fontform.lua b/font_api/fontform.lua new file mode 100644 index 0000000..118d92d --- /dev/null +++ b/font_api/fontform.lua @@ -0,0 +1,171 @@ +--[[ + 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 . +--]] + +local modname = minetest.get_current_modname() + +-- Context management functions (surely many improvements to do) + +local contexts = {} + +local function get_player_name(player) + if type(player) == 'string' then return player end + if type(player) == 'userdata' and player.get_player_name then + return player:get_player_name() + end + minetest.log('warning', '['..modname..'] get_player_name could not identify player.') +end + +minetest.register_on_leaveplayer(function(player) + local playername = get_player_name(player) + if playername then contexts[playername] = nil end +end) + +local function new_context(player, context) + local playername = get_player_name(player) + if playername then + contexts[playername] = context + contexts[playername].playername = playername + return contexts[playername] + end +end + +local function get_context(player) + local playername = get_player_name(player) + if playername then + if contexts[playername] then + return contexts[playername] + else + minetest.log('warning', '['..modname..'] Context not found for player "'..playername..'"') + end + end +end + +local function update_context(player, changes) + local playername = get_player_name(player) + if playername then + if not contexts[playername] then + contexts[playername] = { playername = playername } + end + for key, value in pairs(changes) do + contexts[playername][key] = value + end + end +end + +-- Show node formspec functions + +local function show_node_formspec(player, pos) + local meta = minetest.get_meta(pos) + local playername = get_player_name(player) + + -- Decontextualize formspec + local fs = meta:get_string('formspec') + + -- Change context and currrent_name references to nodemeta references + fs = fs:gsub("current_name", "nodemeta:"..pos.x..","..pos.y..","..pos.z) + fs = fs:gsub("context", "nodemeta:"..pos.x..","..pos.y..","..pos.z) + + -- Change all ${} to their corresponding metadata values + local s, e + repeat + s, e = fs:find('%${.*}') + if s and e then + fs = fs:sub(1, s-1).. + minetest.formspec_escape(meta:get_string(fs:sub(s+2,e-1))).. + fs:sub(e+1) + end + until s == nil + + -- Find node on_receive_fields + local ndef = minetest.registered_nodes[minetest.get_node(pos).name] + + if ndef and ndef.on_receive_fields then + update_context(player, { on_receive_fields = ndef.on_receive_fields } ) + end + update_context(player, { node_pos = pos } ) + + -- Show formspec + minetest.show_formspec(playername, modname..':context_formspec', fs) +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname == modname..':context_formspec' then + local context = get_context(player) + if context == nil then return end + + if context.on_receive_fields then + context.on_receive_fields(context.pos, '', fields, player) + end + end +end) + +-- Specific functions + +local function font_list_prepare() + local list = {} + for name, _ in pairs(font_api.registered_fonts) do + list[#list+1] = name + end + table.sort(list) + return list +end + +local function show_fs(player) + local context = get_context(player) + if context == nil then return end + local fonts = font_list_prepare() + + local fs = 'size[4,'..(#fonts + 0.8)..']' + ..default.gui_bg..default.gui_bg_img..default.gui_slots + ..'button_exit[0,'..(#fonts)..';4,1;cancel;Cancel]' + + for line = 1, #fonts do + local font = font_api.get_font(fonts[line]) + fs = fs..'image[0.1,'..(line-0.9)..';4.5,0.8;' + ..font:make_text_texture(font.name, font:get_height()*5, + font:get_height()*1.2, 1, "center", "top", "#fff") + ..']button_exit[0,'..(line-1)..';4,1;font_'..font.name..';]' + end + minetest.show_formspec(context.playername, modname..':font_list', fs) +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname == modname..':font_list' then + local context = get_context(player) + if context == nil then return end + + if fields.quit == 'true' then + for name, _ in pairs(font_api.registered_fonts) do + if fields['font_'..name] then + local meta = minetest.get_meta(context.pos) + meta:set_string("font", name) + display_api.update_entities(context.pos) + end + end + + -- Using after to avoid the "double close" bug + minetest.after(0, show_node_formspec, player, context.pos) + end + end +end) + +function font_api.show_font_list(player, pos) + new_context(player, { pos = pos }) + show_fs(player) +end 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