//
// Copyright (c) 2017, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 15 May 2017 Brian Frank Creation
//
**
** FontData stores metrics for unicode chars from 0x20 to 0xff for a
** predefined set of fonts. For each font style we store a list of
** unique widths (in points) for the font at 1000pt. Then we map the
** width for each char in the range using a single ASCII char as an
** index to the width table with the char '%' 37 + index (we use '%'
** because is avoids both double quote and dollar chars).
**
@Js @NoDoc
const class FontData
{
//////////////////////////////////////////////////////////////////////////
// Registry
//////////////////////////////////////////////////////////////////////////
** Lookup metrics for font by key
static FontData? find(Font f)
{
registry[toKey(f.style, f.weight, f.name)]
}
** Table keyed by "<style> <weight> <name>"
const static Str:FontData registry
static
{
acc := Str:FontData[:] { ordered = true }
try
{
// Auto-generated by fontmetrics.html [Mon May 15 2017 18:09:08 GMT-0400 (EDT)]
register(acc, make("normal 400 Helvetica",770,230,[0,191,222,260,278,334,355,365,370,389,400,469,500,537,549,556,576,584,611,667,722,737,778,834,889,944,1000,1015],"))+44=8&**.6)*))4444444444))6664@889987;9)184<9;8;98798>887)))04*44144)44''1'<4444*1)419111*(*6%%%%%%?%%%%%%%%%%%%%%%%%%%%%%%%%%)*4444(4*:-46%:*/3***52)**,4<<<7888888?98888))))99;;;;;6;9999887444444=14444))))4444444374444141"))
register(acc, make("normal 700 Helvetica",770,230,[0,238,280,333,365,370,389,400,474,500,549,556,576,584,611,667,722,737,778,834,889,944,975,1000],"'(-0095&((+2'(''0000000000((2223;55554375'0538574754354:443('(20(03030(33''0'93333+0(30700.+'+2%%%%%%<%%%%%%%%%%%%%%%%%%%%%%%%%%'(0000'0(6*02%6(,/(((10'(()08883555555<54444''''5577777275555443000000900000''''3333333/33333030"))
register(acc, make("italic 400 Helvetica",770,230,[0,191,222,260,278,334,355,365,370,389,400,469,500,537,549,556,576,584,611,667,722,737,778,834,889,944,1000,1015],"))+44=8&**.6)*))4444444444))6664@889987;9)184<9;8;98798>887)))04*44144)44''1'<4444*1)419111*(*6%%%%%%?%%%%%%%%%%%%%%%%%%%%%%%%%%)*4444(4*:-46%:*/3***52)**,4<<<7888888?98888))))99;;;;;6;9999887444444=14444))))4444444374444141"))
register(acc, make("italic 700 Helvetica",770,230,[0,238,280,333,365,370,389,400,474,500,549,556,576,584,611,667,722,737,778,834,889,944,975,1000],"'(-0095&((+2'(''0000000000((2223;55554375'0538574754354:443('(20(03030(33''0'93333+0(30700.+'+2%%%%%%<%%%%%%%%%%%%%%%%%%%%%%%%%%'(0000'0(6*02%6(,/(((10'(()08883555555<54444''''5577777275555443000000900000''''3333333/33333030"))
register(acc, make("normal 300 Roboto",967,211,[0,170,191,195,210,217,223,226,228,240,246,266,281,287,321,326,331,336,361,378,397,416,427,432,442,453,456,459,475,481,486,490,506,511,516,518,523,530,536,545,550,554,556,558,562,564,569,571,582,586,593,598,599,605,613,616,617,625,631,635,649,657,669,677,685,710,725,739,756,776,802,846,865,886,896,913,1000],"/,2UNh\\&34;R'2.9NNNNNNNNNN)(FNH?p^[abSQef0M_Jmfd\\d`WXb]o[YX.9.:<2KNGOH5OM,-D,nMQNP6E3MBiCAC5+5e%%%%%%q%%%%%%%%%%%%%%%%%%%%%%%%%%/+LTgG*Z<k=?L%k;8J771PA//7>>gij@^^^^^^paSSSS0000cfdddddIdbbbbYWVKKKKKKlGHHHH++++UMQQQQQSPMMMMAQA"))
register(acc, make("normal 400 Roboto",967,211,[0,174,196,211,240,243,247,248,257,263,265,272,276,309,313,320,327,338,342,348,367,374,412,418,431,447,451,458,472,473,484,489,496,508,516,523,525,533,534,538,547,551,553,554,562,566,568,571,576,581,586,593,597,601,616,623,627,631,636,651,652,656,670,681,687,713,732,751,778,786,844,876,887,898,935,1000],",-4[Qg\\&78=S'1.;QQQQQQQQQQ*(FNHAna\\`bSOdf0O]Llfe^e[XY`_m]ZZ/;/<?2MQHRJ8QN*)F*lOTQS6G5NChEBE6+6d%%%%%%p%%%%%%%%%%%%%%%%%%%%%%%%%%,+MVfI)[<j>AP%j@:K993RD.,9@AgiiBaaaaaao`SSSS0000cfeeeeeJe````ZXYMMMMMMkHJJJJ++++WOTTTTTTRNNNNBUB"))
register(acc, make("normal 500 Roboto",967,211,[0,169,220,238,249,251,258,265,268,274,282,324,328,335,352,354,370,380,396,418,427,442,446,451,457,485,487,491,495,503,508,516,522,523,533,537,541,549,555,557,561,564,566,568,571,574,582,591,602,607,610,613,624,631,633,639,647,653,666,668,681,690,702,710,727,734,743,771,783,792,844,870,875,880,895,940,1000],")-0WPf\\&34:L'1/7PPPPPPPPPP,(CME?o_Z^^OJad/KZImdb\\bYVV^]n[WU.8.9<0INFNH4PK+*E+lLQNP3D2LAgB?B2*2_%%%%%%q%%%%%%%%%%%%%%%%%%%%%%%%%%),MTcG)Y:i;>K%i>6H551T@/+5=>ehj?______p^OOOO////`dbbbbbGb^^^^WUXIIIIIIkFHHHH,,,,SLQQQQQQOLLLL?R?"))
register(acc, make("normal 700 Roboto",967,211,[0,162,244,252,253,262,268,274,278,282,292,301,321,331,332,338,353,358,365,374,388,422,437,446,453,457,467,490,500,502,505,509,517,521,534,537,542,548,551,560,563,565,570,572,575,596,608,616,619,631,638,648,650,656,658,665,673,681,690,692,707,718,738,761,784,808,844,866,876,895,940,1000],"(,1RQcZ&55=J'9/8QQQQQQQQQQ.*DPEAj]WZYMJ^a/LWIia_X_WTU[ZiWUS-:-;<2HMFMI6PL+*G+hLNMN7E4LCcDBD2)2X%%%%%%l%%%%%%%%%%%%%%%%%%%%%%%%%%(.QR`H(V?e<AK%eB9H883T@0+8>AbdfA]]]]]]kZMMMM////\\a_____G_[[[[USVHHHHHHgFIIII,,,,QLNNNNNONLLLLBOB"))
register(acc, make("normal 400 Times",750,250,[0,180,200,250,278,300,310,333,389,400,408,444,453,469,480,500,541,549,556,564,576,611,667,722,750,760,778,833,889,921,944,1000],"(,/44@?&,,48(,()4444444444))8880B<;;<:7<<,-<:A<<7<;7:<<C<<:,),24,04040,44))4)?4444,-)44<4403'35%%%%%%D%%%%%%%%%%%%%%%%%%%%%%%%%%(,4444'4,>)48%>,.6**,91(,*+4===0<<<<<<A;::::,,,,<<<<<<<8<<<<<<74000000;00000))))4444444644444444"))
register(acc, make("normal 700 Times",750,250,[0,220,250,278,300,333,389,394,400,444,500,520,540,549,556,570,576,581,611,667,722,750,778,833,930,944,1000],"'*3//?<(**/4'*'(//////////**444/=989987;;+/;8>9;7;93899?998*(*6/*/3.3.*/3(*3(<3/33.+*3/9//.,&,0%%%%%%?%%%%%%%%%%%%%%%%%%%%%%%%%%'*////&/*:)/4%:*-2))*51'*)*/:::/999999?98888++++99;;;;;4;9999973//////9.....((((/3/////2/3333/3/"))
register(acc, make("italic 400 Times",750,250,[0,214,250,276,278,300,310,333,389,400,422,444,500,523,541,549,556,576,611,667,675,722,750,760,778,833,889,920,1000],"',/11>=&,,19',')1111111111,,9991@778:77::,085>8:7:715:7>755-)-/1,11010)11))0):1111--)10800-.(.3%%%%%%A%%%%%%%%%%%%%%%%%%%%%%%%%%'-1111(1,<(19%<,.4**,62',*+1;;;1777777?87777,,,,:8:::::9:::::571111111800000))))1111111411111010"))
register(acc, make("italic 700 Times",750,250,[0,220,250,266,278,300,333,348,389,395,400,444,485,500,549,556,570,576,606,611,667,722,750,778,833,889,944,1000],"'-422=<)++25'+')2222222222++5552=999:99:<-298>::8:948:9>988+)+52+22020+24))2)<4222--)40920-,&,5%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%'-2222&2+;(27%;+/3**+62'+**2;;;2999999?99999----:::::::5:::::882222222:00001))).2422222324444020"))
register(acc, make("normal 400 Courier",754,247,[0,400,549,576,600,1000],")))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))%%%%%%*%%%%%%%%%%%%%%%%%%%%%%%%%%)))))))))))))%))&')))()))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))'))))))))"))
register(acc, make("normal 700 Courier",754,247,[0,400,549,576,600,1000],")))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))%%%%%%*%%%%%%%%%%%%%%%%%%%%%%%%%%)))))))))))))%))&')))()))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))'))))))))"))
register(acc, make("italic 400 Courier",754,247,[0,400,549,576,600,1000],")))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))%%%%%%*%%%%%%%%%%%%%%%%%%%%%%%%%%)))))))))))))%))&')))()))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))'))))))))"))
register(acc, make("italic 700 Courier",754,247,[0,400,549,576,600,1000],")))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))%%%%%%*%%%%%%%%%%%%%%%%%%%%%%%%%%)))))))))))))%))&')))()))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))'))))))))"))
register(acc, make("normal 400 Roboto-Mono",967,211,[0,601,615,1000],"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%(%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"))
}
catch (Err e) e.trace
registry = acc
}
** Used by static initializer
private static Void register(Str:FontData acc, FontData m)
{
acc[m.key] = m
}
** Map to registry key string
private static Str toKey(FontStyle style, FontWeight weight, Str name)
{
"$style $weight.num $name"
}
** Constructor used by auto-generated FontRegistry
private new make(Str key, Int ascent, Int descent, Int[] widths, Str charToWidth)
{
toks := key.split
this.style = FontStyle.decode(toks[0])
this.weight = FontWeight.decode(toks[1])
this.name = toks[2].replace("-", " ")
this.key = toKey(style, weight, name)
this.ascent = ascent
this.descent = descent
this.leading = descent
this.height = leading + ascent + descent
this.widths = widths
this.charToWidth = charToWidth
this.lastChar = charToWidth.size + 32
}
//////////////////////////////////////////////////////////////////////////
// Normalize
//////////////////////////////////////////////////////////////////////////
** Return best match for normalized font
static Font normalize(Font f)
{
// try to normalize by each name in prioritized list of names
FontData? m := null
for (i := 0; i<f.names.size; ++i)
{
m = toNormalize(f.style, f.weight, f.names[i])
if (m != null) break
}
// fallback
if (m == null) m = registry.getChecked("normal 400 Helvetica")
// map metrics back to Font instance with proper size
return Font.makeFields([m.name], f.size, m.weight, m.style)
}
private static FontData? toNormalize(FontStyle style, FontWeight weight, Str name)
{
// first find exact match
m := registry[toKey(style, weight, name)]
if (m != null) return m
// normalize weight
if (weight.num < 400) weight = FontWeight.normal
else if (weight.num > 400) weight = FontWeight.bold
m = registry[toKey(style, weight, name)]
if (m != null) return m
// next check oblique as italic
if (style == FontStyle.oblique)
{
m = registry[toKey(FontStyle.italic, weight, name)]
if (m != null) return m
}
// try to fallback to no style, normal weight
return registry[toKey(FontStyle.normal, FontWeight.normal, name)]
}
//////////////////////////////////////////////////////////////////////////
// Metrics
//////////////////////////////////////////////////////////////////////////
** Key of "<style> <weight> <name>"
const Str key
** Family name
const Str name
** Font weight
const FontWeight weight
** Font style
const FontStyle style
** Height is leading + ascent + descent
const Int height
** Leading normalized to 1000pt
const Int leading
** Ascent normalized to 1000pt
const Int ascent
** Descent normalized to 1000pt
const Int descent
** Widths table
const Int[] widths
** ASCII map of char to index into widths
private const Str charToWidth
** Last unicode char we have metrics for
const Int lastChar
** Character width normalized to 1000pts
Int charWidth(Int ch)
{
// control chars
if (ch < 32) return 0
// mapped char
if (ch < lastChar)
{
index := charToWidth[ch - 32] - 37
return widths[index]
}
// bullet
if (ch == 0x2022) return charWidth('*')
// subscripts to superscript 2
if (0x2080 <= ch && ch <= 0x2089) return charWidth('\u00B3')
// fallback to width of lowercase m
return charWidth('m')
}
}