Editing Module:ItemBox

Warning: You are not logged in. Once you make an edit, a temporary account will be created for you. Learn more. Log in or create an account to continue receiving notifications after this account expires, and to access other features.
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
Latest revision Your text
Line 1: Line 1:
local p = {}
local p = {}
local ustring = mw.ustring
local ustring = mw.ustring
local textfit = require "Module:TextFit"


-- ============================================================================
-- ============================================================================
--  Name box geometry, in em of .itembox's font-size (1em = 10px at size=1).
--  Retail Demo (bold) glyph advance widths, in em of the font size.
--  Mirrors Template:ItemBox/styles.css:
--  unitsPerEm = 1000. Measured from File:Retaildemo-bold.woff2
--   .itembox           width: 8em
-- ( /images/d/d5/Retaildemo-bold.woff2 ), the bold face used by .itembox-name.
--   .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)
-- THIS TABLE IS FONT-SPECIFIC. If that file is re-uploaded with different
-- so the name's content box is 8 - 2*0.4 = 7.2em wide and 4.5 - 2*0.5 = 3.5em
--  metrics, name fitting drifts. Regenerate the table with:
-- 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
--    curl -o rd.woff2 https://deadlock.wiki/images/d/d5/Retaildemo-bold.woff2
-- the actual width. Glyph widths live in [[Module:TextFit]].
--    fontforge -lang=py -c 'import fontforge;f=fontforge.open("rd.woff2");f.generate("rd.ttf")'
--   python3 - << 'PY'
--   from fontTools.ttLib import TTFont; import string
--    f=TTFont("rd.ttf"); upm=f["head"].unitsPerEm; hm=f["hmtx"]; uni={}
--   for t in f["cmap"].tables:
--        if t.isUnicode():
--           for cp,gn in t.cmap.items(): uni.setdefault(cp,gn)
--   for c in string.ascii_uppercase+string.ascii_lowercase+string.digits+" ":
--       gn=uni.get(ord(c))
--       if gn: print(f'    ["{c}"]={hm[gn][0]/upm:.3f},')
--    PY
-- ============================================================================
-- ============================================================================
local NAME_W = 7.2
local WIDTH = {
local NAME_H = 3.5
    A=0.677, B=0.590, C=0.621, D=0.693, E=0.552, F=0.530, G=0.686, H=0.702,
local LINE_H = 1.2
    I=0.276, J=0.301, K=0.637, L=0.491, M=0.904, N=0.695, O=0.741, P=0.585,
local MAX_EM = 1.5
    Q=0.740, R=0.628, S=0.555, T=0.555, U=0.694, V=0.668, W=0.913, X=0.667,
local MIN_EM = 0.8
    Y=0.635, Z=0.666,
    a=0.573, b=0.608, c=0.473, d=0.604, e=0.570, f=0.382, g=0.558, h=0.608,
    i=0.269, j=0.266, k=0.584, l=0.268, m=0.907, n=0.602, o=0.611, p=0.604,
    q=0.605, r=0.392, s=0.496, t=0.404, u=0.602, v=0.549, w=0.776, x=0.571,
    y=0.550, z=0.561,
    ["0"]=0.662, ["1"]=0.312, ["2"]=0.560, ["3"]=0.566, ["4"]=0.612,
    ["5"]=0.562, ["6"]=0.601, ["7"]=0.520, ["8"]=0.583, ["9"]=0.598,
    [" "]=0.240,
}


-- Returns the name font size as a cqw string, measured against
-- Characters absent from Retail Demo (hyphen, apostrophe, ...) are drawn by the
-- .itembox-name-area, which [[Template:ItemBox]] makes a query container.
-- browser in the fallback sans-serif; this is a reasonable stand-in width.
local FALLBACK = 0.60
 
-- Card geometry in px at the reference scale where 1em = 10px. The card is
-- scaled through font-size on .itembox (the |size= parameter) and the value
-- returned here is em, so it scales together with everything else.
local USABLE_W = 72    -- 80px card minus 4px padding on each side
local NAME_H  = 42    -- vertical room the wrapped name may occupy
local LINE_H  = 1.2  -- must match .itembox-name line-height
local MAX_PX  = 15    -- full size  (1.50em)
local MIN_PX  = 8    -- hard floor (0.80em)
 
local function charWidth(ch)
    local w = WIDTH[ch]
    if w then return w end
    local cp = ustring.codepoint(ch) or 0
    -- CJK / full-width ranges render roughly twice as wide as Latin
    if (cp >= 0x1100 and cp <= 0x115F) or (cp >= 0x2E80 and cp <= 0xA4CF)
        or (cp >= 0xAC00 and cp <= 0xD7A3) or (cp >= 0xF900 and cp <= 0xFAFF)
        or (cp >= 0xFF00 and cp <= 0xFF60) or (cp >= 0xFFE0 and cp <= 0xFFE6) then
        return 2.0
    end
    return FALLBACK
end
 
local function wordWidth(word)
    local total = 0
    for ch in ustring.gmatch(word, ".") do
        total = total + charWidth(ch)
    end
    return total
end
 
-- Greedy line count when filling USABLE_W at font size `px`. Close enough to
-- text-wrap:balance for sizing, since both keep every word intact.
local function lineCount(words, px)
    local space = charWidth(" ") * px
    local cur, lines = 0, 1
    for _, w in ipairs(words) do
        local ww = wordWidth(w) * px
        if cur == 0 then
            cur = ww
        elseif cur + space + ww <= USABLE_W then
            cur = cur + space + ww
        else
            lines = lines + 1
            cur = ww
        end
    end
    return lines
end
 
-- Returns the name font size as an em string (scales with the card).
function p.getFontSize(frame)
function p.getFontSize(frame)
     local name = ustring.gsub(frame.args[1] or "", "^%s*(.-)%s*$", "%1")
     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("%.2f", MAX_PX / 10) end
     if name == "" then return string.format("%.2fcqw", max) end
 
    local words = {}
    for w in ustring.gmatch(name, "%S+") do words[#words + 1] = w end
 
    local widest = 0
    for _, w in ipairs(words) do
        local ww = wordWidth(w)
        if ww > widest then widest = ww end
    end
 
    local px = USABLE_W / widest
    if px > MAX_PX then px = MAX_PX end
 
    while px > MIN_PX and lineCount(words, px) * LINE_H * px > NAME_H do
        px = px - 0.25
    end
    if px < MIN_PX then px = MIN_PX end
 
    local snapped = math.floor(px * 2 + 0.5) / 2
    if lineCount(words, snapped) * LINE_H * snapped > NAME_H then
        snapped = math.floor(px * 2) / 2
    end


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


-- User for stable per-item texture variety
-- Stable per-item texture variety, replacing position-dependent :nth-child.
-- A djb2 hash of the (language-independent) code name picks one of each of the
-- icon-mask / wear / paper-texture variants. Same item -> same look everywhere.
local function hash(s)
local function hash(s)
     local h = 5381
     local h = 5381
Please note that all contributions to The Deadlock Wiki are considered to be released under the Creative Commons Attribution-NonCommercial-ShareAlike (see Deadlock:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!
Cancel Editing help (opens in new window)
Preview page with this template

Page included on this page: