mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
add ttf tiles option
lets a translation keep chosen characters on the rom tiles instead of the ttf. needed for japanese: sizing the font for kana makes latin narrower, which knocks the party menu numbers out of line.
This commit is contained in:
+11
-2
@@ -386,8 +386,17 @@ Vautour (Burpy Fresh), licensed under CC-BY 4.0 (5x11 base characters,
|
||||
the registry entry: `file` for a mod-shipped TTF, `size` (the font's
|
||||
design em; Plain Pixel rasterizes cleanly only at multiples of 15),
|
||||
`spacing` added to every advance, `yOffset` for vertical alignment
|
||||
against the 8px cell grid, and `bold`, which double-prints at a 1px
|
||||
offset for fonts whose strokes read too light.
|
||||
against the 8px cell grid, `bold`, which double-prints at a 1px
|
||||
offset for fonts whose strokes read too light, and `tiles`, the
|
||||
characters that keep their ROM tile instead of coming from the TTF.
|
||||
|
||||
`tiles` matters for a CJK translation. Sizing the font so a kana fills
|
||||
the 8px cell leaves Latin narrower than the tile font it replaces, which
|
||||
pulls the numeric columns out of line: the party menu's `:L12` stops
|
||||
sitting over `34/ 34`. Naming `"0123456789/:"` keeps those on the
|
||||
vanilla tiles, so numbers render exactly as they do in English while
|
||||
kana still come from the font. It takes a string of characters, or a
|
||||
list when a multi-character charmap sequence is meant.
|
||||
|
||||
See the wiki's Translations guide.
|
||||
|
||||
|
||||
@@ -1079,7 +1079,11 @@ R.font = {
|
||||
f.rec{ seq = f.str, code = f.int(0) },
|
||||
f.rec{ file = f.opt(f.path), size = f.opt(f.int(1)),
|
||||
spacing = f.opt(f.num), yOffset = f.opt(f.num),
|
||||
bold = f.opt(f.bool) },
|
||||
bold = f.opt(f.bool),
|
||||
-- characters that keep their ROM tile instead of coming from the
|
||||
-- TTF: a string of them, or a list when a multi-character charmap
|
||||
-- sequence is meant (src/render/Font.lua)
|
||||
tiles = f.opt(f.union{ f.str, f.list(f.str) }) },
|
||||
},
|
||||
extra = function(id, value)
|
||||
if fontIsCharmap(id) then
|
||||
@@ -1092,7 +1096,7 @@ R.font = {
|
||||
elseif fontIsTtf(id) then
|
||||
-- every field optional: {} is "the bundled font at its native size"
|
||||
if value.image ~= nil or value.base ~= nil then
|
||||
return 'the "ttf" entry takes file/size/spacing/yOffset/bold, not a page'
|
||||
return 'the "ttf" entry takes file/size/spacing/yOffset/bold/tiles, not a page'
|
||||
end
|
||||
elseif value.image == nil or value.base == nil then
|
||||
return "a font page needs an image and a base"
|
||||
|
||||
+40
-2
@@ -55,6 +55,34 @@ local function pagesOf(def)
|
||||
return pages
|
||||
end
|
||||
|
||||
-- ttf.tiles as a lookup keyed by charmap sequence. Accepts a plain string
|
||||
-- ("0123456789"), which is split into UTF-8 characters, or a list of
|
||||
-- sequences ({ "0", "1", "<PK>" }) when a multi-character macro is meant.
|
||||
local function tileSet(spec)
|
||||
local set = {}
|
||||
if type(spec) == "table" then
|
||||
for _, seq in ipairs(spec) do set[tostring(seq)] = true end
|
||||
elseif type(spec) == "string" then
|
||||
-- split on UTF-8 lead bytes rather than utf8Decode, which this file
|
||||
-- declares further down and would be nil here
|
||||
local i, n = 1, #spec
|
||||
while i <= n do
|
||||
local last = i
|
||||
if spec:byte(i) >= 0xC0 then
|
||||
local k = i + 1
|
||||
while k <= n do
|
||||
local b = spec:byte(k)
|
||||
if b < 0x80 or b > 0xBF then break end
|
||||
last, k = k, k + 1
|
||||
end
|
||||
end
|
||||
set[spec:sub(i, last)] = true
|
||||
i = last + 1
|
||||
end
|
||||
end
|
||||
return set
|
||||
end
|
||||
|
||||
function Font.load(data)
|
||||
loadedFrom = data
|
||||
local def = data.font
|
||||
@@ -128,6 +156,15 @@ function Font.load(data)
|
||||
-- caps line up and descenders hang below as the GB font's own do
|
||||
yOffset = def.ttf.yOffset or (obj.getBaseline
|
||||
and (GLYPH - 1 - obj:getBaseline()) or (GLYPH - obj:getHeight())),
|
||||
-- Single characters that keep their ROM tile instead of coming from
|
||||
-- the TTF. A CJK translation sizes the font so a kana fills the 8px
|
||||
-- cell, which leaves Latin narrower than the tile font it replaces:
|
||||
-- the numbers in a right-aligned column (the party menu's ":L12" over
|
||||
-- "34/ 34") then no longer land where the 8px-per-character layout put
|
||||
-- them. Naming "0123456789" here keeps digits on the vanilla tiles --
|
||||
-- identical to the English build -- while kana still come from the
|
||||
-- font. Sequence keys, so "é" or a "<PK>" macro can be listed too.
|
||||
tiles = tileSet(def.ttf.tiles),
|
||||
widths = {}, chars = {},
|
||||
}
|
||||
else
|
||||
@@ -227,9 +264,10 @@ function Font.split(text)
|
||||
for _, entry in ipairs(candidates) do
|
||||
local len = #entry.seq
|
||||
if text:sub(i, i + len - 1) == entry.seq then
|
||||
if ttf then
|
||||
if ttf and not ttf.tiles[entry.seq] then
|
||||
-- single characters belong to the TTF; only multi-character
|
||||
-- sequences (ligatures, <PK> macros) keep their tile mapping
|
||||
-- sequences (ligatures, <PK> macros) keep their tile mapping,
|
||||
-- plus anything the mod named in ttf.tiles (see Font.load)
|
||||
local cp, last = utf8Decode(entry.seq, 1)
|
||||
if cp and last == len then break end
|
||||
end
|
||||
|
||||
@@ -53,6 +53,28 @@ T.eq(#Font.encode("'d"), 1, "and still consumes the whole sequence")
|
||||
T.eq(Font.encode("\227\129\130")[1], BASE + 0x3042,
|
||||
"kana with no charmap entry at all still gets a glyph")
|
||||
|
||||
-- ------------------------------------------------- ttf.tiles opts back out
|
||||
|
||||
-- A CJK translation sizes the font so a kana fills the 8px cell, which leaves
|
||||
-- Latin narrower than the tile font it replaces and pulls the numeric columns
|
||||
-- (the party menu's ":L12" over "34/ 34") out of line. ttf.tiles names the
|
||||
-- characters that keep their ROM tile anyway, so numbers stay identical to the
|
||||
-- English build while kana still come from the font.
|
||||
Font.load({ font = { charmap = CHARMAP, ttf = { tiles = "A" } } })
|
||||
T.eq(Font.encode("A")[1], 0x80, "a character in ttf.tiles keeps its tile glyph")
|
||||
T.eq(Font.advanceOf(0x80), 8, "and its 8px monospace advance with it")
|
||||
T.eq(Font.encode("\195\169")[1], BASE + 0xE9,
|
||||
"while everything else still routes to the TTF")
|
||||
|
||||
-- a list form, for naming a multi-character sequence explicitly
|
||||
Font.load({ font = { charmap = CHARMAP, ttf = { tiles = { "\195\169" } } } })
|
||||
T.eq(Font.encode("\195\169")[1], 0xBA,
|
||||
"the list form accepts a multi-byte character")
|
||||
T.eq(Font.encode("A")[1], BASE + 65, "and leaves the others on the TTF")
|
||||
|
||||
Font.load({ font = { charmap = CHARMAP, ttf = {} } })
|
||||
T.eq(Font.encode("A")[1], BASE + 65, "no tiles list means the TTF takes it back")
|
||||
|
||||
-- metrics come straight from the font object (the stub: half the point
|
||||
-- size per codepoint, doubled from U+1000 up, mimicking Plain Pixel's
|
||||
-- single/double width split)
|
||||
@@ -161,6 +183,13 @@ do
|
||||
T.check(select(1, Schemas.check(spec, "font", "ttf",
|
||||
{ file = "mods/x/f.ttf", size = 11, spacing = 1, yOffset = -3 })) == true,
|
||||
"so is a fully tuned entry")
|
||||
T.check(select(1, Schemas.check(spec, "font", "ttf",
|
||||
{ size = 10, tiles = "0123456789/:" })) == true,
|
||||
"tiles takes a string of characters")
|
||||
T.check(select(1, Schemas.check(spec, "font", "ttf",
|
||||
{ tiles = { "0", "<PK>" } })) == true, "or a list of charmap sequences")
|
||||
T.check(Schemas.check(spec, "font", "ttf", { tiles = 10 }) == nil,
|
||||
"but not a number")
|
||||
T.check(Schemas.check(spec, "font", "ttf", { image = "x.png", base = 0x100 })
|
||||
== nil, "a page payload under the ttf id is refused")
|
||||
T.check(Schemas.check(spec, "font", "page", {}) == nil,
|
||||
|
||||
Reference in New Issue
Block a user