From fc4466b32ed330a956ac603b00fd145524cff49a Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Fri, 22 Apr 2016 22:14:39 +0000 Subject: context (22apr16) git-svn-id: svn://tug.org/texlive/trunk@40691 c570f23f-e606-0410-a88d-b1316a301751 --- .../texmf-dist/tex/context/base/mkiv/font-chk.lua | 462 +++++++++++++++++++++ 1 file changed, 462 insertions(+) create mode 100644 Master/texmf-dist/tex/context/base/mkiv/font-chk.lua (limited to 'Master/texmf-dist/tex/context/base/mkiv/font-chk.lua') diff --git a/Master/texmf-dist/tex/context/base/mkiv/font-chk.lua b/Master/texmf-dist/tex/context/base/mkiv/font-chk.lua new file mode 100644 index 00000000000..5b1ad992070 --- /dev/null +++ b/Master/texmf-dist/tex/context/base/mkiv/font-chk.lua @@ -0,0 +1,462 @@ +if not modules then modules = { } end modules ['font-chk'] = { + 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" +} + +-- possible optimization: delayed initialization of vectors +-- move to the nodes namespace + +local next = next + +local formatters = string.formatters +local bpfactor = number.dimenfactors.bp +local fastcopy = table.fastcopy + +local report_fonts = logs.reporter("fonts","checking") + +local allocate = utilities.storage.allocate + +local fonts = fonts + +fonts.checkers = fonts.checkers or { } +local checkers = fonts.checkers + +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local fontcharacters = fonthashes.characters + +local helpers = fonts.helpers + +local addprivate = helpers.addprivate +local hasprivate = helpers.hasprivate +local getprivatenode = helpers.getprivatenode + +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register +local afmfeatures = fonts.constructors.newfeatures("afm") +local registerafmfeature = afmfeatures.register + +local is_character = characters.is_character +local chardata = characters.data + +local tasks = nodes.tasks +local enableaction = tasks.enableaction +local disableaction = tasks.disableaction + +local implement = interfaces.implement + +local glyph_code = nodes.nodecodes.glyph + +local nuts = nodes.nuts +local tonut = nuts.tonut +local tonode = nuts.tonode + +local getfont = nuts.getfont +local getchar = nuts.getchar + +local setfield = nuts.setfield +local setchar = nuts.setchar + +local traverse_id = nuts.traverse_id +local remove_node = nuts.remove +local insert_node_after = nuts.insert_after + +-- maybe in fonts namespace +-- deletion can be option + +local action = false + +-- to tfmdata.properties ? + +local function onetimemessage(font,char,message) -- char == false returns table + local tfmdata = fontdata[font] + local shared = tfmdata.shared + local messages = shared.messages + if not messages then + messages = { } + shared.messages = messages + end + local category = messages[message] + if not category then + category = { } + messages[message] = category + end + if char == false then + return table.sortedkeys(category) + elseif not category[char] then + report_fonts("char %C in font %a with id %a: %s",char,tfmdata.properties.fullname,font,message) + category[char] = true + end +end + +fonts.loggers.onetimemessage = onetimemessage + +local mapping = allocate { -- this is just an experiment to illustrate some principles elsewhere + lu = "placeholder uppercase red", + ll = "placeholder lowercase red", + lt = "placeholder uppercase red", + lm = "placeholder lowercase red", + lo = "placeholder lowercase red", + mn = "placeholder mark green", + mc = "placeholder mark green", + me = "placeholder mark green", + nd = "placeholder lowercase blue", + nl = "placeholder lowercase blue", + no = "placeholder lowercase blue", + pc = "placeholder punctuation cyan", + pd = "placeholder punctuation cyan", + ps = "placeholder punctuation cyan", + pe = "placeholder punctuation cyan", + pi = "placeholder punctuation cyan", + pf = "placeholder punctuation cyan", + po = "placeholder punctuation cyan", + sm = "placeholder lowercase magenta", + sc = "placeholder lowercase yellow", + sk = "placeholder lowercase yellow", + so = "placeholder lowercase yellow", +} + +table.setmetatableindex(mapping, + function(t,k) + v = "placeholder unknown gray" + t[k] = v + return v + end +) + +local fakes = allocate { + { + name = "lowercase", + code = ".025 -.175 m .425 -.175 l .425 .525 l .025 .525 l .025 -.175 l .025 0 l .425 0 l .025 -.175 m h S", + width = .45, + height = .55, + depth = .20, + }, + { + name = "uppercase", + code = ".025 -.225 m .625 -.225 l .625 .675 l .025 .675 l .025 -.225 l .025 0 l .625 0 l .025 -.225 m h S", + width = .65, + height = .70, + depth = .25, + }, + { + name = "mark", + code = ".025 .475 m .125 .475 l .125 .675 l .025 .675 l .025 .475 l h B", + width = .15, + height = .70, + depth = -.50, + }, + { + name = "punctuation", + code = ".025 -.175 m .125 -.175 l .125 .525 l .025 .525 l .025 -.175 l h B", + width = .15, + height = .55, + depth = .20, + }, + { + name = "unknown", + code = ".025 0 m .425 0 l .425 .175 l .025 .175 l .025 0 l h B", + width = .45, + height = .20, + depth = 0, + }, +} + +local variants = allocate { + { tag = "gray", r = .6, g = .6, b = .6 }, + { tag = "red", r = .6, g = 0, b = 0 }, + { tag = "green", r = 0, g = .6, b = 0 }, + { tag = "blue", r = 0, g = 0, b = .6 }, + { tag = "cyan", r = 0, g = .6, b = .6 }, + { tag = "magenta", r = .6, g = 0, b = .6 }, + { tag = "yellow", r = .6, g = .6, b = 0 }, +} + +local pdf_blob = "pdf: q %0.6F 0 0 %0.6F 0 0 cm %s %s %s rg %s %s %s RG 10 M 1 j 1 J 0.05 w %s Q" + +local cache = { } -- saves some tables but not that impressive + +local function addmissingsymbols(tfmdata) -- we can have an alternative with rules + local characters = tfmdata.characters + local size = tfmdata.parameters.size + local privates = tfmdata.properties.privates + local scale = size * bpfactor + for i=1,#variants do + local v = variants[i] + local tag, r, g, b = v.tag, v.r, v.g, v.b + for i =1, #fakes do + local fake = fakes[i] + local name = fake.name + local privatename = formatters["placeholder %s %s"](name,tag) + if not hasprivate(tfmdata,privatename) then + local hash = formatters["%s_%s_%s_%s_%s_%s"](name,tag,r,g,b,size) + local char = cache[hash] + if not char then + char = { + width = size*fake.width, + height = size*fake.height, + depth = size*fake.depth, + -- bah .. low level pdf ... should be a rule or plugged in + commands = { { "special", formatters[pdf_blob](scale,scale,r,g,b,r,g,b,fake.code) } } + } + cache[hash] = char + end + addprivate(tfmdata, privatename, char) + end + end + end +end + +registerotffeature { + name = "missing", + description = "missing symbols", + manipulators = { + base = addmissingsymbols, + node = addmissingsymbols, + } +} + +fonts.loggers.add_placeholders = function(id) addmissingsymbols(fontdata[id or true]) end +fonts.loggers.category_to_placeholder = mapping + +function commands.getplaceholderchar(name) + local id = font.current() + addmissingsymbols(fontdata[id]) + context(helpers.getprivatenode(fontdata[id],name)) +end + +local function placeholder(font,char) + local tfmdata = fontdata[font] + local properties = tfmdata.properties + local privates = properties.privates + local category = chardata[char].category + local fakechar = mapping[category] + local p = privates and privates[fakechar] + if not p then + addmissingsymbols(tfmdata) + p = properties.privates[fakechar] + end + if properties.lateprivates then + -- frozen already + return "node", getprivatenode(tfmdata,fakechar) + else + -- good, we have \definefontfeature[default][default][missing=yes] + return "char", p + end +end + +checkers.placeholder = placeholder + +function checkers.missing(head) + local lastfont, characters, found = nil, nil, nil + head = tonut(head) + for n in traverse_id(glyph_code,head) do -- faster than while loop so we delay removal + local font = getfont(n) + local char = getchar(n) + if font ~= lastfont then + characters = fontcharacters[font] + lastfont = font + end + if not characters[char] and is_character[chardata[char].category] then + if action == "remove" then + onetimemessage(font,char,"missing (will be deleted)") + elseif action == "replace" then + onetimemessage(font,char,"missing (will be flagged)") + else + onetimemessage(font,char,"missing") + end + if not found then + found = { n } + else + found[#found+1] = n + end + end + end + if not found then + -- all well + elseif action == "remove" then + for i=1,#found do + head = remove_node(head,found[i],true) + end + elseif action == "replace" then + for i=1,#found do + local node = found[i] + local kind, char = placeholder(getfont(node),getchar(node)) + if kind == "node" then + insert_node_after(head,node,tonut(char)) + head = remove_node(head,node,true) + elseif kind == "char" then + setchar(node,char) + else + -- error + end + end + else + -- maye write a report to the log + end + return tonode(head), false +end + +local relevant = { + "missing (will be deleted)", + "missing (will be flagged)", + "missing" +} + +local function getmissing(id) + if id then + local list = getmissing(font.current()) + if list then + local _, list = next(getmissing(font.current())) + return list + else + return { } + end + else + local t = { } + for id, d in next, fontdata do + local shared = d.shared + local messages = shared.messages + if messages then + local tf = t[d.properties.filename] or { } + for i=1,#relevant do + local tm = messages[relevant[i]] + if tm then + tf = table.merged(tf,tm) + end + end + if next(tf) then + t[d.properties.filename] = tf + end + end + end + for k, v in next, t do + t[k] = table.sortedkeys(v) + end + return t + end +end + +checkers.getmissing = getmissing + +local tracked = false + +trackers.register("fonts.missing", function(v) + if v then + enableaction("processors","fonts.checkers.missing") + tracked = true + else + disableaction("processors","fonts.checkers.missing") + end + if v == "replace" then + otffeatures.defaults.missing = true + end + action = v +end) + +local report_characters = logs.reporter("fonts","characters") +local report_character = logs.reporter("missing") + +local logsnewline = logs.newline +local logspushtarget = logs.pushtarget +local logspoptarget = logs.poptarget + +luatex.registerstopactions(function() + if tracked then + local collected = checkers.getmissing() + if next(collected) then + logspushtarget("logfile") + for filename, list in table.sortedhash(collected) do + logsnewline() + report_characters("start missing characters: %s",filename) + logsnewline() + for i=1,#list do + local u = list[i] + report_character("%U %c %s",u,u,chardata[u].description) + end + logsnewline() + report_characters("stop missing characters") + logsnewline() + end + logspoptarget() + end + end +end) + +-- for the moment here + +local function expandglyph(characters,index,done) + done = done or { } + if not done[index] then + local data = characters[index] + if data then + done[index] = true + local d = fastcopy(data) + local n = d.next + if n then + d.next = expandglyph(characters,n,done) + end + local h = d.horiz_variants + if h then + for i=1,#h do + h[i].glyph = expandglyph(characters,h[i].glyph,done) + end + end + local v = d.vert_variants + if v then + for i=1,#v do + v[i].glyph = expandglyph(characters,v[i].glyph,done) + end + end + return d + end + end +end + +helpers.expandglyph = expandglyph + +-- should not be needed as we add .notdef in the engine + +local dummyzero = { + -- width = 0, + -- height = 0, + -- depth = 0, + commands = { { "special", "" } }, +} + +local function adddummysymbols(tfmdata,...) + local characters = tfmdata.characters + if not characters[0] then + characters[0] = dummyzero + end + -- if not characters[1] then + -- characters[1] = dummyzero -- test only + -- end +end + +registerotffeature { + name = "dummies", + description = "dummy symbols", + default = true, + manipulators = { + base = adddummysymbols, + node = adddummysymbols, + } +} + +registerafmfeature { + name = "dummies", + description = "dummy symbols", + default = true, + manipulators = { + base = adddummysymbols, + node = adddummysymbols, + } +} + +-- callback.register("char_exists",function(f,c) -- to slow anyway as called often so we should flag in tfmdata +-- return true +-- end) -- cgit v1.2.3