Editing Module:ItemBoxGive feedback
Jump to navigation
Jump to search
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 | ||
-- ============================================================================ | -- ============================================================================ | ||
-- | -- Retail Demo (bold) glyph advance widths, in em of the font size. | ||
-- unitsPerEm = 1000. Measured from /images/d/d5/Retaildemo-bold.woff2 | |||
-- Table can be regenerated with fontforge / fonttools | |||
-- | |||
- | |||
-- | |||
-- ============================================================================ | -- ============================================================================ | ||
local | local WIDTH = { | ||
A=0.677, B=0.590, C=0.621, D=0.693, E=0.552, F=0.530, G=0.686, H=0.702, | |||
I=0.276, J=0.301, K=0.637, L=0.491, M=0.904, N=0.695, O=0.741, P=0.585, | |||
Q=0.740, R=0.628, S=0.555, T=0.555, U=0.694, V=0.668, W=0.913, X=0.667, | |||
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, | |||
} | |||
-- | -- Characters absent from Retail Demo (hyphen, apostrophe, ...) are drawn by the | ||
-- .itembox-name- | -- 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") | ||
if name == "" then return string.format("%.2f", MAX_PX / 10) end | |||
if name == "" then return string.format("%. | |||
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("%. | return string.format("%.2f", snapped / 10) | ||
end | end | ||