% !TEX TS-program = lualatex \documentclass[11pt]{article} \usepackage[letterpaper, margin=0.75in, top=0.75in, bottom=0.85in]{geometry} \usepackage{fontspec} \usepackage{luacode} \usepackage{longtable, array, xcolor, needspace} \usepackage[hidelinks]{hyperref} \makeatletter \renewcommand{\@pnumwidth}{2em} \renewcommand{\@tocrmarg}{3em} \makeatother % Prefer the accompanying exports to an older installed copy. A build may also % supply \MFBFontPath explicitly (including its trailing slash). \providecommand{\MFBFontPath}{} \ifx\MFBFontPath\empty \IfFileExists{MFBOldstyle-Regular.otf}{}{% \IfFileExists{../opentype/MFBOldstyle-Regular.otf} {\renewcommand{\MFBFontPath}{../opentype/}} {\IfFileExists{../../../otf/MFBOldstyle-Regular.otf} {\renewcommand{\MFBFontPath}{../../../otf/}} {\IfFileExists{../../../MFBOldstyle-Regular.otf} {\renewcommand{\MFBFontPath}{../../../}}{}}}} \fi \setmainfont{MFBOldstyle}[ Path = \MFBFontPath, Extension = .otf, UprightFont = *-Regular, ItalicFont = *-Italic, BoldFont = *-Bold, BoldItalicFont = *-BoldItalic] \newfontface\MFBglyphR{MFBOldstyle-Regular.otf}[Path=\MFBFontPath,Scale=1.6] \newfontface\MFBglyphI{MFBOldstyle-Italic.otf}[Path=\MFBFontPath,Scale=1.6] \newfontface\MFBglyphB{MFBOldstyle-Bold.otf}[Path=\MFBFontPath,Scale=1.6] \newfontface\MFBglyphBI{MFBOldstyle-BoldItalic.otf}[Path=\MFBFontPath,Scale=1.6] \setlength{\parindent}{0pt} \setlength{\tabcolsep}{4pt} \renewcommand{\arraystretch}{1.1} \newcolumntype{G}{>{\centering\arraybackslash}m{0.55in}} \newcommand{\cmapnone}{{\color{gray}\normalsize ---}} \newsavebox{\cmapsavebox} \newcommand{\cmapbox}[1]{\sbox{\cmapsavebox}{#1}\textcolor{gray}{% \vrule height 0.3em depth 0pt width 0.3pt% \vrule height 0.3pt depth 0pt width \wd\cmapsavebox \vrule height 0.3em depth 0pt width 0.3pt}} % Use the actual ink bounds, with breathing room, when the font's TeX metrics % underestimate a tall symbol. Starting horizontal mode also centers mark boxes. \newcommand{\cmapink}[3]{\leavevmode\raisebox{0pt}[#1sp][#2sp]{#3}} \newcommand{\cmappart}[1]{% \needspace{3in}% \section*{#1}\addcontentsline{toc}{section}{#1}} \newcommand{\cmaptitle}[2]{% \multicolumn{7}{@{}p{\textwidth}@{}}{\rule{0pt}{4ex}\phantomsection \addcontentsline{toc}{subsection}{#1}% {\large\bfseries #1}\hfill{\normalfont\small\ttfamily #2}}\\[1ex]} \newcommand{\cmapcolumns}[1]{% {\footnotesize\itshape #1} & {\footnotesize\itshape Glyph} & {\footnotesize\itshape Regular} & {\footnotesize\itshape Italic} & {\footnotesize\itshape Bold} & {\footnotesize\itshape Bold Italic} & {\footnotesize\itshape Name}\\} \newcommand{\cmaphead}[3]{% \cmaptitle{#2}{#3}\cmapcolumns{#1}\endfirsthead \multicolumn{7}{@{}l@{}}{\footnotesize\itshape #2 (continued)}\\ \cmapcolumns{#1}\endhead} \begin{luacode*} mfbmap = {} -- Unicode names and general categories local names, cats = {}, {} for line in io.lines(kpse.find_file("UnicodeData.txt")) do local f = {} for field in (line .. ";"):gmatch("([^;]*);") do f[#f + 1] = field end local cp = tonumber(f[1], 16) local name = f[2] if name == "" and f[11] ~= "" then name = f[11] .. " (control)" end names[cp], cats[cp] = name, f[3] end -- Named blocks used by the family; future blocks still get a code-range heading. local blocks = { {0x0000, 0x007F, "Basic Latin"}, {0x0080, 0x00FF, "Latin-1 Supplement"}, {0x0100, 0x017F, "Latin Extended-A"}, {0x0180, 0x024F, "Latin Extended-B"}, {0x0250, 0x02AF, "IPA Extensions"}, {0x02B0, 0x02FF, "Spacing Modifier Letters"}, {0x0300, 0x036F, "Combining Diacritical Marks"}, {0x0370, 0x03FF, "Greek and Coptic"}, {0x0400, 0x04FF, "Cyrillic"}, {0x1AB0, 0x1AFF, "Combining Diacritical Marks Extended"}, {0x1D00, 0x1D7F, "Phonetic Extensions"}, {0x1D80, 0x1DBF, "Phonetic Extensions Supplement"}, {0x1DC0, 0x1DFF, "Combining Diacritical Marks Supplement"}, {0x1E00, 0x1EFF, "Latin Extended Additional"}, {0x1F00, 0x1FFF, "Greek Extended"}, {0x2000, 0x206F, "General Punctuation"}, {0x2070, 0x209F, "Superscripts and Subscripts"}, {0x20A0, 0x20CF, "Currency Symbols"}, {0x20D0, 0x20FF, "Combining Diacritical Marks for Symbols"}, {0x2100, 0x214F, "Letterlike Symbols"}, {0x2150, 0x218F, "Number Forms"}, {0x2190, 0x21FF, "Arrows"}, {0x2200, 0x22FF, "Mathematical Operators"}, {0x2300, 0x23FF, "Miscellaneous Technical"}, {0x2500, 0x257F, "Box Drawing"}, {0x2580, 0x259F, "Block Elements"}, {0x25A0, 0x25FF, "Geometric Shapes"}, {0x2600, 0x26FF, "Miscellaneous Symbols"}, {0x2700, 0x27BF, "Dingbats"}, {0x2C60, 0x2C7F, "Latin Extended-C"}, {0x2E00, 0x2E7F, "Supplemental Punctuation"}, {0xA700, 0xA71F, "Modifier Tone Letters"}, {0xA720, 0xA7FF, "Latin Extended-D"}, {0xAB30, 0xAB6F, "Latin Extended-E"}, {0xE000, 0xF8FF, "Private Use Area"}, {0xFB00, 0xFB4F, "Alphabetic Presentation Forms"}, {0xFE00, 0xFE0F, "Variation Selectors"}, {0xFE20, 0xFE2F, "Combining Half Marks"}, {0xFFF0, 0xFFFF, "Specials"}, {0x10140, 0x1018F, "Ancient Greek Numbers"}, {0x1D200, 0x1D24F, "Ancient Greek Musical Notation"}, {0xF0000, 0xFFFFF, "Supplementary Private Use Area-A"}, {0x100000, 0x10FFFF, "Supplementary Private Use Area-B"}, } local LIGATURE_BLOCK = 0xFB00 -- its characters (fi, fl) lead the ligatures local function block_of(cp) for _, b in ipairs(blocks) do if cp >= b[1] and cp <= b[2] then return b[1], b[2], b[3] end end local first = cp - cp % 256 return first, first + 255, string.format("Characters U+%04X\\textendash U+%04X", first, first + 255) end -- The original cmap and glyph order come from fontloader. luaotfload's Unicode -- descriptions also contain synthetic character slots and ToUnicode values; -- neither is evidence that an unencoded glyph has an actual Unicode encoding. local styles = {"R", "I", "B", "BI"} local codes, info, encoded, order, unicode_of, semantic_of = {}, {}, {}, {}, {}, {} local inventory = {} local function record(...) local fields = {...} for i, value in ipairs(fields) do fields[i] = tostring(value) end inventory[#inventory + 1] = table.concat(fields, "\t") end function mfbmap.register(style, id) local loaded = font.getfont(id) local raw = loaded.shared.rawdata local filename = loaded.filename or raw.metadata.filename local handle = assert(fontloader.open(filename), "Cannot read " .. filename) local native = fontloader.to_table(handle) fontloader.close(handle) local slots = {} for cp, d in pairs(raw.descriptions) do if type(cp) == "number" and d.index then if not slots[d.index] or cp < slots[d.index] then slots[d.index] = cp end end end local byname, details, cmap, encoded_names = {}, {}, {}, {} record("FONT", style, filename) local limit = math.min(assert(native.map.enc.char_cnt), 0x110000) for cp, gid in pairs(native.map.map) do local g = native.glyphs[gid] if type(cp) == "number" and cp >= 0 and cp < limit and g and g.unicode and g.unicode >= 0 then cmap[cp], encoded_names[g.name] = g.name, true unicode_of[g.name] = math.min(unicode_of[g.name] or cp, cp) record("CHAR", string.format("%X", cp), style, g.name) end end for gid, g in pairs(native.glyphs) do local slot = assert(slots[gid], "No printable slot for " .. style .. ":" .. g.name) assert(raw.descriptions[slot].name == g.name, "Stale font cache for " .. style .. ":" .. g.name) byname[g.name] = slot local semantic = raw.descriptions[slot].unicode if type(semantic) == "number" and names[semantic] then semantic_of[g.name] = semantic end details[g.name] = { class = g.class or raw.descriptions[slot].class, box = g.boundingbox or raw.descriptions[slot].boundingbox, factor = loaded.parameters.hfactor or loaded.size / loaded.units_per_em, encoded = encoded_names[g.name] or false, } if not order[g.name] then local rank = 0 for i, s in ipairs(styles) do if s == style then rank = i end end order[g.name] = rank * 100000 + gid end record("GLYPH", style, g.name, encoded_names[g.name] and "encoded" or "unencoded") end codes[style], info[style], encoded[style] = byname, details, cmap end local function escape(s) return (s:gsub("[\\{}%$&#_%^~%%]", function(c) return "\\char" .. string.byte(c) .. " " end)) end -- A literal glyph in one style. Center marks on their ink bounds so zero-width -- marks do not drift into the preceding column. Empty glyphs show their width. local function cell(style, name, unicode) if unicode then name = encoded[style][unicode] end if not name then return "\\cmapnone" end local cp = codes[style][name] if not cp then return "\\cmapnone" end local d = info[style][name] local f = "\\MFBglyph" .. style .. " " local ch = string.format("\\char\"%X ", cp) local cat = unicode and cats[unicode] or "" local function ink(content) local b = d.box if not b or b[1] == b[3] and b[2] == b[4] then return content end local padding = 98304 -- 1.5 pt above and below the actual outline. local height = math.max(0, math.ceil(b[4] * d.factor)) + padding local depth = math.max(0, math.ceil(-b[2] * d.factor)) + padding return string.format("\\cmapink{%d}{%d}{%s}", height, depth, content) end if d.class == "mark" or cat == "Mn" or cat == "Me" then local b = d.box if b and b[3] > b[1] then local width = math.floor((b[3] - b[1]) * d.factor + 0.5) local left = math.floor(-b[1] * d.factor + 0.5) return ink(string.format("%s\\hbox to %dsp{\\kern%dsp %s\\hss}", f, width, left, ch)) end return "\\cmapbox{" .. f .. ch .. "}" elseif cat == "Zs" or cat == "Cf" or cat == "Cc" or cat == "Zl" or cat == "Zp" then return "\\cmapbox{" .. f .. ch .. "}" elseif d.box and d.box[1] == d.box[3] and d.box[2] == d.box[4] then return "\\cmapbox{" .. f .. ch .. "}" end return ink(f .. ch) end local function row(code, name, description, unicode) -- Long Glyphs names also need break opportunities inside undotted words. local parts, run = {}, 0 for c in name:gmatch(".") do parts[#parts + 1] = escape(c) run = run + 1 if c == "_" or c == "." or run == 10 then parts[#parts + 1] = "\\allowbreak{}" run = 0 end end local shown = table.concat(parts) return string.format("{\\ttfamily\\small %s} & {\\ttfamily\\scriptsize %s} & %s & %s & %s & %s & {\\footnotesize\\textsc{%s}}\\\\", code, shown, cell("R", name, unicode), cell("I", name, unicode), cell("B", name, unicode), cell("BI", name, unicode), escape(description:lower())) end -- a block: its heading in the table's first head, the first two rows tied to it local TABLE = "\\begin{longtable}{@{}>{\\raggedright\\arraybackslash}p{0.55in} >{\\raggedright\\arraybackslash}p{1.25in} G G G G >{\\raggedright\\arraybackslash}p{2.15in}@{}}" local function block(put, column, title, range, rows) -- Reserve the title, column headings, and the first two multi-line rows. -- A one-inch reservation can strand the first head and repeat a continued -- heading before the real title when those rows move to the next page. put("\\Needspace{3in}") put(TABLE) put(string.format("\\cmaphead{%s}{%s}{%s}", column, title, range)) for i, r in ipairs(rows) do put(i <= 2 and r:gsub("\\\\$", "\\\\*") or r) end put("\\end{longtable}") end -- what a glyph without a code point of its own is, from its name local function uname(name) local cp = unicode_of[name] or (name:match("^uni(%x%x%x%x)$") and tonumber(name:sub(4), 16)) or (name:match("^u(%x%x%x%x%x?)$") and tonumber(name:sub(2), 16)) return cp and names[cp] or name end local function components(base) local parts = {} if base:match("^uni%x+$") and #base > 7 and (#base - 3) % 4 == 0 then for h in base:sub(4):gmatch("%x%x%x%x") do parts[#parts + 1] = names[tonumber(h, 16)] or h end else for p in base:gmatch("[^_]+") do parts[#parts + 1] = uname(p) end end return parts end local FIGURES = {lf = {"pnum", "proportional lining figure"}, tf = {"tnum", "tabular lining figure"}, osf = {"onum pnum", "proportional old-style figure"}, tosf = {"onum", "tabular old-style figure"}, numr = {"numr", "numerator"}, dnom = {"dnom", "denominator"}} local LOCL = {MAH = "Marshallese", TRK = "Turkish"} local SECTIONS = {"Ligatures", "Small capitals", "Greek small capitals", "Greek centered capitals", "Greek quantity forms", "Greek accent and shaping variants", "Internal Greek components", "Figures", "Accented sequences", "Localized forms", "Other glyphs"} -- Some quantity composites retain Glyphs' descriptive Greek names instead -- of uniXXXX production names. Recover their base from Unicode names too. local greek_aliases = {} for cp, text in pairs(names) do if (cp >= 0x370 and cp <= 0x3FF) or (cp >= 0x1F00 and cp <= 0x1FFF) then local key = text:lower():gsub("greek small letter ", "") :gsub("greek capital letter ", ""):gsub("greek letter ", "") :gsub("greek ", ""):gsub(" with ", ""):gsub(" and ", ""):gsub(" ", "") greek_aliases[key] = cp end end local function greek_name(base, full) local cp = unicode_of[base] or (full and semantic_of[full]) or greek_aliases[base:lower()] if not cp and base:match("^uni%x%x%x%x$") then cp = tonumber(base:sub(4), 16) end return cp and ((cp >= 0x370 and cp <= 0x3FF) or (cp >= 0x1F00 and cp <= 0x1FFF)) end local function greek_description(name, full) local text = uname(name) if text == name and (name:find("_") or name:match("^uni%x+$") and #name > 7) then text = table.concat(components(name), " + ") end if text == name and full and semantic_of[full] then text = names[semantic_of[full]] end if text == name and greek_aliases[name:lower()] then text = names[greek_aliases[name:lower()]] end return (text:lower() :gsub("greek small letter ", ""):gsub("greek capital letter ", "") :gsub("greek letter ", ""):gsub("greek ", "") :gsub("psili", "smooth breathing"):gsub("dasia", "rough breathing") :gsub("perispomeni", "circumflex"):gsub("oxia", "acute") :gsub("varia", "grave"):gsub("ypogegrammeni", "iota subscript") :gsub("prosgegrammeni", "iota adscript")) end local function greek_variant(base, rest, full) local what = greek_description(base, full) if rest:find("length", 1, true) then local quantity = rest:find("breve", 1, true) and "breve" or "macron" local kind = rest:match("%.base$") and "anchor base" or "preposed-accent carrier" return "Internal Greek components", "context", what .. "; " .. kind .. " used before a separate " .. quantity .. " mark" end local issc = rest:match("%.sc$") or rest == "sc" local iscap = rest:find("allcaps", 1, true) or rest:match("%.uc$") local quantity = rest:find("macron", 1, true) or rest:find(".mac", 1, true) local breve = rest:find("breve", 1, true) or rest:find(".br", 1, true) local details = {} if quantity then details[#details + 1] = "macron" end if breve then details[#details + 1] = "breve" end if rest:find("commaabove", 1, true) or rest:find("psili", 1, true) then details[#details + 1] = "smooth breathing" elseif rest:find("commareversedabove", 1, true) or rest:find("dasia", 1, true) then details[#details + 1] = "rough breathing" end if rest:find("perispomeni", 1, true) or rest:find("_circ", 1, true) then details[#details + 1] = "circumflex" elseif rest:find("acute", 1, true) then details[#details + 1] = "acute" elseif rest:find("grave", 1, true) then details[#details + 1] = "grave" end if #details > 0 then what = what .. "; " .. table.concat(details, ", ") end if rest:find("noaccent", 1, true) or rest:find(".na.", 1, true) then what = what .. "; breathing and pitch omitted" end if rest:find("adscriptbase", 1, true) then what = what .. "; adscript base" end if rest:find("spacingadscript", 1, true) then what = what .. "; spacing adscript" end if issc then return "Greek small capitals", "smcp / c2sc", "small capital: " .. what end if iscap then return "Greek centered capitals", "case / ss02", "centered capital: " .. what end if quantity or breve then return "Greek quantity forms", "ccmp", what end return "Greek accent and shaping variants", "context", what end local function describe(name) -- its section, the feature that reaches it, and what it is local base, rest = name:match("^([^.]*)%.?(.*)$") local locl = name:match("%.locl(%u%u%u)$") if locl then local inner = name:gsub("%.locl%u%u%u$", "") local what = inner:match("%.sc$") and ("small capital of " .. uname((inner:gsub("%.sc$", "")))) or uname(inner) return "Localized forms", "locl", (LOCL[locl] or locl) .. " form of " .. what elseif name == "mfbGreekAllcapsNoAccent.allcaps" then return "Internal Greek components", "context", "zero-width internal all-capital accent control" elseif name == "mfbGreekSmallcapsNoAccent.sc" then return "Internal Greek components", "context", "zero-width internal small-capital accent control" elseif base:match("^mfbGreekSwap") then return "Internal Greek components", "ccmp", "zero-width Greek mark-order control" elseif greek_name(base, name) or rest:find("allcaps", 1, true) or rest:find("grek", 1, true) or rest:find("length", 1, true) then return greek_variant(base, rest, name) elseif rest == "liga" or rest == "dlig" then local letters = {} for p in base:gmatch("[^_]+") do letters[#letters + 1] = p end return "Ligatures", rest, (rest == "dlig" and "discretionary ligature of " or "ligature of ") .. table.concat(letters, " + ") elseif base:find("_") or (base:match("^uni%x+$") and #base > 7) then local what = table.concat(components(base), " + ") if rest == "sc" then return "Accented sequences", "smcp", "small capital of " .. what end return "Accented sequences", "ccmp", what elseif rest == "sc" then return "Small capitals", "smcp", "small capital of " .. uname(base) elseif FIGURES[rest] then return "Figures", FIGURES[rest][1], FIGURES[rest][2] .. ": " .. uname(base) elseif name == ".notdef" then return "Other glyphs", "", "shown for characters the fonts lack" elseif rest == "alt" then return "Other glyphs", "", "alternate of " .. uname(base) .. " (in composites)" end return "Other glyphs", "", rest ~= "" and (rest .. " form of " .. uname(base)) or uname(base) end -- These are shaped input sequences, not isolated internal carrier glyphs. -- They demonstrate the same quantity support in every style. local function quantity_examples(put) put("\\cmappart{Greek quantity sequences}") put("Macrons and breves are entered as combining marks. The examples below show complete shaped sequences in every style. The internal components listed later need not contain the separately attached quantity mark.\\par\\medskip") local vowels = {{0x3B1,0x391,"alpha"},{0x3B5,0x395,"epsilon"},{0x3B7,0x397,"eta"}, {0x3B9,0x399,"iota"},{0x3BF,0x39F,"omicron"},{0x3C5,0x3A5,"upsilon"}, {0x3C9,0x3A9,"omega"}} local modes = { {"Lowercase quantity sequences",1,"","default"}, {"Capital quantity sequences",2,"","default"}, {"Centered-capital quantity sequences",2,",RawFeature={+ss02}","ss02"}, {"Small-capital quantity sequences",1,",RawFeature={+smcp,+c2sc}","smcp / c2sc"}} for _, mode in ipairs(modes) do local rows = {} for _, vowel in ipairs(vowels) do for _, q in ipairs({{0x304,"macron"},{0x306,"breve"}}) do for _, stacked in ipairs({false,true}) do local points = {vowel[mode[2]],q[1]} local detail = vowel[3] .. " with " .. q[2] if stacked then points[#points + 1], points[#points + 2] = 0x313, 0x342 detail = detail .. ", smooth breathing and circumflex" end local input, shown = {}, {} for _, cp in ipairs(points) do input[#input + 1] = string.format("\\char\"%X ",cp) shown[#shown + 1] = string.format("%04X",cp) end local cells = {} for _, style in ipairs(styles) do cells[#cells + 1] = "{\\MFBglyph" .. style .. "\\addfontfeatures{Script=Greek" .. mode[3] .. "}" .. table.concat(input) .. "}" end rows[#rows + 1] = "{\\ttfamily\\small " .. mode[4] .. "} & {\\ttfamily\\scriptsize " .. table.concat(shown," +\\allowbreak{} ") .. "} & " .. table.concat(cells," & ") .. " & {\\footnotesize\\textsc{" .. detail .. "}}\\\\" end end end block(put,"Access",mode[1],"Unicode input",rows) end end function mfbmap.map() local out = {} local function put(s) out[#out + 1] = s end -- the Unicode characters, by block local cps, glyph_of, seen, encoded_names = {}, {}, {}, {} for _, s in ipairs(styles) do for cp, name in pairs(encoded[s]) do encoded_names[name] = true if not seen[cp] then seen[cp] = true; cps[#cps + 1] = cp; glyph_of[cp] = name end end end table.sort(cps) put("\\cmappart{Unicode characters}") local rows, current, presentation = {}, nil, {} for _, cp in ipairs(cps) do record("ROW", "unicode", string.format("%X", cp)) local first, last, name = block_of(cp) if first == LIGATURE_BLOCK then presentation[#presentation + 1] = cp else if first ~= (current and current[1]) then if current then block(put, "Code", current[3], current[4], rows) end current, rows = {first, last, name, string.format("U+%04X\\textendash U+%04X", first, last)}, {} end rows[#rows + 1] = row(string.format("U+%04X", cp), glyph_of[cp], names[cp] or "(unassigned)", cp) end end if current then block(put, "Code", current[3], current[4], rows) end quantity_examples(put) -- the glyphs without code points of their own, by kind, in the fonts' glyph order local groups, private, variants_seen = {}, {}, {} for _, s in ipairs(styles) do for name in pairs(codes[s]) do if not info[s][name].encoded and not variants_seen[name] then variants_seen[name] = true private[#private + 1] = name end end end table.sort(private, function(a, b) if order[a] == order[b] then return a < b end return order[a] < order[b] end) for _, name in ipairs(private) do record("ROW", "variant", name) local section, feature, description = describe(name) groups[section] = groups[section] or {} table.insert(groups[section], row(feature ~= "" and feature or "---", name, description)) end -- the presentation forms (fi, fl) lead the ligatures local ligatures = {} for _, cp in ipairs(presentation) do ligatures[#ligatures + 1] = row(string.format("U+%04X", cp), glyph_of[cp], names[cp] or "(unassigned)", cp) end for _, r in ipairs(groups["Ligatures"] or {}) do ligatures[#ligatures + 1] = r end groups["Ligatures"] = ligatures put("\\cmappart{Ligatures and glyph variants}") for _, section in ipairs(SECTIONS) do if groups[section] and #groups[section] > 0 then block(put, "Access", section, string.format("%d glyphs", #groups[section]), groups[section]) end end local report = assert(io.open(tex.jobname .. "-inventory.tsv", "w")) report:write(table.concat(inventory, "\n"), "\n") report:close() texio.write_nl("MFB character map: " .. #cps .. " Unicode rows and " .. #private .. " variant rows.") tex.print(out) end \end{luacode*} % the fonts' glyphs \newcommand{\cmapregister}[2]{\setbox0=\hbox{#2\directlua{mfbmap.register("#1", font.current())}}} \cmapregister{R}{\MFBglyphR} \cmapregister{I}{\MFBglyphI} \cmapregister{B}{\MFBglyphB} \cmapregister{BI}{\MFBglyphBI} \begin{document} \begin{center} {\LARGE\itshape MFB Oldstyle}\par\medskip {\large Character Map}\par\medskip \end{center} \small These tables show the available characters in \textit{MFB Oldstyle}. Combining marks are shown in isolation, centered on their ink. Empty glyphs are indicated by a gray width marker. Access labels identify the associated OpenType feature or context. \normalsize \bigskip \tableofcontents \clearpage \directlua{mfbmap.map()} \end{document} .