aboutsummaryrefslogtreecommitdiff
path: root/advtrains/unifont.lua
blob: 18d0ed554c03f6a8e1ce9c49db5c62753a8db87f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
--[[
Note on copyright:
The Unifont website (http://unifoundry.com/unifont/index.html) says that
the font files are licensed "under the GNU General Public License,
either version 2 or (at your option) a later version". Section 13 of the
GNU GPLv3 gives the "permission to link or combine any covered work with
a work licensed under version 3 of the GNU Affero General Public License
into a single combined work". The use of Unifont here should fall under
this case considering that
- Unifont is licensed under GNU GPLv2+, and, in this case, the terms of
the GNU GPLv3 is used here
- We are combining Unifont into advtrains, and the latter is licensed
under the GNU AGPLv3.
However, as I am not a lawyer, I can not be sure whether this
interpretation is legally accepted.
- Y.W.
]]

local tonumber, unpack = tonumber, unpack
local sbyte, schar, sformat, smatch, ssub = string.byte, string.char, string.format, string.match, string.sub
local tconcat = table.concat

local texture_dir = tconcat({advtrains.modpath, "textures", "unifont"}, DIR_DELIM)
minetest.mkdir(texture_dir)
for _, i in pairs(minetest.get_dir_list(texture_dir)) do
	-- FIXME: remove this workaround eventually when MT 5.5.0 becomes common
	os.remove(texture_dir .. DIR_DELIM .. i)
end

local function texture_file(cp)
	return sformat(cp < 65536 and "%s_%04x.bmp" or "%s_%06x.bmp", "advtrains_unifont", cp)
end
local function texture_path(cp)
	return texture_dir .. DIR_DELIM .. texture_file(cp)
end

local _cpwidth = {}
local cpdata = {}

-- Generate texture files
local bmp_headers = {}
for _, v in pairs {8, 16} do
	bmp_headers[v] = tconcat{
		"BM", -- starting bytes
		"\125\0\0\0", -- file size
		"\0\0\0\0", -- reserved fields
		"\62\0\0\0", -- offset of the pixel array
		"\40\0\0\0", -- BITMAPINFOHEADER
		schar(v, 0, 0, 0), -- image width
		"\16\0\0\0", -- image height
		"\1\0", -- number of color planes (must be 1, apparently)
		"\1\0", -- bits per pixel
		"\0\0\0\0", -- no compression
		"\64\0\0\0", -- size of the raw bitmap data
		"\0\0\0\0\0\0\0\0", -- image resolution (irrelevant here)
		"\2\0\0\0", -- number of colors in the palette
		"\0\0\0\0", -- "important" colors
		-- palette
		"\0\0\0\0",
		"\255\255\255\0",
	}
end

local f = io.open(advtrains.modpath .. DIR_DELIM .. "unifont.hex", "rb") or error("Cannot open unifont.hex")
for l in f:lines() do
	local cp, raw = smatch(l, "^(%x+):(%x+)$")
	cpdata[tonumber(cp, 16)] = raw
end
f:close()
f = nil

local mods_loaded = false
local function cpwidth(cp)
	if _cpwidth[cp] then
		return _cpwidth[cp]
	end
	if cpdata[cp] then
		local raw = cpdata[cp]
		local rowsize = #raw/16
		local width = rowsize*4
		_cpwidth[cp] = width
		local rowbytes = rowsize/2
		local bytes = {bmp_headers[width]}
		for i = 0, 15 do
			local row = {}
			local offset = i*rowsize
			for j = 1, rowbytes do
				local offset = offset+2*j-1
				local data = ssub(raw, offset, offset+1)
				row[j] = tonumber(data, 16)
			end
			for j = rowbytes+1, 4 do
				row[j] = 0
			end
			bytes[17-i] = schar(unpack(row))
		end
		local path = texture_path(cp)
		minetest.safe_file_write(path, tconcat(bytes))
		if mods_loaded then
			minetest.dynamic_add_media(path)
		end
		return width
	end
end
minetest.register_on_mods_loaded(function() mods_loaded = true end)

local function mbstocps(str)
	local t = {}
	local i = 1
	while i <= #str do
		local c = sbyte(str, i)
		local bt = {}
		i = i+1
		if c < 128 then
			-- nop
		elseif c < 192 then
			c = 0
		elseif c < 224 then
			bt = {sbyte(str, i, i)}
			c = c%32
		elseif c < 240 then
			bt = {sbyte(str, i, i+1)}
			c = c%16
		elseif c < 248 then
			bt = {sbyte(str, i, i+2)}
			c = c%8
		else
			c = 0
		end
		for i = 1, #bt do
			c = c*64+(bt[i]%64)
		end
		i = i + #bt
		t[#t+1] = c
	end
	return t
end

local function renderer(opts)
	local opts = opts or {}
	local x0, y0 = (opts.x or 0), (opts.y or 0)
	local width, height = opts.width, opts.height
	local minwidth, minheight = opts.minwidth, opts.minheight
	local halign, valign = (opts.halign or 0.5), (opts.valign or 0.5)
	local textcolor = opts.textcolor or "black"
	local bgcolor = opts.bgcolor
	local function break_lines(cps)
		local lastline = {width = 0}
		local lines = {lastline}
		local maxwidth = 0
		local i = 1
		while i <= #cps do
			local char = cps[i]
			if char == 10 then
				lastline = {width = 0}
				lines[#lines+1] = lastline
			elseif cpwidth(char) then
				local newwidth = lastline.width + cpwidth(char)
				lastline.width = newwidth
				maxwidth = math.max(newwidth, maxwidth)
				lastline[#lastline+1] = char
			end
			i = i+1
		end
		return lines, maxwidth, 16*#lines
	end
	return function(str)
		local lines, textwidth, textheight = break_lines(mbstocps(str))
		local width = math.max(minwidth or 0, width or 0, textwidth)
		local height = math.max(minheight or 0, height or 0, textheight)
		local y = y0 + (height-textheight)*valign
		local st = {
			"[combine",
			sformat("%dx%d", x0+width, y0+height)
		}
		for i = 1, #lines do
			local line = lines[i]
			local x = x0 + (width - line.width)*halign
			local spacing = 0
			if minwidth and line.width < minwidth then
				x = x0 + (width - minwidth)*halign
				spacing = (minwidth - line.width) / (#line - 1)
			end
			for j = 1, #line do
				local cp = line[j]
				st[#st+1] = sformat("%d,%d=%s", x, y, texture_file(cp))
				x = x + cpwidth(cp) + spacing
			end
			y = y + 16
		end
		local prefix = ""
		if bgcolor then
			prefix = sformat("[combine:%dx%d:%d,%d=\\(advtrains_hud_bg.png\\^[resize\\:%dx%d\\^[colorize\\:%s\\:alpha\\)^",
				x0+width, y0+height, x0, y0, width, height, bgcolor)
		end
		return sformat("(%s(%s^[makealpha:000000^[multiply:%s))", prefix, tconcat(st, ":"), textcolor), width, height
	end
end

local function render(str, opts)
	return renderer(opts)(str)
end

return {
	texture_dir = texture_dir,
	mbstocps = mbstocps,
	renderer = renderer,
	render = render
}