Module:ItemBox: Difference between revisions

From The Deadlock Wiki
Jump to navigation Jump to search
(it's vergir) better sizing for non-latin fonts
Vergir (talk | contribs)
Emit the name size in cqw against the name area, dropping the em conversion and grid snapping (with help from vergir-bot LLM)
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
local p = {}
local p = {}
local ustring = mw.ustring
local textfit = require "Module:TextFit"


local function charWidth(cp)
-- ============================================================================
    -- Fullwidth & Wide ranges (CJK, Hangul, etc.) → 2 units
-- Name box geometry, in em of .itembox's font-size (1em = 10px at size=1).
    if
-- Mirrors Template:ItemBox/styles.css:
        (cp >= 0x1100  and cp <= 0x115For  -- Hangul Jamo
--   .itembox            width: 8em
        (cp >= 0x2E80  and cp <= 0x303E) or  -- CJK radicals, Kangxi
--    .itembox-name-area  height: 4.5em; padding: 0.5em 0.4em (border-box)
        (cp >= 0x3041  and cp <= 0x33BFor  -- Hiragana, Katakana, CJK compat
--    .itembox-name      line-height: 1.2; font-size: 1.5em (overridden inline)
        (cp >= 0x33FF  and cp <= 0xA4CF)  or  -- CJK unified
-- so the name's content box is 8 - 2*0.4 = 7.2em wide and 4.5 - 2*0.5 = 3.5em
        (cp >= 0xA960 and cp <= 0xA97F)  or -- Hangul Jamo Extended
--  tall. Only the ratio and the size limits are read from those figures: the
        (cp >= 0xAC00  and cp <= 0xD7FF)  or -- Hangul syllables
-- size is emitted in cqw against .itembox-name-area, so the browser supplies
        (cp >= 0xF900  and cp <= 0xFAFF)  or  -- CJK compatibility ideographs
-- the actual width. Glyph widths live in [[Module:TextFit]].
        (cp >= 0xFE10  and cp <= 0xFE1F)  or  -- Vertical forms
-- ============================================================================
        (cp >= 0xFE30  and cp <= 0xFE6F)  or  -- CJK compatibility forms
local NAME_W = 7.2
        (cp >= 0xFF01  and cp <= 0xFF60)  or  -- Fullwidth Latin/punctuation
local NAME_H = 3.5
        (cp >= 0xFFE0  and cp <= 0xFFE6)  or  -- Fullwidth signs
local LINE_H = 1.2
        (cp >= 0x1B000 and cp <= 0x1B0FF) or  -- Kana supplement
local MAX_EM = 1.5
        (cp >= 0x1F300 and cp <= 0x1F9FF) or  -- Emoji block
local MIN_EM = 0.8
        (cp >= 0x20000 and cp <= 0x2FFFD) or  -- CJK Extension B–F
        (cp >= 0x30000 and cp <= 0x3FFFD)    -- CJK Extension G+
    then
        return 2


    -- Narrow Latin specifics
-- Returns the name font size as a cqw string, measured against
    elseif cp == 0x69 or cp == 0x6C or  -- i, l
-- .itembox-name-area, which [[Template:ItemBox]] makes a query container.
          cp == 0x49 or                -- I
function p.getFontSize(frame)
          cp == 0x31 or               -- 1
    local name = ustring.gsub(frame.args[1] or "", "^%s*(.-)%s*$", "%1")
          cp == 0x21 or cp == 0x7C    -- !, |
    local max = MAX_EM / NAME_W * 100          -- 20.83cqw
     then
     if name == "" then return string.format("%.2fcqw", max) end
        return 0.5


     -- Wide Latin
     return string.format("%.2fcqw", textfit.fit(name, {
     elseif cp == 0x6D or cp == 0x77 or  -- m, w
        aspect     = NAME_H / NAME_W,
          cp == 0x4D or cp == 0x57   -- M, W
        lineHeight = LINE_H,
     then
        max        = max,
        return 1.5
        min        = MIN_EM / NAME_W * 100,   -- 11.11cqw
     }))
end


    -- Cyrillic and other "ambiguous" scripts — slightly wider than Latin
-- User for stable per-item texture variety
    elseif cp >= 0x0400 and cp <= 0x04FF then
local function hash(s)
        -- Wide Cyrillic (1.5)
    local h = 5381
            local wide = {
    for i = 1, #s do
                [0x0416]=true, [0x0436]=true, -- Ж ж
        h = (h * 33 + s:byte(i)) % 2147483647
                [0x041C]=true, [0x043C]=true, -- М м
                [0x0424]=true, [0x0444]=true, -- Ф ф
                [0x0428]=true, [0x0448]=true, -- Ш ш
                [0x0429]=true, [0x0449]=true, -- Щ щ
                [0x042E]=true, [0x044E]=true, -- Ю ю
                [0x042B]=true, [0x044B]=true, -- Ы ы
            }
            -- Narrow Cyrillic (0.5)
            local narrow = {
                [0x0406]=true, [0x0456]=true, -- І і (Ukrainian I)
                [0x0407]=true, [0x0457]=true, -- Ї ї
            }
 
            if wide[cp] then
                return 1.5
            elseif narrow[cp] then
                return 0.5
            else
                return 1.1
            end
 
    -- Default for everything else (Arabic, Hebrew, Thai, etc.)
    else
        return 1
     end
     end
    return h
end
end


local function wordWidth(word)
function p.variants(frame)
    local width = 0
     local h = hash(frame.args[1] or "")
    for i = 1, mw.ustring.len(word) do
     local icon = (h % 3) + 1
    local cp = mw.ustring.codepoint(word, i, i)
     local wear = (math.floor(h / 3) % 4) + 1
    width = width + charWidth(cp)
     local tex = (math.floor(h / 12) % 3) + 1
end
    return string.format("itembox-icon-v%d itembox-wear-v%d itembox-texture-v%d",
    return math.ceil(width)
        icon, wear, tex)
end
 
function p.getFontSize(frame)
     local name = frame.args[1] or ""
     local words = {}
    for word in name:gmatch("%S+") do
        table.insert(words, word)
    end
   
     local longestLine = 0
 
    if #words == 1 then
        longestLine = wordWidth(words[1])
 
    elseif #words == 2 then
        longestLine = math.max(wordWidth(words[1]), wordWidth(words[2]))
 
     elseif #words == 3 then
        local first = wordWidth(words[1])
        local mid  = wordWidth(words[2])
        local last = wordWidth(words[3])
 
        local shorterOuter = math.min(first, last)
        local longerOuter  = math.max(first, last)
 
        local combined = mid + 1 + shorterOuter
 
        longestLine = math.max(combined, longerOuter)
    else
    -- 4+ words: take the max of all individual word widths
    for _, word in ipairs(words) do
        longestLine = math.max(longestLine, wordWidth(word))
    end
    end
 
local fullSize = 15
    local fontSize
 
    if longestLine <= 8 then
        fontSize = fullSize
    else
        fontSize = fullSize - (longestLine - 8) * 1
    end
   
    fontSize = math.max(6, fontSize)
 
    return fontSize
end
end


return p
return p

Latest revision as of 19:14, 1 August 2026

Documentation for this module may be created at Module:ItemBox/doc

local p = {}
local ustring = mw.ustring
local textfit = require "Module:TextFit"

-- ============================================================================
--  Name box geometry, in em of .itembox's font-size (1em = 10px at size=1).
--  Mirrors Template:ItemBox/styles.css:
--    .itembox            width: 8em
--    .itembox-name-area  height: 4.5em; padding: 0.5em 0.4em (border-box)
--    .itembox-name       line-height: 1.2; font-size: 1.5em (overridden inline)
--  so the name's content box is 8 - 2*0.4 = 7.2em wide and 4.5 - 2*0.5 = 3.5em
--  tall. Only the ratio and the size limits are read from those figures: the
--  size is emitted in cqw against .itembox-name-area, so the browser supplies
--  the actual width. Glyph widths live in [[Module:TextFit]].
-- ============================================================================
local NAME_W = 7.2
local NAME_H = 3.5
local LINE_H = 1.2
local MAX_EM = 1.5
local MIN_EM = 0.8

-- Returns the name font size as a cqw string, measured against
-- .itembox-name-area, which [[Template:ItemBox]] makes a query container.
function p.getFontSize(frame)
    local name = ustring.gsub(frame.args[1] or "", "^%s*(.-)%s*$", "%1")
    local max = MAX_EM / NAME_W * 100          -- 20.83cqw
    if name == "" then return string.format("%.2fcqw", max) end

    return string.format("%.2fcqw", textfit.fit(name, {
        aspect     = NAME_H / NAME_W,
        lineHeight = LINE_H,
        max        = max,
        min        = MIN_EM / NAME_W * 100,    -- 11.11cqw
    }))
end

-- User for stable per-item texture variety
local function hash(s)
    local h = 5381
    for i = 1, #s do
        h = (h * 33 + s:byte(i)) % 2147483647
    end
    return h
end

function p.variants(frame)
    local h = hash(frame.args[1] or "")
    local icon = (h % 3) + 1
    local wear = (math.floor(h / 3) % 4) + 1
    local tex  = (math.floor(h / 12) % 3) + 1
    return string.format("itembox-icon-v%d itembox-wear-v%d itembox-texture-v%d",
        icon, wear, tex)
end

return p