From 47cb8f2cd3520c795553d731ea91df9828d2d96e Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Wed, 5 Dec 2018 15:41:56 +0100 Subject: [PATCH] New Font:render method --- API.md | 44 +++++++++++------ font.lua | 142 ++++++++++++++++++++++++------------------------------- init.lua | 72 +++++++++++++++------------- 3 files changed, 132 insertions(+), 126 deletions(-) diff --git a/API.md b/API.md index 6c14ac4..f3f4437 100644 --- a/API.md +++ b/API.md @@ -37,7 +37,7 @@ minetest.register_node("mymod:test_text_node", { }) ``` -At this step, your node already displays text form "display_text" (hardcoded) node meta. +At this step, your node already displays text form "display_text" (by default) node meta. If you want to store your text into another meta data field, add a `meta_text` field to display entity definition. But it uses defaults (default font, default size, default color). Likely you need something more. @@ -59,6 +59,28 @@ Then specify the char width. Two methods available: * `halign`: Horizontal alignment: "left", "center" or "right" (default "center"). * `valign`: Vertical alignement: "top", "middle" or "bottom" (default "middle"). +### Example +Using blue //botic// font, three lines height, aligned top left. Text stored in "text" node meta. +``` +minetest.register_node("mymod:test_text_node", { + ... + ... + display_entities = { + ["mymod:text"] = { + depth = -0.5 - display_api.entity_spacing, + on_display_update = font_api.on_display_update + meta_text = "text", + font_name = "botic", + color = "#0000FF", + maxlines = 3, + aspect_ratio = 0.5, + halign = "left", + valign = "top", + }, + } + ... +}) +``` ## Provided methods ### font_api.get_default_font_name() Returns de default font name. @@ -159,19 +181,13 @@ Returns line(s) height. Takes care of top and bottom margins and line spacing. 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) +### font:renter(text, texturew, textureh, style) 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). + * `textureh`: Height of the texture (extra text will be truncated). + * `style`: A table with style indications: + - `lines` or `maxlines`: Maximum number of lines (default none). + - `halign`: Horizontal text align: "left"/"center"/"right" (default "center") + - `valign`: Vertical text align: "top"/"middle"/"bottom" (default "middle") + - `color`: Color of the text (default black) diff --git a/font.lua b/font.lua index a69e049..8eb43db 100644 --- a/font.lua +++ b/font.lua @@ -54,20 +54,6 @@ local function char_to_codepoint(str) end 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 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 - -------------------------------------------------------------------------------- --- Font class @@ -195,88 +181,86 @@ function Font:get_width(line) 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:make_line_texture(line, texturew, x, y) - local codepoint - local texture = "" - 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:get_char_width(codepoint) >= 0 and x <= texturew then - texture = texture.. - string.format(":%d,%d=font_%s_%04x.png", - x, y, self.name, codepoint) - end - x = x + self:get_char_width(codepoint) - 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 +--- Legacy make_text_texture method (replaced by "render" - Dec 2018) function Font:make_text_texture(text, texturew, textureh, maxlines, halign, valign, color) - local texture = "" - local lines = {} - local textheight = 0 - local y + return self:render(text, texturew, textureh, { + lines = maxlines, + valign = valign, + halign = halign, + color = color + }) +end - -- 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) } +--- Render text with the font in a view +-- @param text Text to be rendered +-- @param texturew Width (in pixels) of the texture (extra text will be truncated) +-- @param textureh Height (in pixels) of the texture (extra text will be truncated) +-- @param style Style of the rendering: +-- - lines: maximum number of text lines (if text is limited) +-- - halign: horizontal align ("left"/"center"/"right") +-- - valign: vertical align ("top"/"center"/"bottom") +-- - color: color of the text ("#rrggbb") +-- @return Texture string + +function Font:render(text, texturew, textureh, style) + local style = style or {} + + -- Split text into lines (and limit to style.lines # of lines) + local lines = {} + local pos = 1 + local found, line + repeat + found = string.find(text, "\n", pos) or (#text + 1) + line = string.sub(text, pos, found - 1) + lines[#lines + 1] = { text = line, width = self:get_width(line) } + pos = found + 1 + until (style.lines and (#lines >= style.lines)) or (pos > (#text + 1)) + + if not #lines then + return "" end - textheight = self:get_height(#lines) + local x, y, codepoint + local texture = "" + local 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 + if style.valign == "top" then + y = 0 + elseif style.valign == "bottom" then + y = textureh - textheight + else + y = (textureh - textheight) / 2 end y = y + (self.margintop or 0) 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) + if style.halign == "left" then + x = 0 + elseif style.halign == "right" then + x = texturew - line.width else - texture = texture.. - self:make_line_texture(line.text, texturew, - (texturew - line.width) / 2, y) + x = (texturew - line.width) / 2 + end + + while line.text ~= '' do + codepoint, line.text = self:get_next_char(line.text) + + -- Add image only if it is visible (at least partly) + if x + self.widths[codepoint] >= 0 and x <= texturew then + texture = texture.. + string.format(":%d,%d=font_%s_%04x.png", x, y, self.name, codepoint) + end + x = x + self.widths[codepoint] end y = y + self:get_height() + (self.linespacing or 0) end - texture = string.format("[combine:%dx%d", texturew, textureh)..texture - if color then texture = texture.."^[colorize:"..color end + if style.color then + texture = texture.."^[colorize:"..style.color + end return texture end diff --git a/init.lua b/init.lua index 57a3f44..307049b 100644 --- a/init.lua +++ b/init.lua @@ -39,43 +39,49 @@ dofile(font_api.path.."/fontform.lua") 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 = font_api.get_font(meta:get_string("font") ~= "" - and meta:get_string("font") or def.font_name) - - -- Compute entity resolution accroding to given attributes - local texturew, textureh - textureh = font:get_height(def.lines or def.maxlines or 1) - - if def.columns then - if font.fixedwidth then - texturew = def.columns * font.fixedwidth - if def.aspect_ratio then - minetest.log('warning', "[font_api] 'aspect_ratio' ignored because 'columns' is specified") - end - else - minetest.log('warning', "[font_api] 'columns' ignored because '"..font.name.."' is not a fixed width font.") - end - end - - if not texturew then - if not def.aspect_ratio then - minetest.log('warning', "[font_api] No 'aspect_ratio' specified, using default 1.") - end - texturew = textureh * def.size.x / def.size.y / (def.aspect_ratio or 1) - end - - objref:set_properties({ - textures={font:make_text_texture(text, texturew, textureh, - def.maxlines, def.halign, def.valign, def.color)}, - visual_size = def.size - }) + if not entity or not ndef.display_entities[entity.name] then + return end + + local def = ndef.display_entities[entity.name] + local font = font_api.get_font(meta:get_string("font") ~= "" + and meta:get_string("font") or def.font_name) + + local text = meta:get_string(def.meta_text or "display_text") + + -- Compute entity resolution accroding to given attributes + local texturew, textureh + textureh = font:get_height(def.lines or def.maxlines or 1) + + if def.columns then + if font.fixedwidth then + texturew = def.columns * font.fixedwidth + if def.aspect_ratio then + minetest.log('warning', "[font_api] 'aspect_ratio' ignored because 'columns' is specified") + end + else + minetest.log('warning', "[font_api] 'columns' ignored because '"..font.name.."' is not a fixed width font.") + end + end + + if not texturew then + if not def.aspect_ratio then + minetest.log('warning', "[font_api] No 'aspect_ratio' specified, using default 1.") + end + texturew = textureh * def.size.x / def.size.y / (def.aspect_ratio or 1) + end + + objref:set_properties({ + textures={ font:render(text, texturew, textureh, { + lines = def.maxlines or def.lines, + halign = def.halign, + valign = def.valign, + color = def.color} ) }, + visual_size = def.size, + }) end -- Compatibility