diff options
author | Karl Berry <karl@freefriends.org> | 2016-04-22 22:14:39 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2016-04-22 22:14:39 +0000 |
commit | fc4466b32ed330a956ac603b00fd145524cff49a (patch) | |
tree | 2c50e2b8de13aa9233b2c76dffe201558f169e86 /Master/texmf-dist/tex/context/base/font-syn.lua | |
parent | 50e2368597d5f6fe2057195d0ae6a9f2044923e4 (diff) |
context (22apr16)
git-svn-id: svn://tug.org/texlive/trunk@40691 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/context/base/font-syn.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/font-syn.lua | 2116 |
1 files changed, 0 insertions, 2116 deletions
diff --git a/Master/texmf-dist/tex/context/base/font-syn.lua b/Master/texmf-dist/tex/context/base/font-syn.lua deleted file mode 100644 index bf46c85730f..00000000000 --- a/Master/texmf-dist/tex/context/base/font-syn.lua +++ /dev/null @@ -1,2116 +0,0 @@ -if not modules then modules = { } end modules ['font-syn'] = { - version = 1.001, - comment = "companion to font-ini.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - --- todo: subs in lookups requests - -local next, tonumber, type, tostring = next, tonumber, type, tostring -local sub, gsub, lower, match, find, lower, upper = string.sub, string.gsub, string.lower, string.match, string.find, string.lower, string.upper -local find, gmatch = string.find, string.gmatch -local concat, sort, format = table.concat, table.sort, string.format -local serialize, sortedhash = table.serialize, table.sortedhash -local lpegmatch = lpeg.match -local unpack = unpack or table.unpack -local formatters, topattern = string.formatters, string.topattern - -local allocate = utilities.storage.allocate -local sparse = utilities.storage.sparse -local setmetatableindex = table.setmetatableindex - -local removesuffix = file.removesuffix -local splitbase = file.splitbase -local splitname = file.splitname -local basename = file.basename -local nameonly = file.nameonly -local pathpart = file.pathpart -local filejoin = file.join -local is_qualified_path = file.is_qualified_path -local exists = io.exists - -local findfile = resolvers.findfile -local cleanpath = resolvers.cleanpath -local resolveprefix = resolvers.resolve - -local fontloader = fontloader -local font_to_table = fontloader.to_table -local open_font = fontloader.open -local get_font_info = fontloader.info -local close_font = fontloader.close -local font_fields = fontloader.fields - -local settings_to_hash = utilities.parsers.settings_to_hash_tolerant - -local trace_names = false trackers.register("fonts.names", function(v) trace_names = v end) -local trace_warnings = false trackers.register("fonts.warnings", function(v) trace_warnings = v end) -local trace_specifications = false trackers.register("fonts.specifications", function(v) trace_specifications = v end) - -local report_names = logs.reporter("fonts","names") - ---[[ldx-- -<p>This module implements a name to filename resolver. Names are resolved -using a table that has keys filtered from the font related files.</p> ---ldx]]-- - -fonts = fonts or { } -- also used elsewhere - -local names = fonts.names or allocate { } -fonts.names = names - -local filters = names.filters or { } -names.filters = filters - -local treatments = fonts.treatments or { } -fonts.treatments = treatments - -names.data = names.data or allocate { } - -names.version = 1.123 -names.basename = "names" -names.saved = false -names.loaded = false -names.be_clever = true -names.enabled = true -names.cache = containers.define("fonts","data",names.version,true) - -local usesystemfonts = true -local autoreload = true - -directives.register("fonts.autoreload", function(v) autoreload = toboolean(v) end) -directives.register("fonts.usesystemfonts", function(v) usesystemfonts = toboolean(v) end) - ---[[ldx-- -<p>A few helpers.</p> ---ldx]]-- - -local P, C, Cc, Cs = lpeg.P, lpeg.C, lpeg.Cc, lpeg.Cs - --- -- what to do with these -- -- --- --- thin -> thin --- --- regu -> regular -> normal --- norm -> normal -> normal --- stan -> standard -> normal --- medi -> medium --- ultr -> ultra --- ligh -> light --- heav -> heavy --- blac -> black --- thin --- book --- verylight --- --- buch -> book --- buchschrift -> book --- halb -> demi --- halbfett -> demi --- mitt -> medium --- mittel -> medium --- fett -> bold --- mage -> light --- mager -> light --- nord -> normal --- gras -> normal - -local weights = Cs ( -- not extra - P("demibold") - + P("semibold") - + P("mediumbold") - + P("ultrabold") - + P("extrabold") - + P("ultralight") - + P("extralight") - + P("bold") - + P("demi") - + P("semi") - + P("light") - + P("medium") - + P("heavy") - + P("ultra") - + P("black") ---+ P("bol") / "bold" -- blocks - + P("bol") - + P("regular") / "normal" -) - --- numeric_weights = { --- 200 = "extralight", --- 300 = "light", --- 400 = "book", --- 500 = "medium", --- 600 = "demi", --- 700 = "bold", --- 800 = "heavy", --- 900 = "black", --- } - -local normalized_weights = sparse { - regular = "normal", -} - -local styles = Cs ( - P("reverseoblique") / "reverseitalic" - + P("regular") / "normal" - + P("italic") - + P("oblique") / "italic" - + P("slanted") - + P("roman") / "normal" - + P("ital") / "italic" -- might be tricky - + P("ita") / "italic" -- might be tricky ---+ P("obli") / "oblique" -) - -local normalized_styles = sparse { - reverseoblique = "reverseitalic", - regular = "normal", - oblique = "italic", -} - -local widths = Cs( - P("condensed") - + P("thin") - + P("expanded") - + P("cond") / "condensed" ---+ P("expa") / "expanded" - + P("normal") - + P("book") / "normal" -) - -local normalized_widths = sparse() - -local variants = Cs( -- fax casual - P("smallcaps") - + P("oldstyle") - + P("caps") / "smallcaps" -) - -local normalized_variants = sparse() - -names.knownweights = { - "black", - "bold", - "demi", - "demibold", - "extrabold", - "heavy", - "light", - "medium", - "mediumbold", - "normal", - "regular", - "semi", - "semibold", - "ultra", - "ultrabold", - "ultralight", -} - -names.knownstyles = { - "italic", - "normal", - "oblique", - "regular", - "reverseitalic", - "reverseoblique", - "roman", - "slanted", -} - -names.knownwidths = { - "book", - "condensed", - "expanded", - "normal", - "thin", -} - -names.knownvariants = { - "normal", - "oldstyle", - "smallcaps", -} - -local remappedweights = { - [""] = "normal", - ["bol"] = "bold", -} - -local remappedstyles = { - [""] = "normal", -} - -local remappedwidths = { - [""] = "normal", -} - -local remappedvariants = { - [""] = "normal", -} - -names.remappedweights = remappedweights setmetatableindex(remappedweights ,"self") -names.remappedstyles = remappedstyles setmetatableindex(remappedstyles ,"self") -names.remappedwidths = remappedwidths setmetatableindex(remappedwidths ,"self") -names.remappedvariants = remappedvariants setmetatableindex(remappedvariants,"self") - -local any = P(1) - -local analyzed_table - -local analyzer = Cs ( - ( - weights / function(s) analyzed_table[1] = s return "" end - + styles / function(s) analyzed_table[2] = s return "" end - + widths / function(s) analyzed_table[3] = s return "" end - + variants / function(s) analyzed_table[4] = s return "" end - + any - )^0 -) - -local splitter = lpeg.splitat("-") - -function names.splitspec(askedname) - local name, weight, style, width, variant = lpegmatch(splitter,askedname) - weight = weight and lpegmatch(weights, weight) or weight - style = style and lpegmatch(styles, style) or style - width = width and lpegmatch(widths, width) or width - variant = variant and lpegmatch(variants,variant) or variant - if trace_names then - report_names("requested name %a split in name %a, weight %a, style %a, width %a and variant %a", - askedname,name,weight,style,width,variant) - end - if not weight or not weight or not width or not variant then - weight, style, width, variant = weight or "normal", style or "normal", width or "normal", variant or "normal" - if trace_names then - report_names("request %a normalized to '%s-%s-%s-%s-%s'", - askedname,name,weight,style,width,variant) - end - end - return name or askedname, weight, style, width, variant -end - -local function analyzespec(somename) - if somename then - analyzed_table = { } - local name = lpegmatch(analyzer,somename) - return name, analyzed_table[1], analyzed_table[2], analyzed_table[3], analyzed_table[4] - end -end - ---[[ldx-- -<p>It would make sense to implement the filters in the related modules, -but to keep the overview, we define them here.</p> ---ldx]]-- - -filters.otf = get_font_info -filters.ttf = get_font_info -filters.ttc = get_font_info -filters.dfont = get_font_info - --- We had this as temporary solution because we needed a bit more info but in the --- meantime it got an interesting side effect: currently luatex delays loading of e.g. --- glyphs so here we first load and then discard which is a waste. In the past it did --- free memory because a full load was done. One of these things that goes unnoticed. --- --- missing: names, units_per_em, design_range_bottom, design_range_top, design_size, --- pfminfo, top_side_bearing - --- local function get_full_info(...) -- check with taco what we get / could get --- local ff = open_font(...) --- if ff then --- local d = ff -- and font_to_table(ff) --- d.glyphs, d.subfonts, d.gpos, d.gsub, d.lookups = nil, nil, nil, nil, nil --- close_font(ff) --- return d --- else --- return nil, "error in loading font" --- end --- end - --- Phillip suggested this faster variant but it's still a hack as fontloader.info should --- return these keys/values (and maybe some more) but at least we close the loader which --- might save some memory in the end. - --- local function get_full_info(name) --- local ff = open_font(name) --- if ff then --- local fields = table.tohash(font_fields(ff),true) -- isn't that one stable --- local d = { --- names = fields.names and ff.names, --- familyname = fields.familyname and ff.familyname, --- fullname = fields.fullname and ff.fullname, --- fontname = fields.fontname and ff.fontname, --- weight = fields.weight and ff.weight, --- italicangle = fields.italicangle and ff.italicangle, --- units_per_em = fields.units_per_em and ff.units_per_em, --- design_range_bottom = fields.design_range_bottom and ff.design_range_bottom, --- design_range_top = fields.design_range_top and ff.design_range_top, --- design_size = fields.design_size and ff.design_size, --- italicangle = fields.italicangle and ff.italicangle, --- pfminfo = fields.pfminfo and ff.pfminfo, --- top_side_bearing = fields.top_side_bearing and ff.top_side_bearing, --- } --- setmetatableindex(d,function(t,k) --- report_names("warning, trying to access field %a in font table of %a",k,name) --- end) --- close_font(ff) --- return d --- else --- return nil, "error in loading font" --- end --- end - --- more efficient: - -local fields = nil - -local function get_full_info(name) - local ff = open_font(name) - if ff then - if not fields then - fields = table.tohash(font_fields(ff),true) - end - -- unfortunately luatex aborts when a field is not available - local d = { - names = fields.names and ff.names, - familyname = fields.familyname and ff.familyname, - fullname = fields.fullname and ff.fullname, - fontname = fields.fontname and ff.fontname, - weight = fields.weight and ff.weight, - italicangle = fields.italicangle and ff.italicangle, - units_per_em = fields.units_per_em and ff.units_per_em, - design_range_bottom = fields.design_range_bottom and ff.design_range_bottom, - design_range_top = fields.design_range_top and ff.design_range_top, - design_size = fields.design_size and ff.design_size, - italicangle = fields.italicangle and ff.italicangle, - pfminfo = fields.pfminfo and ff.pfminfo, - top_side_bearing = fields.top_side_bearing and ff.top_side_bearing, -- not there - } - setmetatableindex(d,function(t,k) - report_names("warning, trying to access field %a in font table of %a",k,name) - end) - close_font(ff) - return d - else - return nil, "error in loading font" - end -end - --- As we have lazy loading anyway, this one still is full and with less code than --- the previous one. But this depends on the garbage collector to kick in and in the --- current version that somehow happens not that often (on my machine I end up with --- soem 3 GB extra before that happens). - --- local function get_full_info(...) --- local ff = open_font(...) --- if ff then --- local d = { } -- ff is userdata so [1] or # fails on it --- setmetatableindex(d,ff) --- return d -- garbage collection will do the close_font(ff) --- else --- return nil, "error in loading font" --- end --- end - -fontloader.fullinfo = get_full_info -filters .otf = get_full_info -filters .ttf = get_full_info - -function filters.afm(name) - -- we could parse the afm file as well, and then report an error but - -- it's not worth the trouble - local pfbname = findfile(removesuffix(name)..".pfb","pfb") or "" - if pfbname == "" then - pfbname = findfile(nameonly(name)..".pfb","pfb") or "" - end - if pfbname ~= "" then - local f = io.open(name) - if f then - local hash = { } - for line in f:lines() do -- slow - local key, value = match(line,"^(.+)%s+(.+)%s*$") - if key and #key > 0 then - hash[lower(key)] = value - end - if find(line,"StartCharMetrics",1,true) then - break - end - end - f:close() - return hash - end - end - return nil, "no matching pfb file" -end - -function filters.pfb(name) - return get_font_info(name) -end - ---[[ldx-- -<p>The scanner loops over the filters using the information stored in -the file databases. Watch how we check not only for the names, but also -for combination with the weight of a font.</p> ---ldx]]-- - -filters.list = { - "otf", "ttf", "ttc", "dfont", "afm", - -- "ttc", "otf", "ttf", "dfont", "afm", -} - -names.fontconfigfile = "fonts.conf" -- a bit weird format, bonus feature -names.osfontdirvariable = "OSFONTDIR" -- the official way, in minimals etc - -filters.paths = { } -filters.names = { } - -function names.getpaths(trace) - local hash, result, r = { }, { }, 0 - local function collect(t,where) - for i=1,#t do - local v = cleanpath(t[i]) - v = gsub(v,"/+$","") -- not needed any more - local key = lower(v) - report_names("%a specifies path %a",where,v) - if not hash[key] then - r = r + 1 - result[r] = v - hash[key] = true - end - end - end - local path = names.osfontdirvariable or "" - if path ~= "" then - collect(resolvers.expandedpathlist(path),path) - end - if xml then - local confname = resolvers.expansion("FONTCONFIG_FILE") or "" - if confname == "" then - confname = names.fontconfigfile or "" - end - if confname ~= "" then - -- first look in the tex tree - local name = findfile(confname,"fontconfig files") or "" - if name == "" then - -- after all, fontconfig is a unix thing - name = filejoin("/etc",confname) - if not lfs.isfile(name) then - name = "" -- force quit - end - end - if name ~= "" and lfs.isfile(name) then - if trace_names then - report_names("%s fontconfig file %a","loading",name) - end - local xmldata = xml.load(name) - -- begin of untested mess - xml.include(xmldata,"include","",true,function(incname) - if not is_qualified_path(incname) then - local path = pathpart(name) -- main name - if path ~= "" then - incname = filejoin(path,incname) - end - end - if lfs.isfile(incname) then - if trace_names then - report_names("%s fontconfig file %a","merging included",incname) - end - return io.loaddata(incname) - elseif trace_names then - report_names("%s fontconfig file: %a","ignoring included",incname) - end - end) - -- end of untested mess - local fontdirs = xml.collect_texts(xmldata,"dir",true) - if trace_names then - report_names("%s dirs found in fontconfig",#fontdirs) - end - collect(fontdirs,"fontconfig file") - end - end - end - function names.getpaths() - return result - end - return result -end - -local function cleanname(name) - return (gsub(lower(name),"[^%a%d]","")) -end - -local function cleanfilename(fullname,defaultsuffix) - local path, name, suffix = splitname(fullname) - name = gsub(lower(name),"[^%a%d]","") - if suffix and suffix ~= "" then - return name .. ".".. suffix - elseif defaultsuffix and defaultsuffix ~= "" then - return name .. ".".. defaultsuffix - else - return name - end -end - -names.cleanname = cleanname -names.cleanfilename = cleanfilename - -local function check_names(result) - local names = result.names - if names then - for i=1,#names do - local name = names[i] - if name.lang == "English (US)" then - return name.names - end - end - end -end - -local function walk_tree(pathlist,suffix,identify) - if pathlist then - for i=1,#pathlist do - local path = pathlist[i] - path = cleanpath(path .. "/") - path = gsub(path,"/+","/") - local pattern = path .. "**." .. suffix -- ** forces recurse - report_names("globbing path %a",pattern) - local t = dir.glob(pattern) - sort(t,sorter) - for j=1,#t do - local completename = t[j] - identify(completename,basename(completename),suffix,completename) - end - end - end -end - -local function check_name(data,result,filename,modification,suffix,subfont) - -- shortcuts - local specifications = data.specifications - -- prepare - local names = check_names(result) - -- fetch - local familyname = names and names.preffamilyname or result.familyname - local fullname = names and names.fullname or result.fullname - local fontname = result.fontname - local subfamily = names and names.subfamily - local modifiers = names and names.prefmodifiers - local weight = names and names.weight or result.weight - local italicangle = tonumber(result.italicangle) - local subfont = subfont or nil - local rawname = fullname or fontname or familyname - local filebase = removesuffix(basename(filename)) - local cleanfilename = cleanname(filebase) -- for WS - -- normalize - familyname = familyname and cleanname(familyname) - fullname = fullname and cleanname(fullname) - fontname = fontname and cleanname(fontname) - subfamily = subfamily and cleanname(subfamily) - modifiers = modifiers and cleanname(modifiers) - weight = weight and cleanname(weight) - italicangle = italicangle == 0 and nil - -- analyze - local a_name, a_weight, a_style, a_width, a_variant = analyzespec(fullname or fontname or familyname) - -- check - local width = a_width - local variant = a_variant - local style = modifiers and gsub(modifiers,"[^%a]","") - if not style and italicangle then - style = "italic" - end - if not variant or variant == "" then - variant = "normal" - end - if not weight or weight == "" then - weight = a_weight - end - if not style or style == "" then - style = a_style - end - if not familyname then - familyname = a_name - end - fontname = fontname or fullname or familyname or filebase -- maybe cleanfilename - fullname = fullname or fontname - familyname = familyname or fontname - -- we do these sparse - local units = result.units_per_em or 1000 -- can be zero too - local minsize = result.design_range_bottom or 0 - local maxsize = result.design_range_top or 0 - local designsize = result.design_size or 0 - local angle = result.italicangle or 0 - local pfminfo = result.pfminfo - local pfmwidth = pfminfo and pfminfo.width or 0 - local pfmweight = pfminfo and pfminfo.weight or 0 - -- - specifications[#specifications + 1] = { - filename = filename, -- unresolved - cleanfilename = cleanfilename, - format = lower(suffix), - subfont = subfont, - rawname = rawname, - familyname = familyname, - fullname = fullname, - fontname = fontname, - subfamily = subfamily, - modifiers = modifiers, - weight = weight, - style = style, - width = width, - variant = variant, - units = units ~= 1000 and units or nil, - pfmwidth = pfmwidth ~= 0 and pfmwidth or nil, - pfmweight = pfmweight ~= 0 and pfmweight or nil, - angle = angle ~= 0 and angle or nil, - minsize = minsize ~= 0 and minsize or nil, - maxsize = maxsize ~= 0 and maxsize or nil, - designsize = designsize ~= 0 and designsize or nil, - modification = modification ~= 0 and modification or nil, - } --- inspect(filename) --- inspect(result) --- inspect(specifications[#specifications]) -end - -local function cleanupkeywords() - local data = names.data - local specifications = names.data.specifications - if specifications then - local weights = { } - local styles = { } - local widths = { } - local variants = { } - for i=1,#specifications do - local s = specifications[i] - -- fix (sofar styles are taken from the name, and widths from the specification) - local _, b_weight, b_style, b_width, b_variant = analyzespec(s.weight) - local _, c_weight, c_style, c_width, c_variant = analyzespec(s.style) - local _, d_weight, d_style, d_width, d_variant = analyzespec(s.width) - local _, e_weight, e_style, e_width, e_variant = analyzespec(s.variant) - local _, f_weight, f_style, f_width, f_variant = analyzespec(s.fullname or "") - local weight = b_weight or c_weight or d_weight or e_weight or f_weight or "normal" - local style = b_style or c_style or d_style or e_style or f_style or "normal" - local width = b_width or c_width or d_width or e_width or f_width or "normal" - local variant = b_variant or c_variant or d_variant or e_variant or f_variant or "normal" - weight = remappedweights [weight or ""] - style = remappedstyles [style or ""] - width = remappedwidths [width or ""] - variant = remappedvariants[variant or ""] - weights [weight ] = (weights [weight ] or 0) + 1 - styles [style ] = (styles [style ] or 0) + 1 - widths [width ] = (widths [width ] or 0) + 1 - variants[variant] = (variants[variant] or 0) + 1 - if weight ~= s.weight then - s.fontweight = s.weight - end - s.weight, s.style, s.width, s.variant = weight, style, width, variant - end - local stats = data.statistics - stats.used_weights, stats.used_styles, stats.used_widths, stats.used_variants = weights, styles, widths, variants - end -end - -local function collectstatistics() - local data = names.data - local specifications = data.specifications - if specifications then - local f_w = formatters["%i"] - local f_a = formatters["%0.2f"] - -- normal stuff - local weights = { } - local styles = { } - local widths = { } - local variants = { } - -- weird stuff - local angles = { } - -- extra stuff - local pfmweights = { } setmetatableindex(pfmweights,"table") - local pfmwidths = { } setmetatableindex(pfmwidths, "table") - -- main loop - for i=1,#specifications do - local s = specifications[i] - -- normal stuff - local weight = s.weight - local style = s.style - local width = s.width - local variant = s.variant - if weight then weights [weight ] = (weights [weight ] or 0) + 1 end - if style then styles [style ] = (styles [style ] or 0) + 1 end - if width then widths [width ] = (widths [width ] or 0) + 1 end - if variant then variants[variant] = (variants[variant] or 0) + 1 end - -- weird stuff - local angle = f_a(tonumber(s.angle) or 0) - angles[angle] = (angles[angles] or 0) + 1 - -- extra stuff - local pfmweight = f_w(s.pfmweight or 0) - local pfmwidth = f_w(s.pfmwidth or 0) - local tweights = pfmweights[pfmweight] - local twidths = pfmwidths [pfmwidth] - tweights[pfmweight] = (tweights[pfmweight] or 0) + 1 - twidths[pfmwidth] = (twidths [pfmwidth] or 0) + 1 - end - -- - local stats = data.statistics - stats.weights = weights - stats.styles = styles - stats.widths = widths - stats.variants = variants - stats.angles = angles - stats.pfmweights = pfmweights - stats.pfmwidths = pfmwidths - stats.fonts = #specifications - -- - setmetatableindex(pfmweights,nil) - setmetatableindex(pfmwidths, nil) - -- - report_names("") - report_names("weights") - report_names("") - report_names(formatters[" %T"](weights)) - report_names("") - report_names("styles") - report_names("") - report_names(formatters[" %T"](styles)) - report_names("") - report_names("widths") - report_names("") - report_names(formatters[" %T"](widths)) - report_names("") - report_names("variants") - report_names("") - report_names(formatters[" %T"](variants)) - report_names("") - report_names("angles") - report_names("") - report_names(formatters[" %T"](angles)) - report_names("") - report_names("pfmweights") - report_names("") - for k, v in sortedhash(pfmweights) do - report_names(formatters[" %-10s: %T"](k,v)) - end - report_names("") - report_names("pfmwidths") - report_names("") - for k, v in sortedhash(pfmwidths) do - report_names(formatters[" %-10s: %T"](k,v)) - end - report_names("") - end -end - -local function collecthashes() - local data = names.data - local mappings = data.mappings - local fallbacks = data.fallbacks - local specifications = data.specifications - local nofmappings = 0 - local noffallbacks = 0 - if specifications then - -- maybe multiple passes - for index=1,#specifications do - local s = specifications[index] - local format, fullname, fontname, familyname, weight, subfamily = s.format, s.fullname, s.fontname, s.familyname, s.weight, s.subfamily - local mf, ff = mappings[format], fallbacks[format] - if fullname and not mf[fullname] then - mf[fullname], nofmappings = index, nofmappings + 1 - end - if fontname and not mf[fontname] then - mf[fontname], nofmappings = index, nofmappings + 1 - end - if familyname and weight and weight ~= sub(familyname,#familyname-#weight+1,#familyname) then - local madename = familyname .. weight - if not mf[madename] and not ff[madename] then - ff[madename], noffallbacks = index, noffallbacks + 1 - end - end - if familyname and subfamily and subfamily ~= sub(familyname,#familyname-#subfamily+1,#familyname) then - local extraname = familyname .. subfamily - if not mf[extraname] and not ff[extraname] then - ff[extraname], noffallbacks = index, noffallbacks + 1 - end - end - if familyname and not mf[familyname] and not ff[familyname] then - ff[familyname], noffallbacks = index, noffallbacks + 1 - end - end - end - return nofmappings, noffallbacks -end - -local function collectfamilies() - local data = names.data - local specifications = data.specifications - local families = data.families - for index=1,#specifications do - local familyname = specifications[index].familyname - local family = families[familyname] - if not family then - families[familyname] = { index } - else - family[#family+1] = index - end - end -end - -local function checkduplicate(where) -- fails on "Romantik" but that's a border case anyway - local data = names.data - local mapping = data[where] - local specifications = data.specifications - local loaded = { } - if specifications and mapping then - -- was: for _, m in sortedhash(mapping) do - local order = filters.list - for i=1,#order do - local m = mapping[order[i]] - for k, v in sortedhash(m) do - local s = specifications[v] - local hash = formatters["%s-%s-%s-%s-%s"](s.familyname,s.weight or "*",s.style or "*",s.width or "*",s.variant or "*") - local h = loaded[hash] - if h then - local ok = true - local fn = s.filename - for i=1,#h do - local hn = s.filename - if h[i] == fn then - ok = false - break - end - end - if ok then - h[#h+1] = fn - end - else - loaded[hash] = { s.filename } - end - end - end - end - local n = 0 - for k, v in sortedhash(loaded) do - local nv = #v - if nv > 1 then - if trace_warnings then - report_names("lookup %a clashes with %a",k,v) - end - n = n + nv - end - end - report_names("%a double lookups in %a",n,where) -end - -local function checkduplicates() - checkduplicate("mappings") - checkduplicate("fallbacks") -end - -local sorter = function(a,b) - return a > b -- to be checked -end - -local function sorthashes() - local data = names.data - local list = filters.list - local mappings = data.mappings - local fallbacks = data.fallbacks - local sorted_mappings = { } - local sorted_fallbacks = { } - data.sorted_mappings = sorted_mappings - data.sorted_fallbacks = sorted_fallbacks - for i=1,#list do - local l = list[i] - sorted_mappings [l] = table.keys(mappings[l]) - sorted_fallbacks[l] = table.keys(fallbacks[l]) - sort(sorted_mappings [l],sorter) - sort(sorted_fallbacks[l],sorter) - end - data.sorted_families = table.keys(data.families) - sort(data.sorted_families,sorter) -end - -local function unpackreferences() - local data = names.data - local specifications = data.specifications - if specifications then - for k, v in next, data.families do - for i=1,#v do - v[i] = specifications[v[i]] - end - end - local mappings = data.mappings - if mappings then - for _, m in next, mappings do - for k, v in next, m do - m[k] = specifications[v] - end - end - end - local fallbacks = data.fallbacks - if fallbacks then - for _, f in next, fallbacks do - for k, v in next, f do - f[k] = specifications[v] - end - end - end - end -end - -local function analyzefiles(olddata) - if not trace_warnings then - report_names("warnings are disabled (tracker 'fonts.warnings')") - end - local data = names.data - local done = { } - local totalnofread = 0 - local totalnofskipped = 0 - local totalnofduplicates = 0 - local nofread = 0 - local nofskipped = 0 - local nofduplicates = 0 - local skip_paths = filters.paths - local skip_names = filters.names - local specifications = data.specifications - local oldindices = olddata and olddata.indices or { } - local oldspecifications = olddata and olddata.specifications or { } - local oldrejected = olddata and olddata.rejected or { } - local treatmentdata = treatments.data or { } -- when used outside context - local function identify(completename,name,suffix,storedname) - local pathpart, basepart = splitbase(completename) - nofread = nofread + 1 - local treatment = treatmentdata[completename] or treatmentdata[basepart] - if treatment and treatment.ignored then - if trace_names then - report_names("%s font %a is ignored, reason %a",suffix,completename,treatment.comment or "unknown") - end - nofskipped = nofskipped + 1 - elseif done[name] then - -- already done (avoid otf afm clash) - if trace_names then - report_names("%s font %a already done",suffix,completename) - end - nofduplicates = nofduplicates + 1 - nofskipped = nofskipped + 1 - elseif not exists(completename) then - -- weird error - if trace_names then - report_names("%s font %a does not really exist",suffix,completename) - end - nofskipped = nofskipped + 1 - elseif not is_qualified_path(completename) and findfile(completename,suffix) == "" then - -- not locatable by backend anyway - if trace_names then - report_names("%s font %a cannot be found by backend",suffix,completename) - end - nofskipped = nofskipped + 1 - else - if #skip_paths > 0 then - for i=1,#skip_paths do - if find(pathpart,skip_paths[i]) then - if trace_names then - report_names("rejecting path of %s font %a",suffix,completename) - end - nofskipped = nofskipped + 1 - return - end - end - end - if #skip_names > 0 then - for i=1,#skip_paths do - if find(basepart,skip_names[i]) then - done[name] = true - if trace_names then - report_names("rejecting name of %s font %a",suffix,completename) - end - nofskipped = nofskipped + 1 - return - end - end - end - if trace_names then - report_names("identifying %s font %a",suffix,completename) - end - local result = nil - local modification = lfs.attributes(completename,"modification") - if olddata and modification and modification > 0 then - local oldindex = oldindices[storedname] -- index into specifications - if oldindex then - local oldspecification = oldspecifications[oldindex] - if oldspecification and oldspecification.filename == storedname then -- double check for out of sync - local oldmodification = oldspecification.modification - if oldmodification == modification then - result = oldspecification - specifications[#specifications + 1] = result - else - end - else - end - elseif oldrejected[storedname] == modification then - result = false - end - end - if result == nil then - local lsuffix = lower(suffix) - local result, message = filters[lsuffix](completename) - if result then - if #result > 0 then - for r=1,#result do - local ok = check_name(data,result[r],storedname,modification,suffix,r-1) -- subfonts start at zero - -- if not ok then - -- nofskipped = nofskipped + 1 - -- end - end - else - local ok = check_name(data,result,storedname,modification,suffix) - -- if not ok then - -- nofskipped = nofskipped + 1 - -- end - end - if trace_warnings and message and message ~= "" then - report_names("warning when identifying %s font %a, %s",suffix,completename,message) - end - elseif trace_warnings then - nofskipped = nofskipped + 1 - report_names("error when identifying %s font %a, %s",suffix,completename,message or "unknown") - end - end - done[name] = true - end - logs.flush() -- a bit overkill for each font, maybe not needed here - end - local function traverse(what, method) - local list = filters.list - for n=1,#list do - local suffix = list[n] - local t = os.gettimeofday() -- use elapser - nofread, nofskipped, nofduplicates = 0, 0, 0 - suffix = lower(suffix) - report_names("identifying %s font files with suffix %a",what,suffix) - method(suffix) - suffix = upper(suffix) - report_names("identifying %s font files with suffix %a",what,suffix) - method(suffix) - totalnofread, totalnofskipped, totalnofduplicates = totalnofread + nofread, totalnofskipped + nofskipped, totalnofduplicates + nofduplicates - local elapsed = os.gettimeofday() - t - report_names("%s %s files identified, %s skipped, %s duplicates, %s hash entries added, runtime %0.3f seconds",nofread,what,nofskipped,nofduplicates,nofread-nofskipped,elapsed) - end - logs.flush() - end - -- problem .. this will not take care of duplicates - local function withtree(suffix) - resolvers.dowithfilesintree(".*%." .. suffix .. "$", function(method,root,path,name) - if method == "file" or method == "tree" then - local completename = root .."/" .. path .. "/" .. name - completename = resolveprefix(completename) -- no shortcut - identify(completename,name,suffix,name) - return true - end - end, function(blobtype,blobpath,pattern) - blobpath = resolveprefix(blobpath) -- no shortcut - report_names("scanning path %a for %s files",blobpath,suffix) - end, function(blobtype,blobpath,pattern,total,checked,done) - blobpath = resolveprefix(blobpath) -- no shortcut - report_names("%s entries found, %s %s files checked, %s okay",total,checked,suffix,done) - end) - end - local function withlsr(suffix) -- all trees - -- we do this only for a stupid names run, not used for context itself, - -- using the vars is too clumsy so we just stick to a full scan instead - local pathlist = resolvers.splitpath(resolvers.showpath("ls-R") or "") - walk_tree(pathlist,suffix,identify) - end - local function withsystem(suffix) -- OSFONTDIR cum suis - walk_tree(names.getpaths(trace),suffix,identify) - end - traverse("tree",withtree) -- TEXTREE only - if not usesystemfonts then - report_names("ignoring system fonts") - elseif texconfig.kpse_init then - traverse("lsr", withlsr) - else - traverse("system", withsystem) - end - data.statistics.readfiles = totalnofread - data.statistics.skippedfiles = totalnofskipped - data.statistics.duplicatefiles = totalnofduplicates -end - -local function addfilenames() - local data = names.data - local specifications = data.specifications - local indices = { } - local files = { } - for i=1,#specifications do - local fullname = specifications[i].filename - files[cleanfilename(fullname)] = fullname - indices[fullname] = i - end - data.files = files - data.indices = indices -end - -local function rejectclashes() -- just to be sure, so no explicit afm will be found then - local specifications = names.data.specifications - local used = { } - local okay = { } - local rejected = { } -- only keep modification - local o = 0 - for i=1,#specifications do - local s = specifications[i] - local f = s.fontname - if f then - local fnd = used[f] - local fnm = s.filename - if fnd then - if trace_warnings then - report_names("fontname %a clashes, %a rejected in favor of %a",f,fnm,fnd) - end - rejected[f] = s.modification - else - used[f] = fnm - o = o + 1 - okay[o] = s - end - else - o = o + 1 - okay[o] = s - end - end - local d = #specifications - #okay - if d > 0 then - report_names("%s files rejected due to clashes",d) - end - names.data.specifications = okay - names.data.rejected = rejected -end - -local function resetdata() - local mappings = { } - local fallbacks = { } - for _, k in next, filters.list do - mappings [k] = { } - fallbacks[k] = { } - end - names.data = { - version = names.version, - mappings = mappings, - fallbacks = fallbacks, - specifications = { }, - families = { }, - statistics = { }, - names = { }, - indices = { }, - rejected = { }, - datastate = resolvers.datastate(), - } -end - -function names.identify(force) - local starttime = os.gettimeofday() -- use elapser - resetdata() - analyzefiles(not force and names.readdata(names.basename)) - rejectclashes() - collectfamilies() - -- collectstatistics() - cleanupkeywords() - collecthashes() - checkduplicates() - addfilenames() - -- sorthashes() -- will be resorted when saved - collectstatistics() - report_names("total scan time %0.3f seconds",os.gettimeofday()-starttime) -end - -function names.is_permitted(name) - return containers.is_usable(names.cache, name) -end -function names.writedata(name,data) - containers.write(names.cache,name,data) -end -function names.readdata(name) - return containers.read(names.cache,name) -end - -function names.load(reload,force) - if not names.loaded then - if reload then - if names.is_permitted(names.basename) then - names.identify(force) - names.writedata(names.basename,names.data) - else - report_names("unable to access database cache") - end - names.saved = true - end - local data = names.readdata(names.basename) - names.data = data - if not names.saved then - if not data or not next(data) or not data.specifications or not next(data.specifications) then - names.load(true) - end - names.saved = true - end - if not data then - report_names("accessing the data table failed") - else - unpackreferences() - sorthashes() - end - names.loaded = true - end -end - -local function list_them(mapping,sorted,pattern,t,all) - if mapping[pattern] then - t[pattern] = mapping[pattern] - else - for k=1,#sorted do - local v = sorted[k] - if not t[v] and find(v,pattern) then - t[v] = mapping[v] - if not all then - return - end - end - end - end -end - -function names.list(pattern,reload,all) -- here? - names.load() -- todo reload - if names.loaded then - local t = { } - local data = names.data - if data then - local list = filters.list - local mappings = data.mappings - local sorted_mappings = data.sorted_mappings - local fallbacks = data.fallbacks - local sorted_fallbacks = data.sorted_fallbacks - for i=1,#list do - local format = list[i] - list_them(mappings[format],sorted_mappings[format],pattern,t,all) - if next(t) and not all then - return t - end - list_them(fallbacks[format],sorted_fallbacks[format],pattern,t,all) - if next(t) and not all then - return t - end - end - end - return t - end -end - -local reloaded = false - -local function is_reloaded() - if not reloaded then - local data = names.data - if autoreload then - local c_status = serialize(resolvers.datastate()) - local f_status = serialize(data.datastate) - if c_status == f_status then - if trace_names then - report_names("font database has matching configuration and file hashes") - end - return - else - report_names("font database has mismatching configuration and file hashes") - end - else - report_names("font database is regenerated (controlled by directive 'fonts.autoreload')") - end - names.loaded = false - reloaded = true - logs.flush() - names.load(true) - end -end - ---[[ldx-- -<p>The resolver also checks if the cached names are loaded. Being clever -here is for testing purposes only (it deals with names prefixed by an -encoding name).</p> ---ldx]]-- - -local function fuzzy(mapping,sorted,name,sub) - local condensed = gsub(name,"[^%a%d]","") - for k=1,#sorted do - local v = sorted[k] - if find(v,condensed) then - return mapping[v], v - end - end -end - --- we could cache a lookup .. maybe some day ... (only when auto loaded!) - -local function foundname(name,sub) -- sub is not used currently - local data = names.data - local mappings = data.mappings - local sorted_mappings = data.sorted_mappings - local fallbacks = data.fallbacks - local sorted_fallbacks = data.sorted_fallbacks - local list = filters.list - -- dilemma: we lookup in the order otf ttf ttc ... afm but now an otf fallback - -- can come after an afm match ... well, one should provide nice names anyway - -- and having two lists is not an option - for i=1,#list do - local l = list[i] - local found = mappings[l][name] - if found then - if trace_names then - report_names("resolved via direct name match: %a",name) - end - return found - end - end - for i=1,#list do - local l = list[i] - local found, fname = fuzzy(mappings[l],sorted_mappings[l],name,sub) - if found then - if trace_names then - report_names("resolved via fuzzy name match: %a onto %a",name,fname) - end - return found - end - end - for i=1,#list do - local l = list[i] - local found = fallbacks[l][name] - if found then - if trace_names then - report_names("resolved via direct fallback match: %a",name) - end - return found - end - end - for i=1,#list do - local l = list[i] - local found, fname = fuzzy(sorted_mappings[l],sorted_fallbacks[l],name,sub) - if found then - if trace_names then - report_names("resolved via fuzzy fallback match: %a onto %a",name,fname) - end - return found - end - end - if trace_names then - report_names("font with name %a cannot be found",name) - end -end - -function names.resolvedspecification(askedname,sub) - if askedname and askedname ~= "" and names.enabled then - askedname = cleanname(askedname) - names.load() - local found = foundname(askedname,sub) - if not found and is_reloaded() then - found = foundname(askedname,sub) - end - return found - end -end - -function names.resolve(askedname,sub) - local found = names.resolvedspecification(askedname,sub) - if found then - return found.filename, found.subfont and found.rawname - end -end - --- function names.getfilename(askedname,suffix) -- last resort, strip funny chars --- names.load() --- local files = names.data.files --- askedname = files and files[cleanfilename(askedname,suffix)] or "" --- if askedname == "" then --- return "" --- else -- never entered --- return resolvers.findbinfile(askedname,suffix) or "" --- end --- end - -function names.getfilename(askedname,suffix) -- last resort, strip funny chars - names.load() - local files = names.data.files - local cleanname = cleanfilename(askedname,suffix) - local found = files and files[cleanname] or "" - if found == "" and is_reloaded() then - files = names.data.files - found = files and files[cleanname] or "" - end - if found and found ~= "" then - return resolvers.findbinfile(found,suffix) or "" -- we still need to locate it - end -end - --- specified search - -local function s_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,family) - if family then - for i=1,#family do - local f = family[i] - if f and weight == f.weight and style == f.style and width == f.width and variant == f.variant then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end -local function m_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,families,sorted,strictname) - for i=1,#sorted do - local k = sorted[i] - local family = families[k] - for i=1,#family do - local f = family[i] - if not done[f] and weight == f.weight and style == f.style and width == f.width and variant == f.variant and find(f.fontname,strictname) then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end - -local function s_collect_weight_style_width(found,done,all,weight,style,width,family) - if family then - for i=1,#family do - local f = family[i] - if f and weight == f.weight and style == f.style and width == f.width then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end -local function m_collect_weight_style_width(found,done,all,weight,style,width,families,sorted,strictname) - for i=1,#sorted do - local k = sorted[i] - local family = families[k] - for i=1,#family do - local f = family[i] - if not done[f] and weight == f.weight and style == f.style and width == f.width and find(f.fontname,strictname) then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end - -local function s_collect_weight_style(found,done,all,weight,style,family) - if family then - for i=1,#family do local f = family[i] - if f and weight == f.weight and style == f.style then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end -local function m_collect_weight_style(found,done,all,weight,style,families,sorted,strictname) - for i=1,#sorted do - local k = sorted[i] - local family = families[k] - for i=1,#family do - local f = family[i] - if not done[f] and weight == f.weight and style == f.style and find(f.fontname,strictname) then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end - -local function s_collect_style_width(found,done,all,style,width,family) - if family then - for i=1,#family do local f = family[i] - if f and style == f.style and width == f.width then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end -local function m_collect_style_width(found,done,all,style,width,families,sorted,strictname) - for i=1,#sorted do - local k = sorted[i] - local family = families[k] - for i=1,#family do - local f = family[i] - if not done[f] and style == f.style and width == f.width and find(f.fontname,strictname) then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end - -local function s_collect_weight(found,done,all,weight,family) - if family then - for i=1,#family do local f = family[i] - if f and weight == f.weight then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end -local function m_collect_weight(found,done,all,weight,families,sorted,strictname) - for i=1,#sorted do - local k = sorted[i] - local family = families[k] - for i=1,#family do - local f = family[i] - if not done[f] and weight == f.weight and find(f.fontname,strictname) then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end - -local function s_collect_style(found,done,all,style,family) - if family then - for i=1,#family do local f = family[i] - if f and style == f.style then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end -local function m_collect_style(found,done,all,style,families,sorted,strictname) - for i=1,#sorted do - local k = sorted[i] - local family = families[k] - for i=1,#family do - local f = family[i] - if not done[f] and style == f.style and find(f.fontname,strictname) then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end - -local function s_collect_width(found,done,all,width,family) - if family then - for i=1,#family do local f = family[i] - if f and width == f.width then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end -local function m_collect_width(found,done,all,width,families,sorted,strictname) - for i=1,#sorted do - local k = sorted[i] - local family = families[k] - for i=1,#family do - local f = family[i] - if not done[f] and width == f.width and find(f.fontname,strictname) then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end - -local function s_collect(found,done,all,family) - if family then - for i=1,#family do local f = family[i] - if f then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end -local function m_collect(found,done,all,families,sorted,strictname) - for i=1,#sorted do - local k = sorted[i] - local family = families[k] - for i=1,#family do - local f = family[i] - if not done[f] and find(f.fontname,strictname) then - found[#found+1], done[f] = f, true - if not all then return end - end - end - end -end - -local function collect(stage,found,done,name,weight,style,width,variant,all) - local data = names.data - local families = data.families - local sorted = data.sorted_families - local strictname = "^".. name -- to be checked - local family = families[name] - if trace_names then - report_names("resolving name %a, weight %a, style %a, width %a, variant %a",name,weight,style,width,variant) - end - if weight and weight ~= "" then - if style and style ~= "" then - if width and width ~= "" then - if variant and variant ~= "" then - if trace_names then - report_names("resolving stage %s, name %a, weight %a, style %a, width %a, variant %a",stage,name,weight,style,width,variant) - end - s_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,family) - m_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,families,sorted,strictname) - else - if trace_names then - report_names("resolving stage %s, name %a, weight %a, style %a, width %a",stage,name,weight,style,width) - end - s_collect_weight_style_width(found,done,all,weight,style,width,family) - m_collect_weight_style_width(found,done,all,weight,style,width,families,sorted,strictname) - end - else - if trace_names then - report_names("resolving stage %s, name %a, weight %a, style %a",stage,name,weight,style) - end - s_collect_weight_style(found,done,all,weight,style,family) - m_collect_weight_style(found,done,all,weight,style,families,sorted,strictname) - end - else - if trace_names then - report_names("resolving stage %s, name %a, weight %a",stage,name,weight) - end - s_collect_weight(found,done,all,weight,family) - m_collect_weight(found,done,all,weight,families,sorted,strictname) - end - elseif style and style ~= "" then - if width and width ~= "" then - if trace_names then - report_names("resolving stage %s, name %a, style %a, width %a",stage,name,style,width) - end - s_collect_style_width(found,done,all,style,width,family) - m_collect_style_width(found,done,all,style,width,families,sorted,strictname) - else - if trace_names then - report_names("resolving stage %s, name %a, style %a",stage,name,style) - end - s_collect_style(found,done,all,style,family) - m_collect_style(found,done,all,style,families,sorted,strictname) - end - elseif width and width ~= "" then - if trace_names then - report_names("resolving stage %s, name %a, width %a",stage,name,width) - end - s_collect_width(found,done,all,width,family) - m_collect_width(found,done,all,width,families,sorted,strictname) - else - if trace_names then - report_names("resolving stage %s, name %a",stage,name) - end - s_collect(found,done,all,family) - m_collect(found,done,all,families,sorted,strictname) - end -end - -local function heuristic(name,weight,style,width,variant,all) -- todo: fallbacks - local found, done = { }, { } ---~ print(name,weight,style,width,variant) - weight, style, width, variant = weight or "normal", style or "normal", width or "normal", variant or "normal" - name = cleanname(name) - collect(1,found,done,name,weight,style,width,variant,all) - -- still needed ? - if #found == 0 and variant ~= "normal" then -- not weight - variant = "normal" - collect(4,found,done,name,weight,style,width,variant,all) - end - if #found == 0 and width ~= "normal" then - width = "normal" - collect(2,found,done,name,weight,style,width,variant,all) - end - if #found == 0 and weight ~= "normal" then -- not style - weight = "normal" - collect(3,found,done,name,weight,style,width,variant,all) - end - if #found == 0 and style ~= "normal" then -- not weight - style = "normal" - collect(4,found,done,name,weight,style,width,variant,all) - end - -- - local nf = #found - if trace_names then - if nf then - local t = { } - for i=1,nf do - t[i] = formatters["%a"](found[i].fontname) - end - report_names("name %a resolved to %s instances: % t",name,nf,t) - else - report_names("name %a unresolved",name) - end - end - if all then - return nf > 0 and found - else - return found[1] - end -end - -function names.specification(askedname,weight,style,width,variant,reload,all) - if askedname and askedname ~= "" and names.enabled then - askedname = cleanname(askedname) -- or cleanname - names.load(reload) - local found = heuristic(askedname,weight,style,width,variant,all) - if not found and is_reloaded() then - found = heuristic(askedname,weight,style,width,variant,all) - if not filename then - found = foundname(askedname) -- old method - end - end - return found - end -end - -function names.collect(askedname,weight,style,width,variant,reload,all) - if askedname and askedname ~= "" and names.enabled then - askedname = cleanname(askedname) -- or cleanname - names.load(reload) - local list = heuristic(askedname,weight,style,width,variant,true) - if not list or #list == 0 and is_reloaded() then - list = heuristic(askedname,weight,style,width,variant,true) - end - return list - end -end - -function names.collectspec(askedname,reload,all) - local name, weight, style, width, variant = names.splitspec(askedname) - return names.collect(name,weight,style,width,variant,reload,all) -end - -function names.resolvespec(askedname,sub) -- redefined later - local found = names.specification(names.splitspec(askedname)) - if found then - return found.filename, found.subfont and found.rawname - end -end - -function names.collectfiles(askedname,reload) -- no all - if askedname and askedname ~= "" and names.enabled then - askedname = cleanname(askedname) -- or cleanname - names.load(reload) - local list = { } - local specifications = names.data.specifications - for i=1,#specifications do - local s = specifications[i] - if find(cleanname(basename(s.filename)),askedname) then - list[#list+1] = s - end - end - return list - end -end - --- todo: --- --- blacklisted = { --- ["cmr10.ttf"] = "completely messed up", --- } - -function names.exists(name) - local found = false - local list = filters.list - for k=1,#list do - local v = list[k] - found = (findfile(name,v) or "") ~= "" - if found then - return found - end - end - return (findfile(name,"tfm") or "") ~= "" or (names.resolve(name) or "") ~= "" -end - -local lastlookups, lastpattern = { }, "" - --- function names.lookup(pattern,name,reload) -- todo: find --- if lastpattern ~= pattern then --- names.load(reload) --- local specifications = names.data.specifications --- local families = names.data.families --- local lookups = specifications --- if name then --- lookups = families[name] --- elseif not find(pattern,"=",1,true) then --- lookups = families[pattern] --- end --- if trace_names then --- report_names("starting with %s lookups for %a",#lookups,pattern) --- end --- if lookups then --- for key, value in gmatch(pattern,"([^=,]+)=([^=,]+)") do --- local t, n = { }, 0 --- if find(value,"*",1,true) then --- value = topattern(value) --- for i=1,#lookups do --- local s = lookups[i] --- if find(s[key],value) then --- n = n + 1 --- t[n] = lookups[i] --- end --- end --- else --- for i=1,#lookups do --- local s = lookups[i] --- if s[key] == value then --- n = n + 1 --- t[n] = lookups[i] --- end --- end --- end --- if trace_names then --- report_names("%s matches for key %a with value %a",#t,key,value) --- end --- lookups = t --- end --- end --- lastpattern = pattern --- lastlookups = lookups or { } --- end --- return #lastlookups --- end - -local function look_them_up(lookups,specification) - for key, value in next, specification do - local t, n = { }, 0 - if find(value,"*",1,true) then - value = topattern(value) - for i=1,#lookups do - local s = lookups[i] - if find(s[key],value) then - n = n + 1 - t[n] = lookups[i] - end - end - else - for i=1,#lookups do - local s = lookups[i] - if s[key] == value then - n = n + 1 - t[n] = lookups[i] - end - end - end - if trace_names then - report_names("%s matches for key %a with value %a",#t,key,value) - end - lookups = t - end - return lookups -end - -local function first_look(name,reload) - names.load(reload) - local data = names.data - local specifications = data.specifications - local families = data.families - if name then - return families[name] - else - return specifications - end -end - -function names.lookup(pattern,name,reload) -- todo: find - names.load(reload) - local data = names.data - local specifications = data.specifications - local families = data.families - local lookups = specifications - if name then - name = cleanname(name) - end - if type(pattern) == "table" then - local familyname = pattern.familyname - if familyname then - familyname = cleanname(familyname) - pattern.familyname = familyname - end - local lookups = first_look(name or familyname,reload) - if lookups then - if trace_names then - report_names("starting with %s lookups for '%T'",#lookups,pattern) - end - lookups = look_them_up(lookups,pattern) - end - lastpattern = false - lastlookups = lookups or { } - elseif lastpattern ~= pattern then - local lookups = first_look(name or (not find(pattern,"=",1,true) and pattern),reload) - if lookups then - if trace_names then - report_names("starting with %s lookups for %a",#lookups,pattern) - end - local specification = settings_to_hash(pattern) - local familyname = specification.familyname - if familyname then - familyname = cleanname(familyname) - specification.familyname = familyname - end - lookups = look_them_up(lookups,specification) - end - lastpattern = pattern - lastlookups = lookups or { } - end - return #lastlookups -end - -function names.getlookupkey(key,n) - local l = lastlookups[n or 1] - return (l and l[key]) or "" -end - -function names.noflookups() - return #lastlookups -end - -function names.getlookups(pattern,name,reload) - if pattern then - names.lookup(pattern,name,reload) - end - return lastlookups -end - --- The following is new ... watch the overload! - -local specifications = allocate() -names.specifications = specifications - --- files = { --- name = "antykwapoltawskiego", --- list = { --- ["AntPoltLtCond-Regular.otf"] = { --- -- name = "antykwapoltawskiego", --- style = "regular", --- weight = "light", --- width = "condensed", --- }, --- }, --- } - -function names.register(files) - if files then - local list, commonname = files.list, files.name - if list then - local n, m = 0, 0 - for filename, filespec in next, list do - local name = lower(filespec.name or commonname) - if name and name ~= "" then - local style = normalized_styles [lower(filespec.style or "normal")] - local width = normalized_widths [lower(filespec.width or "normal")] - local weight = normalized_weights [lower(filespec.weight or "normal")] - local variant = normalized_variants[lower(filespec.variant or "normal")] - local weights = specifications[name ] if not weights then weights = { } specifications[name ] = weights end - local styles = weights [weight] if not styles then styles = { } weights [weight] = styles end - local widths = styles [style ] if not widths then widths = { } styles [style ] = widths end - local variants = widths [width ] if not variants then variants = { } widths [width ] = variants end - variants[variant] = filename - n = n + 1 - else - m = m + 1 - end - end - if trace_specifications then - report_names("%s filenames registered, %s filenames rejected",n,m) - end - end - end -end - -function names.registered(name,weight,style,width,variant) - local ok = specifications[name] - ok = ok and (ok[(weight and weight ~= "" and weight ) or "normal"] or ok.normal) - ok = ok and (ok[(style and style ~= "" and style ) or "normal"] or ok.normal) - ok = ok and (ok[(width and width ~= "" and width ) or "normal"] or ok.normal) - ok = ok and (ok[(variant and variant ~= "" and variant) or "normal"] or ok.normal) - -- - -- todo: same fallbacks as with database - -- - if ok then - return { - filename = ok, - subname = "", - -- rawname = nil, - } - end -end - -function names.resolvespec(askedname,sub) -- overloads previous definition - local name, weight, style, width, variant = names.splitspec(askedname) - if trace_specifications then - report_names("resolving specification: %a to name=%s, weight=%s, style=%s, width=%s, variant=%s",askedname,name,weight,style,width,variant) - end - local found = names.registered(name,weight,style,width,variant) - if found and found.filename then - if trace_specifications then - report_names("resolved by registered names: %a to %s",askedname,found.filename) - end - return found.filename, found.subname, found.rawname - else - found = names.specification(name,weight,style,width,variant) - if found and found.filename then - if trace_specifications then - report_names("resolved by font database: %a to %s",askedname,found.filename) - end - return found.filename, found.subfont and found.rawname - end - end - if trace_specifications then - report_names("unresolved: %s",askedname) - end -end - --- We could generate typescripts with designsize info from the name database but --- it's not worth the trouble as font names remain a mess: for instance how do we --- idenfity a font? Names, families, subfamilies or whatever snippet can contain --- a number related to the design size and so we end up with fuzzy logic again. So, --- instead it's easier to make a few goody files. --- --- local hash = { } --- --- for i=1,#specifications do --- local s = specifications[i] --- local min = s.minsize or 0 --- local max = s.maxsize or 0 --- if min ~= 0 or max ~= 0 then --- -- the usual name mess: --- -- antykwa has modifiers so we need to take these into account, otherwise we get weird combinations --- -- ebgaramond has modifiers with the size encoded, so we need to strip this in order to recognized similar styles --- -- lm has 'slanted appended in some names so how to choose that one --- -- --- local modifier = string.gsub(s.modifiers or "normal","%d","") --- -- print funny modifier --- local instance = string.formatters["%s-%s-%s-%s-%s-%s"](s.familyname,s.width,s.style,s.weight,s.variant,modifier) --- local h = hash[instance] --- if not h then --- h = { } --- hash[instance] = h --- end --- size = string.formatters["%0.1fpt"]((min)/10) --- h[size] = s.filename --- end --- end --- --- local newhash = { } --- --- for k, v in next, hash do --- if next(v,next(v)) then --- -- local instance = string.match(k,"(.+)%-.+%-.+%-.+$") --- local instance = string.match(k,"(.+)%-.+%-.+$") --- local instance = string.gsub(instance,"%-normal$","") --- if not newhash[instance] then --- newhash[instance] = v --- end --- end --- end --- --- inspect(newhash) |