New Font:render method

This commit is contained in:
Pierre-Yves Rollo 2018-12-05 15:41:56 +01:00
parent 1d4c45e130
commit 47cb8f2cd3
3 changed files with 132 additions and 126 deletions

44
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. 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"). * `halign`: Horizontal alignment: "left", "center" or "right" (default "center").
* `valign`: Vertical alignement: "top", "middle" or "bottom" (default "middle"). * `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 ## Provided methods
### font_api.get_default_font_name() ### font_api.get_default_font_name()
Returns de 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. 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. * `line`: Line of text which the width will be computed.
### font:make_line_texture(line, texturew, x, y) ### font:renter(text, texturew, textureh, style)
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. Builds texture for a multiline colored text.
* `text`: Text to be rendered. * `text`: Text to be rendered.
* `texturew`: Width of the texture (extra text will be truncated). * `texturew`: Width of the texture (extra text will be truncated).
* `textureh`: Height of the texture. * `textureh`: Height of the texture (extra text will be truncated).
* `maxlines`: Maximum number of lines. * `style`: A table with style indications:
* `halign`: Horizontal text align ("left"/"center"/"right") (optional). - `lines` or `maxlines`: Maximum number of lines (default none).
* `valign`: Vertical text align ("top"/"center"/"bottom") (optional). - `halign`: Horizontal text align: "left"/"center"/"right" (default "center")
* `color`: Color of the text (optional). - `valign`: Vertical text align: "top"/"middle"/"bottom" (default "middle")
- `color`: Color of the text (default black)

142
font.lua

@ -54,20 +54,6 @@ local function char_to_codepoint(str)
end end
end end
-- Split multiline text into array of lines, with <maxlines> 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 --- Font class
@ -195,88 +181,86 @@ function Font:get_width(line)
return width return width
end end
--- Builds texture part for a text line --- Legacy make_text_texture method (replaced by "render" - Dec 2018)
-- @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
function Font:make_text_texture(text, texturew, textureh, maxlines, function Font:make_text_texture(text, texturew, textureh, maxlines,
halign, valign, color) halign, valign, color)
local texture = "" return self:render(text, texturew, textureh, {
local lines = {} lines = maxlines,
local textheight = 0 valign = valign,
local y halign = halign,
color = color
})
end
-- Split text into lines (limited to maxlines fist lines) --- Render text with the font in a view
for num, line in pairs(split_lines(text, maxlines)) do -- @param text Text to be rendered
lines[num] = { text = line, width = self:get_width(line) } -- @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 end
textheight = self:get_height(#lines) local x, y, codepoint
local texture = ""
local textheight = self:get_height(#lines)
if #lines then if style.valign == "top" then
if valign == "top" then y = 0
y = 0 elseif style.valign == "bottom" then
elseif valign == "bottom" then y = textureh - textheight
y = textureh - textheight else
else y = (textureh - textheight) / 2
y = (textureh - textheight) / 2
end
end end
y = y + (self.margintop or 0) y = y + (self.margintop or 0)
for _, line in pairs(lines) do for _, line in pairs(lines) do
if halign == "left" then if style.halign == "left" then
texture = texture.. x = 0
self:make_line_texture(line.text, texturew, elseif style.halign == "right" then
0, y) x = texturew - line.width
elseif halign == "right" then
texture = texture..
self:make_line_texture(line.text, texturew,
texturew - line.width, y)
else else
texture = texture.. x = (texturew - line.width) / 2
self:make_line_texture(line.text, texturew, end
(texturew - line.width) / 2, y)
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 end
y = y + self:get_height() + (self.linespacing or 0) y = y + self:get_height() + (self.linespacing or 0)
end end
texture = string.format("[combine:%dx%d", texturew, textureh)..texture 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 return texture
end end

@ -39,43 +39,49 @@ dofile(font_api.path.."/fontform.lua")
function font_api.on_display_update(pos, objref) function font_api.on_display_update(pos, objref)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local text = meta:get_string("display_text")
local ndef = minetest.registered_nodes[minetest.get_node(pos).name] local ndef = minetest.registered_nodes[minetest.get_node(pos).name]
local entity = objref:get_luaentity() local entity = objref:get_luaentity()
if entity and ndef.display_entities[entity.name] then if not entity or not ndef.display_entities[entity.name] then
local def = ndef.display_entities[entity.name] return
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
})
end 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 end
-- Compatibility -- Compatibility