From 13ec4b540e0d46c97fd7b089e0b7413da81e0a9f Mon Sep 17 00:00:00 2001 From: Marius Date: Sun, 19 May 2013 20:40:34 +0300 Subject: beta 2013.05.19 19:27 --- tex/context/base/font-chk.lua | 718 +++++++++++++++++++++--------------------- 1 file changed, 359 insertions(+), 359 deletions(-) (limited to 'tex/context/base/font-chk.lua') diff --git a/tex/context/base/font-chk.lua b/tex/context/base/font-chk.lua index 1b89366fd..9e420744a 100644 --- a/tex/context/base/font-chk.lua +++ b/tex/context/base/font-chk.lua @@ -1,359 +1,359 @@ -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 format = string.format -local bpfactor = number.dimenfactors.bp - -local report_fonts = logs.reporter("fonts","checking") - -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 addprivate = fonts.helpers.addprivate -local hasprivate = fonts.helpers.hasprivate -local getprivatenode = fonts.helpers.getprivatenode - -local otffeatures = fonts.constructors.newfeatures("otf") -local registerotffeature = otffeatures.register - -local is_character = characters.is_character -local chardata = characters.data - -local tasks = nodes.tasks -local enableaction = tasks.enableaction -local disableaction = tasks.disableaction - -local glyph_code = nodes.nodecodes.glyph -local traverse_id = node.traverse_id -local remove_node = nodes.remove -local insert_node_after = node.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 %U in font %a with id %a: %s",char,tfmdata.properties.fullname,font,message) - category[char] = true - end -end - -fonts.loggers.onetimemessage = onetimemessage - -local mapping = { -- 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 = { - { - 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 = { - { 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 package = "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 = format("placeholder %s %s",name,tag) - if not hasprivate(tfmdata,privatename) then - local hash = format("%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", "pdf: " .. format(package,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(fonts.helpers.getprivatenode(fontdata[id],name)) -end - -function checkers.missing(head) - local lastfont, characters, found = nil, nil, nil - for n in traverse_id(glyph_code,head) do -- faster than while loop so we delay removal - local font = n.font - local char = n.char - if font ~= lastfont then - characters = fontcharacters[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 n = found[i] - local font = n.font - local char = n.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 - -- bad, we don't have them at the tex end - local fake = getprivatenode(tfmdata,fakechar) - insert_node_after(head,n,fake) - head = remove_node(head,n,true) - else - -- good, we have \definefontfeature[default][default][missing=yes] - n.char = p - end - end - else - -- maye write a report to the log - end - return head, false -end - -local relevant = { "missing (will be deleted)", "missing (will be flagged)", "missing" } - -function checkers.getmissing(id) - if id then - local list = checkers.getmissing(font.current()) - if list then - local _, list = next(checkers.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 - -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) - -function commands.checkcharactersinfont() - enableaction("processors","fonts.checkers.missing") - tracked = true -end - -function commands.removemissingcharacters() - enableaction("processors","fonts.checkers.missing") - action = "remove" - tracked = true -end - -function commands.replacemissingcharacters() - enableaction("processors","fonts.checkers.missing") - action = "replace" - otffeatures.defaults.missing = true - tracked = true -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) +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 format = string.format +local bpfactor = number.dimenfactors.bp + +local report_fonts = logs.reporter("fonts","checking") + +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 addprivate = fonts.helpers.addprivate +local hasprivate = fonts.helpers.hasprivate +local getprivatenode = fonts.helpers.getprivatenode + +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register + +local is_character = characters.is_character +local chardata = characters.data + +local tasks = nodes.tasks +local enableaction = tasks.enableaction +local disableaction = tasks.disableaction + +local glyph_code = nodes.nodecodes.glyph +local traverse_id = node.traverse_id +local remove_node = nodes.remove +local insert_node_after = node.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 %U in font %a with id %a: %s",char,tfmdata.properties.fullname,font,message) + category[char] = true + end +end + +fonts.loggers.onetimemessage = onetimemessage + +local mapping = { -- 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 = { + { + 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 = { + { 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 package = "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 = format("placeholder %s %s",name,tag) + if not hasprivate(tfmdata,privatename) then + local hash = format("%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", "pdf: " .. format(package,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(fonts.helpers.getprivatenode(fontdata[id],name)) +end + +function checkers.missing(head) + local lastfont, characters, found = nil, nil, nil + for n in traverse_id(glyph_code,head) do -- faster than while loop so we delay removal + local font = n.font + local char = n.char + if font ~= lastfont then + characters = fontcharacters[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 n = found[i] + local font = n.font + local char = n.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 + -- bad, we don't have them at the tex end + local fake = getprivatenode(tfmdata,fakechar) + insert_node_after(head,n,fake) + head = remove_node(head,n,true) + else + -- good, we have \definefontfeature[default][default][missing=yes] + n.char = p + end + end + else + -- maye write a report to the log + end + return head, false +end + +local relevant = { "missing (will be deleted)", "missing (will be flagged)", "missing" } + +function checkers.getmissing(id) + if id then + local list = checkers.getmissing(font.current()) + if list then + local _, list = next(checkers.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 + +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) + +function commands.checkcharactersinfont() + enableaction("processors","fonts.checkers.missing") + tracked = true +end + +function commands.removemissingcharacters() + enableaction("processors","fonts.checkers.missing") + action = "remove" + tracked = true +end + +function commands.replacemissingcharacters() + enableaction("processors","fonts.checkers.missing") + action = "replace" + otffeatures.defaults.missing = true + tracked = true +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) -- cgit v1.2.3