diff options
author | Pierre-Yves Rollo <dev@pyrollo.com> | 2018-07-08 20:36:34 +0200 |
---|---|---|
committer | Pierre-Yves Rollo <dev@pyrollo.com> | 2018-07-08 20:36:34 +0200 |
commit | c6cad702bcea7f7836153b9b7f6ad847e3bd605e (patch) | |
tree | 3c571f40c56ad1bd97f306c02a25eee1bc0c2d33 | |
parent | 23bcd7019986c5a943baad30cd73034f88079394 (diff) | |
download | display_modpack-c6cad702bcea7f7836153b9b7f6ad847e3bd605e.tar.gz display_modpack-c6cad702bcea7f7836153b9b7f6ad847e3bd605e.tar.bz2 display_modpack-c6cad702bcea7f7836153b9b7f6ad847e3bd605e.zip |
Creation of Font class and code update accordingly
-rw-r--r-- | font_api/API.md | 131 | ||||
-rw-r--r-- | font_api/font.lua | 270 | ||||
-rw-r--r-- | font_api/init.lua | 295 | ||||
-rw-r--r-- | font_api/registry.lua | 151 | ||||
-rwxr-xr-x | font_api/tools/make_font_lua.sh | 19 | ||||
-rwxr-xr-x | font_api/tools/make_font_textures.sh | 2 | ||||
-rw-r--r-- | font_epilepsy/init.lua | 6 |
7 files changed, 532 insertions, 342 deletions
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\_<font\_name>_<utf\_code>.png** +**{utf\_code}**: UTF code of the char in 4 hexadecimal digits -**<font\_name>**: Name of the font as given in the first argument -**<utf\_code>**: 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 <font\_name>.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 <fontfile> <fontname> <fontsize>** +**make\_font\_texture.sh {fontfile} {fontname} {fontsize}** -**<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. +**{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\_<font\_name>.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\_<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. +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 <fontname>** +**make\_font_lua.sh {fontname}** -**<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__ /<path_to_font_api>/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 <http://www.gnu.org/licenses/>. +--]] + + +--[[ + 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 <maxlines> 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 <maxlines> 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_<name>_<code>.png --- <name> : name of the font --- <code> : 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 <http://www.gnu.org/licenses/>. +--]] + +-- 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_<name>_<code>.png +-- <name> : name of the font +-- <code> : 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_<fontname>_????.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 "<map code=" | cut -d \" -f 2) +codepoints=$(ttx -o - "$fontfile" | grep "<map code=" | cut -d \" -f 2) # Mandatory chars generate 0020 007f diff --git a/font_epilepsy/init.lua b/font_epilepsy/init.lua index 6dd7d74..d3f63b3 100644 --- a/font_epilepsy/init.lua +++ b/font_epilepsy/init.lua @@ -6,7 +6,9 @@ font_api.register_font( 'epilepsy', - 14, - { [0]=8, [32]=8, [33]=5, [34]=8, [35]=8, [36]=9, [37]=10, [38]=10, [39]=5, [40]=6, [41]=6, [42]=7, [43]=7, [44]=4, [45]=5, [46]=5, [47]=7, [48]=8, [49]=8, [50]=8, [51]=8, [52]=8, [53]=8, [54]=8, [55]=8, [56]=8, [57]=8, [58]=5, [59]=5, [60]=7, [61]=7, [62]=7, [63]=7, [64]=13, [65]=9, [66]=8, [67]=8, [68]=8, [69]=7, [70]=7, [71]=9, [72]=8, [73]=5, [74]=6, [75]=9, [76]=7, [77]=11, [78]=9, [79]=9, [80]=8, [81]=9, [82]=8, [83]=8, [84]=8, [85]=8, [86]=9, [87]=11, [88]=9, [89]=10, [90]=9, [91]=5, [92]=7, [93]=5, [94]=7, [95]=7, [96]=5, [97]=8, [98]=8, [99]=8, [100]=8, [101]=8, [102]=6, [103]=8, [104]=8, [105]=4, [106]=5, [107]=8, [108]=4, [109]=12, [110]=8, [111]=8, [112]=8, [113]=8, [114]=7, [115]=7, [116]=6, [117]=8, [118]=8, [119]=11, [120]=9, [121]=8, [122]=8, [123]=6, [124]=5, [125]=6, [126]=9, [160]=4, [161]=5, [162]=9, [163]=9, [164]=9, [165]=10, [166]=5, [167]=6, [168]=7, [169]=10, [170]=6, [171]=7, [172]=7, [173]=5, [174]=10, [175]=6, [176]=6, [177]=7, [178]=5, [179]=5, [180]=5, [181]=9, [182]=7, [183]=5, [184]=6, [185]=5, [186]=6, [187]=7, [188]=10, [189]=10, [190]=10, [191]=7, [192]=9, [193]=9, [194]=9, [195]=9, [196]=9, [197]=9, [198]=11, [199]=8, [200]=7, [201]=7, [202]=7, [203]=7, [204]=5, [205]=5, [206]=6, [207]=6, [208]=9, [209]=9, [210]=9, [211]=9, [212]=9, [213]=9, [214]=9, [215]=7, [216]=9, [217]=8, [218]=8, [219]=8, [220]=8, [221]=10, [222]=8, [223]=8, [224]=8, [225]=8, [226]=8, [227]=8, [228]=8, [229]=8, [230]=12, [231]=8, [232]=8, [233]=8, [234]=8, [235]=8, [236]=4, [237]=4, [238]=5, [239]=5, [240]=8, [241]=8, [242]=8, [243]=8, [244]=8, [245]=8, [246]=8, [247]=8, [248]=10, [249]=8, [250]=8, [251]=8, [252]=8, [253]=8, [254]=8, [255]=8, [256]=9, [257]=8, [258]=9, [259]=8, [260]=9, [261]=8, [262]=8, [263]=8, [264]=8, [265]=8, [266]=8, [267]=8, [268]=8, [269]=8, [270]=8, [271]=11, [272]=9, [273]=8, [274]=7, [275]=8, [276]=7, [277]=8, [278]=7, [279]=8, [280]=7, [281]=8, [282]=7, [283]=8, [284]=9, [285]=8, [286]=9, [287]=8, [288]=9, [289]=8, [290]=9, [291]=8, [292]=8, [293]=8, [294]=8, [295]=8, [296]=7, [297]=6, [298]=5, [299]=4, [300]=6, [301]=5, [302]=5, [303]=4, [304]=5, [305]=4, [306]=10, [307]=8, [308]=6, [309]=5, [310]=9, [311]=8, [312]=8, [313]=7, [314]=4, [315]=7, [316]=4, [317]=10, [318]=7, [319]=7, [320]=8, [321]=7, [322]=5, [323]=9, [324]=8, [325]=9, [326]=8, [327]=9, [328]=8, [329]=11, [330]=9, [331]=8, [332]=9, [333]=8, [334]=9, [335]=8, [336]=9, [337]=8, [338]=11, [339]=11, [340]=8, [341]=7, [342]=8, [343]=7, [344]=8, [345]=7, [346]=8, [347]=7, [348]=8, [349]=7, [350]=8, [351]=7, [352]=8, [353]=7, [354]=8, [355]=6, [356]=8, [357]=10, [358]=8, [359]=6, [360]=8, [361]=8, [362]=8, [363]=8, [364]=8, [365]=8, [366]=8, [367]=8, [368]=8, [369]=8, [370]=8, [371]=8, [372]=11, [373]=11, [374]=10, [375]=8, [376]=10, [377]=9, [378]=8, [379]=9, [380]=8, [381]=9, [382]=8, [383]=6, [884]=5, [885]=5, [890]=4, [894]=4, [900]=5, [901]=8, [902]=9, [903]=5, [904]=7, [905]=8, [906]=5, [908]=9, [910]=10, [911]=9, [912]=6, [913]=9, [914]=8, [915]=7, [916]=10, [917]=7, [918]=9, [919]=8, [920]=9, [921]=5, [922]=9, [923]=9, [924]=11, [925]=9, [926]=8, [927]=9, [928]=8, [929]=8, [931]=8, [932]=8, [933]=10, [934]=10, [935]=9, [936]=12, [937]=9, [938]=6, [939]=10, [940]=9, [941]=8, [942]=8, [943]=5, [944]=8, [945]=9, [946]=8, [947]=8, [948]=8, [949]=8, [950]=8, [951]=8, [952]=8, [953]=5, [954]=7, [955]=8, [956]=9, [957]=8, [958]=8, [959]=8, [960]=9, [961]=8, [962]=8, [963]=9, [964]=9, [965]=8, [966]=10, [967]=8, [968]=12, [969]=10, [970]=6, [971]=8, [972]=8, [973]=8, [974]=10, [976]=8, [977]=10, [978]=11, [979]=11, [980]=11, [981]=10, [982]=10, [983]=9, [984]=9, [985]=8, [986]=9, [987]=8, [988]=7, [989]=7, [990]=9, [991]=7, [992]=11, [993]=8, [994]=10, [995]=10, [996]=9, [997]=9, [998]=9, [999]=10, [1000]=8, [1001]=7, [1002]=9, [1003]=8, [1004]=8, [1005]=8, [1006]=10, [1007]=8, [1008]=9, [1009]=8, [1010]=8, [1011]=5, [1012]=9, [1013]=9, [1014]=9, [1015]=8, [1016]=8, [1017]=8, [1018]=11, [1019]=11, [1020]=8, [1021]=8, [1022]=8, [1023]=8, [1024]=7, [1025]=7, [1026]=10, [1027]=7, [1028]=9, [1029]=7, [1030]=5, [1031]=6, [1032]=6, [1033]=14, [1034]=13, [1035]=10, [1036]=9, [1037]=9, [1038]=10, [1039]=8, [1040]=9, [1041]=8, [1042]=8, [1043]=7, [1044]=10, [1045]=7, [1046]=12, [1047]=8, [1048]=9, [1049]=9, [1050]=9, [1051]=10, [1052]=11, [1053]=8, [1054]=9, [1055]=8, [1056]=8, [1057]=8, [1058]=8, [1059]=10, [1060]=10, [1061]=9, [1062]=8, [1063]=8, [1064]=12, [1065]=12, [1066]=11, [1067]=11, [1068]=8, [1069]=9, [1070]=12, [1071]=8, [1072]=8, [1073]=8, [1074]=8, [1075]=6, [1076]=10, [1077]=8, [1078]=12, [1079]=8, [1080]=9, [1081]=9, [1082]=7, [1083]=10, [1084]=11, [1085]=8, [1086]=8, [1087]=9, [1088]=8, [1089]=8, [1090]=8, [1091]=8, [1092]=12, [1093]=9, [1094]=8, [1095]=8, [1096]=10, [1097]=10, [1098]=10, [1099]=10, [1100]=7, [1101]=9, [1102]=10, [1103]=8, [1104]=8, [1105]=8, [1106]=8, [1107]=6, [1108]=9, [1109]=7, [1110]=4, [1111]=5, [1112]=5, [1113]=12, [1114]=11, [1115]=8, [1116]=7, [1117]=9, [1118]=8, [1119]=8, [1120]=12, [1121]=10, [1122]=8, [1123]=7, [1124]=11, [1125]=11, [1126]=10, [1127]=9, [1128]=13, [1129]=12, [1130]=10, [1131]=10, [1132]=13, [1133]=13, [1134]=8, [1135]=8, [1136]=12, [1137]=12, [1138]=9, [1139]=7, [1140]=11, [1141]=10, [1142]=11, [1143]=10, [1144]=18, [1145]=17, [1146]=11, [1147]=9, [1148]=12, [1149]=10, [1150]=12, [1151]=10, [1152]=8, [1153]=7, [1154]=7, [1155]=1, [1160]=14, [1161]=14, [1162]=9, [1163]=9, [1164]=8, [1165]=7, [1166]=8, [1167]=8, [1168]=7, [1169]=7, [1170]=7, [1171]=6, [1172]=8, [1173]=8, [1174]=12, [1175]=12, [1176]=8, [1177]=8, [1178]=9, [1179]=8, [1180]=9, [1181]=7, [1182]=9, [1183]=7, [1184]=12, [1185]=10, [1186]=8, [1187]=8, [1188]=11, [1189]=11, [1190]=12, [1191]=12, [1192]=11, [1193]=10, [1194]=8, [1195]=8, [1196]=8, [1197]=8, [1198]=10, [1199]=10, [1200]=10, [1201]=10, [1202]=9, [1203]=9, [1204]=11, [1205]=11, [1206]=8, [1207]=8, [1208]=9, [1209]=9, [1210]=8, [1211]=8, [1212]=11, [1213]=11, [1214]=11, [1215]=11, [1216]=5, [1217]=12, [1218]=12, [1219]=8, [1220]=8, [1221]=10, [1222]=10, [1223]=8, [1224]=8, [1225]=8, [1226]=8, [1227]=8, [1228]=8, [1229]=11, [1230]=11, [1232]=9, [1233]=8, [1234]=9, [1235]=8, [1236]=11, [1237]=12, [1238]=7, [1239]=8, [1240]=8, [1241]=8, [1242]=8, [1243]=8, [1244]=12, [1245]=12, [1246]=8, [1247]=8, [1248]=8, [1249]=8, [1250]=9, [1251]=9, [1252]=9, [1253]=9, [1254]=9, [1255]=8, [1256]=9, [1257]=8, [1258]=9, [1259]=8, [1260]=9, [1261]=9, [1262]=10, [1263]=8, [1264]=10, [1265]=8, [1266]=10, [1267]=8, [1268]=8, [1269]=8, [1270]=7, [1271]=6, [1272]=11, [1273]=10, [8208]=5, [8209]=5, [8210]=8, [8211]=7, [8212]=10, [8213]=12, [8216]=4, [8217]=4, [8218]=4, [8219]=4, [8220]=7, [8221]=7, [8222]=7, [8223]=7, [8364]=8 } + { + height = 14, + widths = { [0]=8, [32]=8, [33]=5, [34]=8, [35]=8, [36]=9, [37]=10, [38]=10, [39]=5, [40]=6, [41]=6, [42]=7, [43]=7, [44]=4, [45]=5, [46]=5, [47]=7, [48]=8, [49]=8, [50]=8, [51]=8, [52]=8, [53]=8, [54]=8, [55]=8, [56]=8, [57]=8, [58]=5, [59]=5, [60]=7, [61]=7, [62]=7, [63]=7, [64]=13, [65]=9, [66]=8, [67]=8, [68]=8, [69]=7, [70]=7, [71]=9, [72]=8, [73]=5, [74]=6, [75]=9, [76]=7, [77]=11, [78]=9, [79]=9, [80]=8, [81]=9, [82]=8, [83]=8, [84]=8, [85]=8, [86]=9, [87]=11, [88]=9, [89]=10, [90]=9, [91]=5, [92]=7, [93]=5, [94]=7, [95]=7, [96]=5, [97]=8, [98]=8, [99]=8, [100]=8, [101]=8, [102]=6, [103]=8, [104]=8, [105]=4, [106]=5, [107]=8, [108]=4, [109]=12, [110]=8, [111]=8, [112]=8, [113]=8, [114]=7, [115]=7, [116]=6, [117]=8, [118]=8, [119]=11, [120]=9, [121]=8, [122]=8, [123]=6, [124]=5, [125]=6, [126]=9, [160]=4, [161]=5, [162]=9, [163]=9, [164]=9, [165]=10, [166]=5, [167]=6, [168]=7, [169]=10, [170]=6, [171]=7, [172]=7, [173]=5, [174]=10, [175]=6, [176]=6, [177]=7, [178]=5, [179]=5, [180]=5, [181]=9, [182]=7, [183]=5, [184]=6, [185]=5, [186]=6, [187]=7, [188]=10, [189]=10, [190]=10, [191]=7, [192]=9, [193]=9, [194]=9, [195]=9, [196]=9, [197]=9, [198]=11, [199]=8, [200]=7, [201]=7, [202]=7, [203]=7, [204]=5, [205]=5, [206]=6, [207]=6, [208]=9, [209]=9, [210]=9, [211]=9, [212]=9, [213]=9, [214]=9, [215]=7, [216]=9, [217]=8, [218]=8, [219]=8, [220]=8, [221]=10, [222]=8, [223]=8, [224]=8, [225]=8, [226]=8, [227]=8, [228]=8, [229]=8, [230]=12, [231]=8, [232]=8, [233]=8, [234]=8, [235]=8, [236]=4, [237]=4, [238]=5, [239]=5, [240]=8, [241]=8, [242]=8, [243]=8, [244]=8, [245]=8, [246]=8, [247]=8, [248]=10, [249]=8, [250]=8, [251]=8, [252]=8, [253]=8, [254]=8, [255]=8, [256]=9, [257]=8, [258]=9, [259]=8, [260]=9, [261]=8, [262]=8, [263]=8, [264]=8, [265]=8, [266]=8, [267]=8, [268]=8, [269]=8, [270]=8, [271]=11, [272]=9, [273]=8, [274]=7, [275]=8, [276]=7, [277]=8, [278]=7, [279]=8, [280]=7, [281]=8, [282]=7, [283]=8, [284]=9, [285]=8, [286]=9, [287]=8, [288]=9, [289]=8, [290]=9, [291]=8, [292]=8, [293]=8, [294]=8, [295]=8, [296]=7, [297]=6, [298]=5, [299]=4, [300]=6, [301]=5, [302]=5, [303]=4, [304]=5, [305]=4, [306]=10, [307]=8, [308]=6, [309]=5, [310]=9, [311]=8, [312]=8, [313]=7, [314]=4, [315]=7, [316]=4, [317]=10, [318]=7, [319]=7, [320]=8, [321]=7, [322]=5, [323]=9, [324]=8, [325]=9, [326]=8, [327]=9, [328]=8, [329]=11, [330]=9, [331]=8, [332]=9, [333]=8, [334]=9, [335]=8, [336]=9, [337]=8, [338]=11, [339]=11, [340]=8, [341]=7, [342]=8, [343]=7, [344]=8, [345]=7, [346]=8, [347]=7, [348]=8, [349]=7, [350]=8, [351]=7, [352]=8, [353]=7, [354]=8, [355]=6, [356]=8, [357]=10, [358]=8, [359]=6, [360]=8, [361]=8, [362]=8, [363]=8, [364]=8, [365]=8, [366]=8, [367]=8, [368]=8, [369]=8, [370]=8, [371]=8, [372]=11, [373]=11, [374]=10, [375]=8, [376]=10, [377]=9, [378]=8, [379]=9, [380]=8, [381]=9, [382]=8, [383]=6, [884]=5, [885]=5, [890]=4, [894]=4, [900]=5, [901]=8, [902]=9, [903]=5, [904]=7, [905]=8, [906]=5, [908]=9, [910]=10, [911]=9, [912]=6, [913]=9, [914]=8, [915]=7, [916]=10, [917]=7, [918]=9, [919]=8, [920]=9, [921]=5, [922]=9, [923]=9, [924]=11, [925]=9, [926]=8, [927]=9, [928]=8, [929]=8, [931]=8, [932]=8, [933]=10, [934]=10, [935]=9, [936]=12, [937]=9, [938]=6, [939]=10, [940]=9, [941]=8, [942]=8, [943]=5, [944]=8, [945]=9, [946]=8, [947]=8, [948]=8, [949]=8, [950]=8, [951]=8, [952]=8, [953]=5, [954]=7, [955]=8, [956]=9, [957]=8, [958]=8, [959]=8, [960]=9, [961]=8, [962]=8, [963]=9, [964]=9, [965]=8, [966]=10, [967]=8, [968]=12, [969]=10, [970]=6, [971]=8, [972]=8, [973]=8, [974]=10, [976]=8, [977]=10, [978]=11, [979]=11, [980]=11, [981]=10, [982]=10, [983]=9, [984]=9, [985]=8, [986]=9, [987]=8, [988]=7, [989]=7, [990]=9, [991]=7, [992]=11, [993]=8, [994]=10, [995]=10, [996]=9, [997]=9, [998]=9, [999]=10, [1000]=8, [1001]=7, [1002]=9, [1003]=8, [1004]=8, [1005]=8, [1006]=10, [1007]=8, [1008]=9, [1009]=8, [1010]=8, [1011]=5, [1012]=9, [1013]=9, [1014]=9, [1015]=8, [1016]=8, [1017]=8, [1018]=11, [1019]=11, [1020]=8, [1021]=8, [1022]=8, [1023]=8, [1024]=7, [1025]=7, [1026]=10, [1027]=7, [1028]=9, [1029]=7, [1030]=5, [1031]=6, [1032]=6, [1033]=14, [1034]=13, [1035]=10, [1036]=9, [1037]=9, [1038]=10, [1039]=8, [1040]=9, [1041]=8, [1042]=8, [1043]=7, [1044]=10, [1045]=7, [1046]=12, [1047]=8, [1048]=9, [1049]=9, [1050]=9, [1051]=10, [1052]=11, [1053]=8, [1054]=9, [1055]=8, [1056]=8, [1057]=8, [1058]=8, [1059]=10, [1060]=10, [1061]=9, [1062]=8, [1063]=8, [1064]=12, [1065]=12, [1066]=11, [1067]=11, [1068]=8, [1069]=9, [1070]=12, [1071]=8, [1072]=8, [1073]=8, [1074]=8, [1075]=6, [1076]=10, [1077]=8, [1078]=12, [1079]=8, [1080]=9, [1081]=9, [1082]=7, [1083]=10, [1084]=11, [1085]=8, [1086]=8, [1087]=9, [1088]=8, [1089]=8, [1090]=8, [1091]=8, [1092]=12, [1093]=9, [1094]=8, [1095]=8, [1096]=10, [1097]=10, [1098]=10, [1099]=10, [1100]=7, [1101]=9, [1102]=10, [1103]=8, [1104]=8, [1105]=8, [1106]=8, [1107]=6, [1108]=9, [1109]=7, [1110]=4, [1111]=5, [1112]=5, [1113]=12, [1114]=11, [1115]=8, [1116]=7, [1117]=9, [1118]=8, [1119]=8, [1120]=12, [1121]=10, [1122]=8, [1123]=7, [1124]=11, [1125]=11, [1126]=10, [1127]=9, [1128]=13, [1129]=12, [1130]=10, [1131]=10, [1132]=13, [1133]=13, [1134]=8, [1135]=8, [1136]=12, [1137]=12, [1138]=9, [1139]=7, [1140]=11, [1141]=10, [1142]=11, [1143]=10, [1144]=18, [1145]=17, [1146]=11, [1147]=9, [1148]=12, [1149]=10, [1150]=12, [1151]=10, [1152]=8, [1153]=7, [1154]=7, [1155]=1, [1160]=14, [1161]=14, [1162]=9, [1163]=9, [1164]=8, [1165]=7, [1166]=8, [1167]=8, [1168]=7, [1169]=7, [1170]=7, [1171]=6, [1172]=8, [1173]=8, [1174]=12, [1175]=12, [1176]=8, [1177]=8, [1178]=9, [1179]=8, [1180]=9, [1181]=7, [1182]=9, [1183]=7, [1184]=12, [1185]=10, [1186]=8, [1187]=8, [1188]=11, [1189]=11, [1190]=12, [1191]=12, [1192]=11, [1193]=10, [1194]=8, [1195]=8, [1196]=8, [1197]=8, [1198]=10, [1199]=10, [1200]=10, [1201]=10, [1202]=9, [1203]=9, [1204]=11, [1205]=11, [1206]=8, [1207]=8, [1208]=9, [1209]=9, [1210]=8, [1211]=8, [1212]=11, [1213]=11, [1214]=11, [1215]=11, [1216]=5, [1217]=12, [1218]=12, [1219]=8, [1220]=8, [1221]=10, [1222]=10, [1223]=8, [1224]=8, [1225]=8, [1226]=8, [1227]=8, [1228]=8, [1229]=11, [1230]=11, [1232]=9, [1233]=8, [1234]=9, [1235]=8, [1236]=11, [1237]=12, [1238]=7, [1239]=8, [1240]=8, [1241]=8, [1242]=8, [1243]=8, [1244]=12, [1245]=12, [1246]=8, [1247]=8, [1248]=8, [1249]=8, [1250]=9, [1251]=9, [1252]=9, [1253]=9, [1254]=9, [1255]=8, [1256]=9, [1257]=8, [1258]=9, [1259]=8, [1260]=9, [1261]=9, [1262]=10, [1263]=8, [1264]=10, [1265]=8, [1266]=10, [1267]=8, [1268]=8, [1269]=8, [1270]=7, [1271]=6, [1272]=11, [1273]=10, [8208]=5, [8209]=5, [8210]=8, [8211]=7, [8212]=10, [8213]=12, [8216]=4, [8217]=4, [8218]=4, [8219]=4, [8220]=7, [8221]=7, [8222]=7, [8223]=7, [8364]=8 }, + } ); |