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/API.md | 110 +++++++++++ font_api/LICENSE.txt | 166 +++++++++++++++++ font_api/README.md | 14 ++ font_api/copyright.txt | 3 + font_api/depends.txt | 0 font_api/init.lua | 343 +++++++++++++++++++++++++++++++++++ font_api/settingtypes.txt | 1 + font_api/tools/make_font_lua.sh | 56 ++++++ font_api/tools/make_font_textures.sh | 111 ++++++++++++ 9 files changed, 804 insertions(+) create mode 100644 font_api/API.md create mode 100644 font_api/LICENSE.txt create mode 100644 font_api/README.md create mode 100644 font_api/copyright.txt create mode 100644 font_api/depends.txt create mode 100644 font_api/init.lua create mode 100644 font_api/settingtypes.txt create mode 100755 font_api/tools/make_font_lua.sh create mode 100755 font_api/tools/make_font_textures.sh (limited to 'font_api') diff --git a/font_api/API.md b/font_api/API.md new file mode 100644 index 0000000..a4aee6d --- /dev/null +++ b/font_api/API.md @@ -0,0 +1,110 @@ +# Font Lib API +This document describes Font Lib API. Font Lib creates textures for font display on entities. + +## 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\_name**: Font name of registered font to use +**text**: Text to be rendered +**Returns**: rendered text width, height + +### make\_line\_texture +**font\_lib.make\_line\_texture(font\_name, text, width, x, y)** + +Builds texture part for a text line + +**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 + +### make\_multiline\_texture +**font\_lib.make\_multiline\_texture(font\_name, text, width, height, maxlines, halign, valign, color)** + +Builds texture for a multiline colored text + +**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 + +### register\_font +**font\_lib.register_font(font\_name, height, widths)** + +Registers a new font in font_api. + +**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 + +Font must have a char 0 which will be used to display any unknown char. + +All textures corresponding to the indexes in **widths** array should be present in textures directory with a name matching the pattern : + +**font\__.png** + +****: Name of the font as given in the first argument +****: UTF code of the char in 4 hexadecimal digits + +To ease that declaration, a shell is provided to build a .lua file from the texture files (see provided tools). + +## Provided tools + +Still in early stage of development, these tools are helpers to create font mods. + +### make_font_texture.sh + +This scripts takes a .ttf file as input and create one .png file per char, that can be used as font texture. Launch it from your future font mod directory. + +__Advice__ + +This script works much better with pixels font, providing the correct height. There is no antialiasing at all, vector fonts and bad heights gives very ugly results. + +__Syntax__ + +**make\_font\_texture.sh ** + +****: 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. + +### 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. + +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. + +__Syntax__ + +**make\_font_lua.sh ** + +****: The font name to be used in font_api (same as given to make\_font\_texture.sh) + +### An exemple generating a font mod + + mkdir font_myfont + cd font_myfont + //tools/make_font_texture.sh myfont.ttf myfont 12 + //tools/make_font_lua.sh myfont + mv font_myfont.lua init.lua + + + + + diff --git a/font_api/LICENSE.txt b/font_api/LICENSE.txt new file mode 100644 index 0000000..341c30b --- /dev/null +++ b/font_api/LICENSE.txt @@ -0,0 +1,166 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + diff --git a/font_api/README.md b/font_api/README.md new file mode 100644 index 0000000..c7ae64b --- /dev/null +++ b/font_api/README.md @@ -0,0 +1,14 @@ +# Font Lib + +This library for font display on entities (to be used with display_api for sign creation). + +**Dependancies**: default + +**License**: LGPL + +(Default font taken from VanessaE's homedecor/signs_lib, originally under WTFPL) + +**API**: See [API.md](https://github.com/pyrollo/display_modpack/blob/master/font_api/API.md) document please. + +For more information, see the [forum topic](https://forum.minetest.net/viewtopic.php?t=13563) at the Minetest forums. + diff --git a/font_api/copyright.txt b/font_api/copyright.txt new file mode 100644 index 0000000..ceb5446 --- /dev/null +++ b/font_api/copyright.txt @@ -0,0 +1,3 @@ +Code by Pierre-Yves Rollo (pyrollo) +Contributors: +Andrzej Pieńkowski (apienk): Unicode support and tool for creating texturess diff --git a/font_api/depends.txt b/font_api/depends.txt new file mode 100644 index 0000000..e69de29 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 + diff --git a/font_api/settingtypes.txt b/font_api/settingtypes.txt new file mode 100644 index 0000000..d111159 --- /dev/null +++ b/font_api/settingtypes.txt @@ -0,0 +1 @@ +default_font(Default font) string diff --git a/font_api/tools/make_font_lua.sh b/font_api/tools/make_font_lua.sh new file mode 100755 index 0000000..ae24001 --- /dev/null +++ b/font_api/tools/make_font_lua.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +scriptname=$(basename $0) +identify="identify" + +font_name=$1 + +for f in textures/font_${font_name}_????.png +do + if [[ $f =~ textures/font_${font_name}_([0-9a-fA-F]{4}).png ]] + then + code=$((16#${BASH_REMATCH[1]})) + size=$(identify $f | cut -d " " -f 3) + w=$(echo $size | cut -d "x" -f 1) + h=$(echo $size | cut -d "x" -f 2) + + if [ -z "$font_height" ] + then + font_height=$h + else + if [ $font_height -ne $h ] + then + echo "Error : $f as height of $h pixels, previous textures have a height of $font_height pixels. All textures should have the same height." + fi + fi + + if [ -z "$font_widths" ] + then + font_widths="[$code]=$w" + else + font_widths="$font_widths, [$code]=$w" + fi + fi +done + +echo "--[[ + +$luafile generated by $scriptname $(LANG=en_US date) + +--]] + +font_api.register_font( + '$font_name', + $font_height, + { $font_widths } +); +" > font_$font_name.lua + +if grep -q font_api depends.txt &>/dev/null +then + echo "font_api already in depends.txt." +else + echo "adding font_api to depends.txt." + echo "font_api" >> depends.txt +fi + diff --git a/font_api/tools/make_font_textures.sh b/font_api/tools/make_font_textures.sh new file mode 100755 index 0000000..6f4959d --- /dev/null +++ b/font_api/tools/make_font_textures.sh @@ -0,0 +1,111 @@ +#!/bin/bash + +# This program generates a bitmap font for font_api mod for Minetest game. +# (c) Andrzej Pieńkowski +# (c) Pierre-Yves Rollo +# License: GPL + +usage() { + echo "Usage: $0 fontfile fontname fontsize" + echo "fontfile: A TTF font file to use to create textures." + echo "fontname: The font name to be used in font_api (should be simple, with no spaces)." + echo "fontsize: Font height to be rendered." +} + +if [ $# -ne 3 ] +then + usage + exit 1 +fi + +fontfile=$1 +fontname=$2 +fontsize=$3 + +if [ ! -r "$fontfile" ] +then + echo "$fontfile not readable." + exit 1 +fi + +# check imagemagick +hash convert &>/dev/null +if [ $? -eq 1 ]; then + echo -e "Error: This program requires convert from ImageMagick! Please install it by typing 'sudo apt-get install imagemagick' in terminal." + abort=1 +fi + +# check ttx +hash ttx &>/dev/null +if [ $? -eq 1 ]; then + echo -e "Error: This program requires ttx from FontTools! Please install it by typing 'sudo apt-get install fonttools' in terminal." + abort=1 +fi + +if [ $abort ] +then + exit 1 +fi + +generate() { + for i in $(seq $((0x$1)) $((0x$2))) + do + if echo "$codepoints" | grep -qi $(printf "0x%x" $i) + then + hex=$(printf "%04x" $i) + echo -e "Generating textures/font_${fontname}_$hex.png file for \"\\U$hex\" char." + if [[ "$hex" == "005c" ]] # Backslash char + then + convert -background none -fill black -font "$fontfile" -pointsize $fontsize label:"\\\\" -colorspace gray -channel alpha -threshold 50% textures/font_${fontname}_$hex.png + else + convert -background none -fill black -font "$fontfile" -pointsize $fontsize label:"$(echo -en "\\U$hex")" -colorspace gray -channel alpha -threshold 50% textures/font_${fontname}_$hex.png + fi + fi + done +} + +mkdir textures + +# Reads all available code points in the font. +codepoints=$(ttx -o - $fontfile | grep " 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 +- 6 files changed, 528 insertions(+), 340 deletions(-) create mode 100644 font_api/font.lua create mode 100644 font_api/registry.lua (limited to 'font_api') 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: Sun, 8 Jul 2018 20:41:02 +0200 Subject: Creation of Font class and code update accordingly (fix) --- font_api/font.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'font_api') diff --git a/font_api/font.lua b/font_api/font.lua index 60563d8..6c848f6 100644 --- a/font_api/font.lua +++ b/font_api/font.lua @@ -187,12 +187,11 @@ function font_api.Font:make_line_texture(line, texturew, x, y) local pos = 1 -- TODO: Use iterator - while pos <= #text do + while pos <= #line 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)) -- cgit v1.2.3 From d3aae9b2626a8d4afda34e78de037de3fb181b1c Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Sun, 8 Jul 2018 21:27:39 +0200 Subject: Added two documentation schema --- font_api/doc/font.svg | 463 +++++++++++++++++++++++++++++++++++++++++ font_api/doc/lines.svg | 544 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1007 insertions(+) create mode 100644 font_api/doc/font.svg create mode 100644 font_api/doc/lines.svg (limited to 'font_api') diff --git a/font_api/doc/font.svg b/font_api/doc/font.svg new file mode 100644 index 0000000..e4e8757 --- /dev/null +++ b/font_api/doc/font.svg @@ -0,0 +1,463 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + Texture Height + + Texture Width + + + + MarginTop + Margin Bottom + + Line Height + + + diff --git a/font_api/doc/lines.svg b/font_api/doc/lines.svg new file mode 100644 index 0000000..f75880f --- /dev/null +++ b/font_api/doc/lines.svg @@ -0,0 +1,544 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + Line Height + + + Text Height + Line Spacing + + + + + + + + + -- cgit v1.2.3 From a83d494d45bdb15cd65749bc8ce505a9e7dc1f8a Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Mon, 9 Jul 2018 07:19:27 +0000 Subject: Fix typos in API.md --- font_api/API.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'font_api') diff --git a/font_api/API.md b/font_api/API.md index 4c711be..9900a80 100644 --- a/font_api/API.md +++ b/font_api/API.md @@ -12,12 +12,12 @@ If no default\_font given or if default\_font given but not registered, the firs ### font_api.get_default_font_name() Returns de default font name. -###font_api.register_font(font_name, font_def) +### 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). -###font_api.on_display_update(pos, objref) +### font_api.on_display_update(pos, objref) Standard on_display_update entity callback. **pos**: Node position @@ -26,7 +26,7 @@ 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. -###Font definition table +### Font definition table Font definition table used by **font_api.register_font** and **font\_api.Font:new** may/can contain following elements: * **height** (required): Font height in pixels (all font textures should have the same height) . -- 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/font.lua | 16 +++++++--------- font_api/init.lua | 12 ++++++++---- 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'font_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 }) -- cgit v1.2.3 From fac6dfe1f896b9c8a59c6f0416759c3dc8d715ed Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Sun, 15 Jul 2018 09:40:18 +0200 Subject: Removed a debug trick and fixed indentation --- font_api/font.lua | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) (limited to 'font_api') diff --git a/font_api/font.lua b/font_api/font.lua index 4619e7a..580744d 100644 --- a/font_api/font.lua +++ b/font_api/font.lua @@ -43,7 +43,7 @@ local function get_next_char(text, pos) -- 1 byte char, ascii equivalent codepoints if msb < 0x80 then return msb, pos + 1 - end + end -- 4 bytes char not managed (Only 16 bits codepoints are managed) if msb >= 0xF0 then @@ -64,7 +64,7 @@ local function get_next_char(text, pos) pos + 2 end - -- Not an UTF char + -- Not an UTF char return 0, pos + 1 end @@ -158,7 +158,7 @@ function font_api.Font:get_width(line) local char local width = 0 - local pos = 1 + local pos = 1 -- TODO: Use iterator while pos <= #line do @@ -187,11 +187,10 @@ 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 + print(string.format("["..font_api.name .."] Missing char %d (%04x)",char,char)) - char = 0 + char = 0 end -- Add image only if it is visible (at least partly) @@ -220,27 +219,27 @@ function font_api.Font:make_text_texture(text, texturew, textureh, maxlines, halign, valign, color) local texture = "" local lines = {} - local textheight = 0 - local y + 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 + 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 + if #lines then + if valign == "top" then + y = 0 + elseif valign == "bottom" then + y = textureh - textheight + else + y = (textureh - textheight) / 2 + end + end - y = y + (self.margintop or 0) + y = y + (self.margintop or 0) for _, line in pairs(lines) do if halign == "left" then -- 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 ++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'font_api') 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 -- cgit v1.2.3 From 95c9da849d98ecee9b040761683e86de81303ccf Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Thu, 1 Nov 2018 10:47:39 +0100 Subject: Changed UTF8 routines and added char fallback mechanism --- font_api/fallbacks.lua | 176 +++++++++++++++++++++++++++++++++++++++++++++++ font_api/font.lua | 180 +++++++++++++++++++++++++------------------------ 2 files changed, 269 insertions(+), 87 deletions(-) create mode 100644 font_api/fallbacks.lua (limited to 'font_api') diff --git a/font_api/fallbacks.lua b/font_api/fallbacks.lua new file mode 100644 index 0000000..480d865 --- /dev/null +++ b/font_api/fallbacks.lua @@ -0,0 +1,176 @@ +--[[ + 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 . +--]] + +-- This is the unicode char fallback map. If a char is not present in +-- font, this maps indicates which char to try to use instead next. + +return { + -- Lowercase chars + ['a'] = 'A', ['b'] = 'B', ['c'] = 'C', ['d'] = 'D', + ['e'] = 'E', ['f'] = 'F', ['g'] = 'G', ['h'] = 'H', + ['i'] = 'I', ['j'] = 'J', ['k'] = 'K', ['l'] = 'L', + ['m'] = 'M', ['n'] = 'N', ['o'] = 'O', ['p'] = 'P', + ['q'] = 'Q', ['r'] = 'R', ['s'] = 'S', ['t'] = 'T', + ['u'] = 'U', ['v'] = 'V', ['w'] = 'W', ['x'] = 'X', + ['y'] = 'Y', ['z'] = 'Z', + + -- Special + ['¢'] = 'c', ['£'] = 'L', ['¥'] = 'Y', ['€'] = 'E', + ['©'] = '(C)', ['®'] = '(R)', ['™'] = 'TM', + ['ª'] = 'a', ['º'] = 'o', + ['«'] = '"', ['»'] = '"', ['´'] = '\'', + ['¹'] = '1', ['²'] = '2', ['³'] = '3', + ['µ'] = 'u', ['¤'] = 'o', + ['¼'] = '1/4', ['½'] = '1/2', ['¾'] = '3/4', + ['⅛'] = '1/8', ['⅜'] = '3/8', ['⅝'] = '5/8', ['⅞'] = '7/8', + ['¿'] = '?', + + -- Upper case accents + ['À'] = 'A', ['Á'] = 'A', ['Â'] = 'A', ['Ã'] = 'A', + ['Ä'] = 'A', ['Å'] = 'A', + ['Æ'] = 'AE', ['Ç'] = 'C', + ['È'] = 'E', ['É'] = 'E', ['Ê'] = 'E', ['Ë'] = 'E', + ['Ì'] = 'I', ['Í'] = 'I', ['Î'] = 'I', ['Ï'] = 'I', + ['Ð'] = 'D', ['Ñ'] = 'N', + ['Ò'] = 'O', ['Ó'] = 'O', ['Ô'] = 'O', ['Õ'] = 'O', + ['Ö'] = 'O', ['Ø'] = 'O', + ['Ú'] = 'U', ['Ù'] = 'U', ['Û'] = 'U', ['Ü'] = 'U', + ['×'] = 'x', ['Ý'] = 'Y', + + -- Lower case accents + ['à'] = 'a', ['à'] = 'a', ['á'] = 'a', ['â'] = 'a', + ['ã'] = 'a', ['ä'] = 'a', ['å'] = 'a', + ['æ'] = 'ae', ['ç'] = 'c', + ['è'] = 'e', ['é'] = 'e', ['ê'] = 'e', ['ë'] = 'e', + ['ì'] = 'i', ['í'] = 'i', ['î'] = 'i', ['ï'] = 'i', + ['ð'] = 'd', ['ñ'] = 'n', + ['ò'] = 'o', ['ó'] = 'o', ['ô'] = 'o', ['õ'] = 'o', + ['ö'] = 'o', ['ø'] = 'o', + ['ù'] = 'u', ['ú'] = 'u', ['û'] = 'u', ['ü'] = 'u', + ['ý'] = 'y', ['ÿ'] = 'y', + + -- Extended latin A + + ['Ā'] = 'A', ['ā'] = 'a', ['Ă'] = 'A', ['ă'] = 'a', + ['Ą'] = 'A', ['ą'] = 'a', ['Ć'] = 'C', ['ć'] = 'c', + ['Ĉ'] = 'C', ['ĉ'] = 'c', ['Ċ'] = 'C', ['ċ'] = 'c', + ['Č'] = 'C', ['č'] = 'c', ['Ď'] = 'D', ['ď'] = 'd', + ['Đ'] = 'D', ['đ'] = 'd', ['Ē'] = 'E', ['ē'] = 'e', + ['Ĕ'] = 'E', ['ĕ'] = 'e', ['Ė'] = 'E', ['ė'] = 'e', + ['Ę'] = 'E', ['ę'] = 'e', ['Ě'] = 'E', ['ě'] = 'e', + ['Ĝ'] = 'G', ['Ğ'] = 'G', ['ğ'] = 'g', ['ĝ'] = 'g', + ['Ġ'] = 'G', ['ġ'] = 'g', ['Ģ'] = 'G', ['ģ'] = 'g', + ['Ĥ'] = 'H', ['ĥ'] = 'h', ['Ħ'] = 'H', ['ħ'] = 'h', + ['Ĩ'] = 'I', ['ĩ'] = 'i', ['Ī'] = 'I', ['ī'] = 'i', + ['Ĭ'] = 'I', ['ĭ'] = 'i', ['Į'] = 'I', ['į'] = 'i', + ['ı'] = 'i', ['İ'] = 'I', ['IJ'] = 'IJ', ['ij'] = 'ij', + ['Ĵ'] = 'J', ['ĵ'] = 'j', ['ķ'] = 'k', ['Ķ'] = 'K', + ['ĸ'] = 'k', + ['Ĺ'] = 'L', ['ĺ'] = 'l', ['Ļ'] = 'L', ['ļ'] = 'l', + ['Ľ'] = 'L', ['ľ'] = 'l', ['Ŀ'] = 'L', ['ŀ'] = 'l', + ['Ł'] = 'L', ['ł'] = 'l', ['Ń'] = 'N', ['ń'] = 'n', + ['Ņ'] = 'N', ['ņ'] = 'n', ['Ň'] = 'N', ['ň'] = 'n', + ['ʼn'] = 'n', ['Ŋ'] = 'n', ['ŋ'] = 'n', + ['Ō'] = 'O', ['ō'] = 'o', ['Ŏ'] = 'O', ['ŏ'] = 'o', + ['ő'] = 'o', ['Ő'] = 'O', ['œ'] = 'oe', ['Œ'] = 'OE', + ['Ŕ'] = 'R', ['ŕ'] = 'r', ['Ŗ'] = 'R', ['ŗ'] = 'r', + ['Ř'] = 'R', ['ř'] = 'r', ['Ś'] = 'S', ['ś'] = 's', + ['Ŝ'] = 'S', ['ŝ'] = 's', ['Ş'] = 'S', ['ş'] = 's', + ['Š'] = 'S', ['š'] = 's', ['Ţ'] = 'T', ['ţ'] = 't', + ['ť'] = 't', ['Ŧ'] = 'T', ['Ť'] = 'T', ['ŧ'] = 't', + ['Ũ'] = 'U', ['ũ'] = 'u', ['Ū'] = 'U', ['ū'] = 'u', + ['Ŭ'] = 'U', ['ŭ'] = 'u', ['Ů'] = 'U', ['ů'] = 'u', + ['Ű'] = 'U', ['ű'] = 'u', ['Ų'] = 'U', ['ų'] = 'u', + ['Ŵ'] = 'W', ['ŵ'] = 'w', ['Ŷ'] = 'Y', ['ŷ'] = 'y', + ['Ÿ'] = 'Y', + ['Ź'] = 'Z', ['ź'] = 'z', ['Ż'] = 'Z', ['ż'] = 'z', + ['Ž'] = 'Z', ['ž'] = 'z', ['ſ'] = 's', + + -- Extended latin B + ['ƀ'] = 'b', ['Ɓ'] = 'B', ['Ƃ'] = 'B', ['ƃ'] = 'b', + ['Ɔ'] = 'O', + ['Ƈ'] = 'C', ['ƈ'] = 'c', ['Ɖ'] = 'D', ['Ɗ'] = 'D', + ['Ƌ'] = 'D', ['ƌ'] = 'd', ['Ǝ'] = 'E', ['Ə'] = 'e', + ['Ɛ'] = 'E', + ['Ƒ'] = 'F', ['ƒ'] = 'f', ['Ɠ'] = 'G', + ['ƕ'] = 'hv', ['Ɨ'] = 'I', ['Ƙ'] = 'K', ['ƙ'] = 'k', + ['ƚ'] = 'l', ['Ɯ'] = 'M', ['Ɲ'] = 'N', ['ƞ'] = 'n', + ['Ɵ'] = 'O', + ['Ơ'] = 'O', ['ơ'] = 'o', ['Ƣ'] = 'OI', ['ƣ'] = 'oi', + ['Ƥ'] = 'P', ['ƥ'] = 'p', ['Ʀ'] = 'YR', + ['Ƨ'] = 'S', ['ƨ'] = 's', ['ƫ'] = 't', + ['Ƭ'] = 'T', ['ƭ'] = 't', ['Ʈ'] = 'T', + ['Ư'] = 'U', ['ư'] = 'u', ['Ʋ'] = 'V', + ['Ƴ'] = 'Y', ['ƴ'] = 'y', ['Ƶ'] = 'Z', ['ƶ'] = 'z', + ['ƻ'] = '2', ['Ƽ'] = '5', ['ƽ'] = '5', + ['DŽ'] = 'DZ', ['Dž'] = 'Dz', ['dž'] = 'dz', + ['LJ'] = 'LJ', ['Lj'] = 'Lj', ['lj'] = 'lj', + ['NJ'] = 'NJ', ['Nj'] = 'Nj', ['nj'] = 'nj', + ['Ǎ'] = 'A', ['ǎ'] = 'a', ['Ǐ'] = 'I', ['ǐ'] = 'i', + ['Ǒ'] = 'O', ['ǒ'] = 'o', ['Ǔ'] = 'U', ['ǔ'] = 'u', + ['Ǖ'] = 'U', ['ǖ'] = 'u', ['Ǘ'] = 'U', ['ǘ'] = 'u', + ['Ǚ'] = 'U', ['ǚ'] = 'u', ['Ǜ'] = 'U', ['ǜ'] = 'u', + ['ǝ'] = 'e', + ['Ǟ'] = 'A', ['ǟ'] = 'a', ['Ǡ'] = 'A', ['ǡ'] = 'a', + ['Ǣ'] = 'Æ', ['ǣ'] = 'æ', ['Ǥ'] = 'G', ['ǥ'] = 'g', + ['Ǧ'] = 'G', ['ǧ'] = 'g', ['Ǩ'] = 'K', ['ǩ'] = 'k', + ['Ǫ'] = 'Q', ['ǫ'] = 'q', ['Ǭ'] = 'Q', ['ǭ'] = 'q', + ['ǰ'] = 'J', + ['DZ'] = 'DZ', ['Dz'] = 'Dz', ['dz'] = 'dz', + ['Ǵ'] = 'G', ['ǵ'] = 'g', ['Ƕ'] = 'H', + ['Ǹ'] = 'N', ['ǹ'] = 'n', ['Ǻ'] = 'A', ['ǻ'] = 'a', + ['Ǽ'] = 'Æ', ['ǽ'] = 'æ', ['Ǿ'] = 'Ø', ['ǿ'] = 'ø', + ['Ȁ'] = 'A', ['ȁ'] = 'a', ['Ȃ'] = 'A', ['ȃ'] = 'a', + ['Ȅ'] = 'E', ['ȅ'] = 'e', ['Ȇ'] = 'E', ['ȇ'] = 'e', + ['Ȉ'] = 'I', ['ȉ'] = 'i', ['Ȋ'] = 'I', ['ȋ'] = 'i', + ['Ȍ'] = 'O', ['ȍ'] = 'o', ['Ȏ'] = 'O', ['ȏ'] = 'o', + ['Ȑ'] = 'R', ['ȑ'] = 'r', ['Ȓ'] = 'R', ['ȓ'] = 'r', + ['Ȕ'] = 'U', ['ȕ'] = 'u', ['Ȗ'] = 'U', ['ȗ'] = 'u', + ['Ș'] = 'S', ['ș'] = 's', ['Ț'] = 'T', ['ț'] = 't', + ['Ȟ'] = 'H', ['ȟ'] = 'h', ['Ƞ'] = 'N', + ['ȡ'] = 'd', + ['Ȣ'] = 'OU', ['ȣ'] = 'ou', ['Ȥ'] = 'Z', ['ȥ'] = 'z', + ['Ȧ'] = 'A', ['ȧ'] = 'a', ['Ȩ'] = 'E', ['ȩ'] = 'e', + ['Ȫ'] = 'O', ['ȫ'] = 'o', ['Ȭ'] = 'O', ['ȭ'] = 'o', + ['Ȯ'] = 'O', ['ȯ'] = 'o', ['Ȱ'] = 'O', ['ȱ'] = 'o', + ['Ȳ'] = 'Y', ['ȳ'] = 'y', ['ȴ'] = 'l', + ['ȵ'] = 'n', ['ȶ'] = 't', ['ȷ'] = 'j', + ['ȸ'] = 'db', ['ȹ'] = 'qp', ['Ⱥ'] = 'A', + ['Ȼ'] = 'C', ['ȼ'] = 'c', ['Ƚ'] = 'L', + ['Ⱦ'] = 'T', ['ȿ'] = 's', ['ɀ'] = 'z', + ['Ƀ'] = 'B', ['Ʉ'] = 'U', ['Ʌ'] = 'V', + ['Ɇ'] = 'E', ['ɇ'] = 'e', ['Ɉ'] = 'J', ['ɉ'] = 'j', + ['Ɋ'] = 'Q', ['ɋ'] = 'q', ['Ɍ'] = 'R', ['ɍ'] = 'r', + ['Ɏ'] = 'Y', ['ɏ'] = 'y', ['ɐ'] = 'a', + ['ɓ'] = 'b', ['ɔ'] = 'o', + ['ɕ'] = 'c', ['ɖ'] = 'd', ['ɗ'] = 'd', + ['ɘ'] = 'e', ['ə'] = 'e', ['ɚ'] = 'e', + ['ɛ'] = 'e', ['ɜ'] = 'e', ['ɝ'] = 'e', ['ɞ'] = 'e', + ['ɟ'] = 'j', + ['ɠ'] = 'g', ['ɡ'] = 'g', ['ɢ'] = 'G', + ['ɥ'] = 'h', ['ɦ'] = 'h', ['ɧ'] = 'h', + ['ɨ'] = 'i', ['ɪ'] = 'I', + ['ɫ'] = 'l', ['ɬ'] = 'l', ['ɭ'] = 'l', + ['ɮ'] = 'lz', + ['ɯ'] = 'm', ['ɰ'] = 'm', ['ɱ'] = 'm', + ['ɲ'] = 'n', ['ɳ'] = 'n', ['ɴ'] = 'N', + ['ɵ'] = 'o', ['ɶ'] = 'Œ', + ['ɹ'] = 'r', ['ɺ'] = 'r', ['ɻ'] = 'r', + ['ɼ'] = 'r', ['ɽ'] = 'r', ['ɾ'] = 'r', ['ɿ'] = 'r', +} diff --git a/font_api/font.lua b/font_api/font.lua index 580744d..db12dba 100644 --- a/font_api/font.lua +++ b/font_api/font.lua @@ -17,59 +17,44 @@ along with this program. If not, see . --]] +-- Fallback table +local fallbacks = dofile(font_api.path.."/fallbacks.lua") + -- 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 +-- Returns number of UTF8 bytes of the first char of the string +local function get_char_bytes(str) + local msb = str:byte(1) + if msb ~= nil then + if msb < 0x80 then return 1 end + if msb >= 0xF0 then return 4 end + if msb >= 0xE0 then return 3 end + if msb >= 0xC2 then return 2 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 +-- Returns the unicode codepoint of the first char of the string +local function char_to_codepoint(str) + local bytes = get_char_bytes(str) + if bytes == 1 then + return str:byte(1) + elseif bytes == 2 then + return (str:byte(1) - 0xC2) * 0x40 + + str:byte(2) + elseif bytes == 3 then + return (str:byte(1) - 0xE0) * 0x1000 + + str:byte(2) % 0x40 * 0x40 + + str:byte(3) % 0x40 + elseif bytes == 4 then -- Not tested + return (str:byte(1) - 0xF0) * 0x40000 + + str:byte(2) % 0x40 * 0x1000 + + str:byte(3) % 0x40 * 0x40 + + str:byte(4) % 0x40 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 @@ -86,42 +71,75 @@ end -------------------------------------------------------------------------------- --- Font class -font_api.Font = {} +local Font = {} +font_api.Font = Font -function font_api.Font:new(def) +function Font:new(def) if type(def) ~= "table" then - minetest.log("error", "Font definition must be a table.") + minetest.log("error", + "[font_api] 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.") + minetest.log("error", + "[font_api] 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.") + minetest.log("error", + "[font_api] 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).") + minetest.log("error", + "[font_api] Font must have a char with codepoint 0 (=unknown char).") return nil end - local font = deep_copy(def) + local font = table.copy(def) setmetatable(font, self) self.__index = self return font end +--- Gets the next char of a text +-- @return Codepoint of first char, +-- @return Remaining string without this first char + +function Font:get_next_char(text) + local bytes = get_char_bytes(text) + + if bytes == nil then + minetest.log("warning", + "[font_api] Encountered a non UTF char, not displaying text.") + return nil, '' + end + + local codepoint = char_to_codepoint(text) + + -- Fallback mechanism + if self.widths[codepoint] == nil then + local char = text:sub(1, bytes) + + if fallbacks[char] then + return self:get_next_char(fallbacks[char]..text:sub(bytes+1)) + else + return 0, text:sub(bytes+1) -- Ultimate fallback + end + else + return codepoint, text:sub(bytes+1) + end +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) +function 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] @@ -134,13 +152,13 @@ end -- @param nb_of_lines : number of text lines (default 1) -- @return Text height -function font_api.Font:get_height(nb_of_lines) +function Font:get_height(nb_of_lines) if nb_of_lines == nil then nb_of_lines = 1 end - + if nb_of_lines > 0 then - return + return ( - (self.height or 0) + + (self.height or 0) + (self.margintop or 0) + (self.marginbottom or 0) ) * nb_of_lines + @@ -154,16 +172,14 @@ end -- @param line Line of text which the width will be computed. -- @return Text width -function font_api.Font:get_width(line) - - local char +function Font:get_width(line) + local codepoint local width = 0 - local pos = 1 + line = line or '' - -- TODO: Use iterator - while pos <= #line do - char, pos = get_next_char(line, pos) - width = width + self:get_char_width(char) + while line ~= "" do + codepoint, line = self:get_next_char(line) + width = width + self:get_char_width(codepoint) end return width @@ -176,30 +192,21 @@ end -- @param y Vertical position of the line in texture -- @return Texture string -function font_api.Font:make_line_texture(line, texturew, x, y) +function Font:make_line_texture(line, texturew, x, y) + local codepoint local texture = "" - local char - local pos = 1 - - -- TODO: Use iterator - while pos <= #line do - char, pos = get_next_char(line, pos) - - -- Replace chars with no texture by the NULL(0) char - if self.widths[char] == nil - then - print(string.format("["..font_api.name - .."] Missing char %d (%04x)",char,char)) - char = 0 - end + line = line or '' + + while line ~= '' do + codepoint, line = self:get_next_char(line) -- Add image only if it is visible (at least partly) - if x + self.widths[char] >= 0 and x <= texturew then + if x + self.widths[codepoint] >= 0 and x <= texturew then texture = texture.. string.format(":%d,%d=font_%s_%04x.png", - x, y, self.name, char) + x, y, self.name, codepoint) end - x = x + self.widths[char] + x = x + self.widths[codepoint] end return texture @@ -215,13 +222,13 @@ end -- @param color Color of the text (optional) -- @return Texture string -function font_api.Font:make_text_texture(text, texturew, textureh, maxlines, +function 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) } @@ -238,7 +245,7 @@ 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 @@ -263,4 +270,3 @@ function font_api.Font:make_text_texture(text, texturew, textureh, maxlines, if color then texture = texture.."^[colorize:"..color end return texture end - -- cgit v1.2.3 From 06d35ec9bf48e5fd96952cca5264d92742cf31db Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Thu, 1 Nov 2018 12:25:47 +0100 Subject: Rewrited split_lines to avoid a string.split bug if first line empty --- font_api/font.lua | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'font_api') diff --git a/font_api/font.lua b/font_api/font.lua index db12dba..e12bb4b 100644 --- a/font_api/font.lua +++ b/font_api/font.lua @@ -55,17 +55,17 @@ local function char_to_codepoint(str) end -- Split multiline text into array of lines, with maximum lines. +-- Can not use minetest string.split as it has bug if first line(s) empty 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 + local lines = {} + local pos = 1 + repeat + local found = string.find(text, "\n", pos) + found = found or #text + 1 + lines[#lines + 1] = string.sub(text, pos, found - 1) + pos = found + 1 + until (maxlines and (#lines >= maxlines)) or (pos > (#text + 1)) + return lines end -------------------------------------------------------------------------------- -- cgit v1.2.3 From ab70424143c878188428de842ca893c419f605c1 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Fri, 9 Nov 2018 15:28:07 +0100 Subject: Update README.md --- font_api/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'font_api') diff --git a/font_api/README.md b/font_api/README.md index c7ae64b..2c485d4 100644 --- a/font_api/README.md +++ b/font_api/README.md @@ -1,4 +1,4 @@ -# Font Lib +# Font API This library for font display on entities (to be used with display_api for sign creation). -- 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') 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 From 1221cfcc4aed566c9f141459c13b0b919fc4306a Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Tue, 13 Nov 2018 14:02:04 +0100 Subject: Update README.md --- font_api/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'font_api') diff --git a/font_api/README.md b/font_api/README.md index 2c485d4..33af92b 100644 --- a/font_api/README.md +++ b/font_api/README.md @@ -12,3 +12,12 @@ This library for font display on entities (to be used with display_api for sign For more information, see the [forum topic](https://forum.minetest.net/viewtopic.php?t=13563) at the Minetest forums. +## Extra fonts + +You can add fonts by installing fonts mod. Be aware that each font comes with numerous textures. This can result in slowing media downloading and/or client display. + +Font mods can be found here: + + * [Metro](https://github.com/pyrollo/display_modpack/tree/master/font_metro): A multipurpose font with many chars (uppercase, lowercase and accentuated latin letters, usual signs, cyrillic and greek letters). + * [OldWizard](https://github.com/pyrollo/font_oldwizard): An old style gothic font. + * [Botic](https://github.com/pyrollo/font_botic): A scifi style font. -- cgit v1.2.3 From 641dddfd5100eafc3d8571576c0ae679c2877ea3 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Sun, 18 Nov 2018 15:29:59 +0100 Subject: Code simplification and securisation --- font_api/fontform.lua | 149 ++++++++++++++++++++++---------------------------- 1 file changed, 66 insertions(+), 83 deletions(-) (limited to 'font_api') diff --git a/font_api/fontform.lua b/font_api/fontform.lua index 118d92d..01929dd 100644 --- a/font_api/fontform.lua +++ b/font_api/fontform.lua @@ -19,67 +19,37 @@ 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 + if minetest.is_player(player) then + contexts[player:get_player_name()] = nil 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 +local function get_context(playername) + if not contexts[playername] then + contexts[playername] = { playername = playername } end + return contexts[playername] end -- Show node formspec functions - -local function show_node_formspec(player, pos) +local function show_node_formspec(playername, pos) local meta = minetest.get_meta(pos) - local playername = get_player_name(player) -- Decontextualize formspec local fs = meta:get_string('formspec') + if not fs then + return + end + + -- Change context and currrent_name references to nodemeta references -- 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) + local nodemeta = string.format("nodemeta:%i,%i,%i", pos.x, pos.y ,pos.z) + fs = fs:gsub("current_name", nodemeta) + fs = fs:gsub("context", nodemeta) -- Change all ${} to their corresponding metadata values local s, e @@ -92,44 +62,44 @@ local function show_node_formspec(player, pos) end until s == nil + local context = get_context(playername) + context.node_pos = pos + -- 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 } ) + context.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 formname ~= modname..':context_formspec' then + return + end - if context.on_receive_fields then - context.on_receive_fields(context.pos, '', fields, player) - end + if not minetest.is_player(player) then + return true end + + local context = get_context(player:get_player_name()) + if context.on_receive_fields then + context.on_receive_fields(context.pos, '', fields, player) + end + return true end) -- Specific functions -local function font_list_prepare() - local list = {} +local function show_font_formspec(playername) + local context = get_context(playername) + local fonts = {} for name, _ in pairs(font_api.registered_fonts) do - list[#list+1] = name + fonts[#fonts+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() + table.sort(fonts) local fs = 'size[4,'..(#fonts + 0.8)..']' ..default.gui_bg..default.gui_bg_img..default.gui_slots @@ -146,26 +116,39 @@ local function show_fs(player) 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 + if formname ~= modname..':font_list' then + return + end + + if not minetest.is_player(player) then + return true + end + + local playername = player:get_player_name() + local context = get_context(playername) - -- Using after to avoid the "double close" bug - minetest.after(0, show_node_formspec, player, context.pos) + if minetest.is_protected(context.pos, playername) then + return true + 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, playername, context.pos) end + return true end) -function font_api.show_font_list(player, pos) - new_context(player, { pos = pos }) - show_fs(player) +function font_api.show_font_list_from_pos(player, pos) + if minetest.is_player(player) then + local context = get_context(player:get_player_name()) + context.pos = pos + show_font_formspec(player:get_player_name()) + end end -- cgit v1.2.3 From 07eb4737fa9d466bbfc2296925502acc1e90b3a4 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Sun, 18 Nov 2018 15:30:37 +0100 Subject: Added font formspec textures --- font_api/textures/font_api_center.png | Bin 0 -> 224 bytes font_api/textures/font_api_font.png | Bin 0 -> 238 bytes font_api/textures/font_api_left.png | Bin 0 -> 220 bytes font_api/textures/font_api_right.png | Bin 0 -> 222 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 font_api/textures/font_api_center.png create mode 100644 font_api/textures/font_api_font.png create mode 100644 font_api/textures/font_api_left.png create mode 100644 font_api/textures/font_api_right.png (limited to 'font_api') diff --git a/font_api/textures/font_api_center.png b/font_api/textures/font_api_center.png new file mode 100644 index 0000000..967db6a Binary files /dev/null and b/font_api/textures/font_api_center.png differ diff --git a/font_api/textures/font_api_font.png b/font_api/textures/font_api_font.png new file mode 100644 index 0000000..e450338 Binary files /dev/null and b/font_api/textures/font_api_font.png differ diff --git a/font_api/textures/font_api_left.png b/font_api/textures/font_api_left.png new file mode 100644 index 0000000..727410b Binary files /dev/null and b/font_api/textures/font_api_left.png differ diff --git a/font_api/textures/font_api_right.png b/font_api/textures/font_api_right.png new file mode 100644 index 0000000..16401d3 Binary files /dev/null and b/font_api/textures/font_api_right.png differ -- cgit v1.2.3 From 367e5026ddef3e6a0220f37a2f4aba64ec88f16b Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Fri, 23 Nov 2018 11:34:55 +0100 Subject: Fonts configurable in signs, posters and steles --- font_api/fontform.lua | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'font_api') diff --git a/font_api/fontform.lua b/font_api/fontform.lua index 01929dd..e4952ed 100644 --- a/font_api/fontform.lua +++ b/font_api/fontform.lua @@ -101,16 +101,18 @@ local function show_font_formspec(playername) end table.sort(fonts) - 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]' + local fs = string.format( + "size[4,%s]%s%s%sbutton_exit[0,%s;4,1;cancel;Cancel]", + #fonts + 0.8, default.gui_bg, default.gui_bg_img, default.gui_slots, + #fonts) 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, + local texture = 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..';]' + 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) end minetest.show_formspec(context.playername, modname..':font_list', fs) end @@ -127,7 +129,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local playername = player:get_player_name() local context = get_context(playername) - if minetest.is_protected(context.pos, playername) then + if not context.pos + or minetest.is_protected(context.pos, playername) then return true end @@ -139,16 +142,26 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) display_api.update_entities(context.pos) end end - -- Using after to avoid the "double close" bug - minetest.after(0, show_node_formspec, playername, context.pos) + + if context.callback and type(context.callback) == "function" then + -- Using after to avoid the "double close" bug + minetest.after(0, context.callback, playername, context.pos) + else + -- Using after to avoid the "double close" bug + minetest.after(0, show_node_formspec, playername, context.pos) + end end return true end) -function font_api.show_font_list_from_pos(player, pos) +-- @param player Player viewing the form +-- @param pos Node pos +-- @param callback function(playername, pos) to be called on form close +function font_api.show_font_list(player, pos, callback) if minetest.is_player(player) then local context = get_context(player:get_player_name()) context.pos = pos + context.callback = callback show_font_formspec(player:get_player_name()) end end -- cgit v1.2.3