diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkxl/typo-itc.lmt')
-rw-r--r-- | Master/texmf-dist/tex/context/base/mkxl/typo-itc.lmt | 703 |
1 files changed, 703 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-itc.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-itc.lmt new file mode 100644 index 00000000000..4350053b367 --- /dev/null +++ b/Master/texmf-dist/tex/context/base/mkxl/typo-itc.lmt @@ -0,0 +1,703 @@ +if not modules then modules = { } end modules ['typo-itc'] = { + version = 1.001, + comment = "companion to typo-itc.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local tonumber = tonumber + +local trace_italics = false trackers.register("typesetters.italics", function(v) trace_italics = v end) + +local report_italics = logs.reporter("nodes","italics") + +local threshold = 0.5 trackers.register("typesetters.threshold", function(v) threshold = v == true and 0.5 or tonumber(v) end) + +typesetters.italics = typesetters.italics or { } +local italics = typesetters.italics + +local nodecodes = nodes.nodecodes +local glyph_code = nodecodes.glyph +local kern_code = nodecodes.kern +local glue_code = nodecodes.glue +local disc_code = nodecodes.disc +local math_code = nodecodes.math + +local enableaction = nodes.tasks.enableaction + +local nuts = nodes.nuts +local nodepool = nuts.pool + +local getprev = nuts.getprev +local getnext = nuts.getnext +local getid = nuts.getid +local getchar = nuts.getchar +local getdisc = nuts.getdisc +local getattr = nuts.getattr +local setattr = nuts.setattr +local getattrlist = nuts.getattrlist +local setattrlist = nuts.setattrlist +local setdisc = nuts.setdisc +local isglyph = nuts.isglyph +local setkern = nuts.setkern +local getkern = nuts.getkern +local getheight = nuts.getheight + +local insert_node_after = nuts.insert_after +local remove_node = nuts.remove +local end_of_math = nuts.end_of_math + +local texgetattribute = tex.getattribute +local texsetattribute = tex.setattribute +local a_italics = attributes.private("italics") +local a_mathitalics = attributes.private("mathitalics") + +local unsetvalue = attributes.unsetvalue + +local new_correction_kern = nodepool.italickern +local new_correction_glue = nodepool.glue + +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local italicsdata = fonthashes.italics +local exheights = fonthashes.exheights +local chardata = fonthashes.characters + +local is_punctuation = characters.is_punctuation + +local implement = interfaces.implement + +local forcedvariant = false + +function typesetters.italics.forcevariant(variant) + forcedvariant = variant +end + +-- We use the same key as the tex font handler. So, if a valua has already be set, we +-- use that one. + +local function setitalicinfont(font,char) + local tfmdata = fontdata[font] + local character = tfmdata.characters[char] + if character then + local italic = character.italic + if not italic then + local autoitalicamount = tfmdata.properties.autoitalicamount or 0 + if autoitalicamount ~= 0 then + local description = tfmdata.descriptions[char] + if description then + italic = description.italic + if not italic then + local boundingbox = description.boundingbox + italic = boundingbox[3] - description.width + autoitalicamount + if italic < 0 then -- < 0 indicates no overshoot or a very small auto italic + italic = 0 + end + end + if italic ~= 0 then + italic = italic * tfmdata.parameters.hfactor + end + end + end + if trace_italics then + report_italics("setting italic correction of %C of font %a to %p",char,font,italic) + end + if not italic then + italic = 0 + end + character.italic = italic + end + return italic + else + return 0 + end +end + +-- todo: clear attribute + +local function okay(data,current,font,prevchar,previtalic,char,what) + if data then + if trace_italics then + report_italics("ignoring %p between %s italic %C and italic %C",previtalic,what,prevchar,char) + end + return false + end + if threshold then + -- if getid(current) == glyph_code then + while current and getid(current) ~= glyph_code do + current = getprev(current) + end + if current then + local ht = getheight(current) + local ex = exheights[font] + local th = threshold * ex + if ht <= th then + if trace_italics then + report_italics("ignoring correction between %s italic %C and regular %C, height %p less than threshold %p",prevchar,what,char,ht,th) + end + return false + end + else + -- maybe backtrack to glyph + end + end + if trace_italics then + report_italics("inserting %p between %s italic %C and regular %C",previtalic,what,prevchar,char) + end + return true +end + +-- maybe: with_attributes(current,n) : +-- +-- local function correction_kern(kern,n) +-- return with_attributes(new_correction_kern(kern),n) +-- end + +local function correction_kern(kern,n) + local k = new_correction_kern(kern) + if n then +-- local a = getattrlist(n) +-- if a then -- maybe not +-- setattrlist(k,a) -- can be a marked content (border case) +-- end + setattrlist(k,n) -- can be a marked content (border case) + end + return k +end + +local function correction_glue(glue,n) + local g = new_correction_glue(glue) + if n then +-- local a = getattrlist(n) +-- if a then -- maybe not +-- setattrlist(g,a) -- can be a marked content (border case) +-- end + setattrlist(g,n) -- can be a marked content (border case) + end + return g +end + +local mathokay = false +local textokay = false +local enablemath = false +local enabletext = false + +local function domath(head,current) + current = end_of_math(current) + local next = getnext(current) + if next then + local char, id = isglyph(next) + if char then + -- we can have an old font where italic correction has been applied + -- or a new one where it hasn't been done + local kern = getprev(current) + if kern and getid(kern) == kern_code then + local glyph = getprev(kern) + if glyph and getid(glyph) == glyph_code then + -- [math: <glyph><kern>]<glyph> : we remove the correction when we have + -- punctuation + if is_punctuation[char] then + local a = getattr(glyph,a_mathitalics) + if a and (a < 100 or a > 100) then + if a > 100 then + a = a - 100 + else + a = a + 100 + end + local i = getkern(kern) + local c, f = isglyph(glyph) + if getheight(next) < 1.25*exheights[f] then + if i == 0 then + if trace_italics then + report_italics("%s italic %p between math %C and punctuation %C","ignoring",i,c,char) + end + else + if trace_italics then + report_italics("%s italic between math %C and punctuation %C","removing",i,c,char) + end + setkern(kern,0) -- or maybe a small value or half the ic + end + elseif i == 0 then + local d = chardata[f][c] + local i = d.italic + if i == 0 then + if trace_italics then + report_italics("%s italic %p between math %C and punctuation %C","ignoring",i,c,char) + end + else + setkern(kern,i) + if trace_italics then + report_italics("%s italic %p between math %C and punctuation %C","setting",i,c,char) + end + end + elseif trace_italics then + report_italics("%s italic %p between math %C and punctuation %C","keeping",k,c,char) + end + end + end + end + else + local glyph = kern + if glyph and getid(glyph) == glyph_code then + -- [math: <glyph>]<glyph> : we add the correction when we have + -- no punctuation + if not is_punctuation[char] then + local a = getattr(glyph,a_mathitalics) + if a and (a < 100 or a > 100) then + if a > 100 then + a = a - 100 + else + a = a + 100 + end + if trace_italics then + report_italics("%s italic %p between math %C and non punctuation %C","adding",a,getchar(glyph),char) + end + insert_node_after(head,glyph,correction_kern(a,glyph)) + end + end + end + end + end + end + return current +end + +local function mathhandler(head) + local current = head + while current do + if getid(current) == math_code then + current = domath(head,current) + end + current = getnext(current) + end + return head +end + +local function texthandler(head) + + local prev = nil + local prevchar = nil + local prevhead = head + local previtalic = 0 + local previnserted = nil + + local pre = nil + local pretail = nil + + local post = nil + local posttail = nil + local postchar = nil + local posthead = nil + local postitalic = 0 + local postinserted = nil + + local replace = nil + local replacetail = nil + local replacechar = nil + local replacehead = nil + local replaceitalic = 0 + local replaceinserted = nil + + local current = prevhead + local lastfont = nil + local lastattr = nil + + while current do + local char, id = isglyph(current) + if char then + local font = id + local data = italicsdata[font] + if font ~= lastfont then + if previtalic ~= 0 then + if okay(data,current,font,prevchar,previtalic,char,"glyph") then + insert_node_after(prevhead,prev,correction_kern(previtalic,current)) + end + elseif previnserted and data then + if trace_italics then + report_italics("deleting last correction before %s %C",char,"glyph") + end + remove_node(prevhead,previnserted,true) + else + -- + if replaceitalic ~= 0 then + if okay(data,replace,font,replacechar,replaceitalic,char,"replace") then + insert_node_after(replacehead,replace,correction_kern(replaceitalic,current)) + end + replaceitalic = 0 + elseif replaceinserted and data then + if trace_italics then + report_italics("deleting last correction before %s %C","replace",char) + end + remove_node(replacehead,replaceinserted,true) + end + -- + if postitalic ~= 0 then + if okay(data,post,font,postchar,postitalic,char,"post") then + insert_node_after(posthead,post,correction_kern(postitalic,current)) + end + postitalic = 0 + elseif postinserted and data then + if trace_italics then + report_italics("deleting last correction before %s %C","post",char) + end + remove_node(posthead,postinserted,true) + end + end + -- + lastfont = font + end + if data then + local attr = forcedvariant or getattr(current,a_italics) + if attr and attr > 0 then + local cd = data[char] + if not cd then + -- this really can happen + previtalic = 0 + else + previtalic = cd.italic + if not previtalic then + previtalic = setitalicinfont(font,char) -- calculated once + -- previtalic = 0 + end + if previtalic ~= 0 then + lastfont = font + lastattr = attr + prev = current + -- prevhead = head + prevchar = char + end + end + else + previtalic = 0 + end + else + previtalic = 0 + end + previnserted = nil + replaceinserted = nil + postinserted = nil + elseif id == disc_code then + previnserted = nil + previtalic = 0 + replaceinserted = nil + replaceitalic = 0 + postinserted = nil + postitalic = 0 + updated = false + replacefont = nil + postfont = nil + pre, post, replace, pretail, posttail, replacetail = getdisc(current,true) + if replace then + local current = replacetail + while current do + local char, id = isglyph(current) + if char then + local font = id + if font ~= lastfont then + local data = italicsdata[font] + if data then + local attr = forcedvariant or getattr(current,a_italics) + if attr and attr > 0 then + local cd = data[char] + if not cd then + -- this really can happen + replaceitalic = 0 + else + replaceitalic = cd.italic + if not replaceitalic then + replaceitalic = setitalicinfont(font,char) -- calculated once + -- replaceitalic = 0 + end + if replaceitalic ~= 0 then + lastfont = font + lastattr = attr + replacechar = char + replacehead = replace + updated = true + end + end + end + end + replacefont = font + end + break + else + current = getprev(current) + end + end + end + if post then + local current = posttail + while current do + local char, id = isglyph(current) + if char then + local font = id + if font ~= lastfont then + local data = italicsdata[font] + if data then + local attr = forcedvariant or getattr(current,a_italics) + if attr and attr > 0 then + local cd = data[char] + if not cd then + -- this really can happen + -- postitalic = 0 + else + postitalic = cd.italic + if not postitalic then + postitalic = setitalicinfont(font,char) -- calculated once + -- postitalic = 0 + end + if postitalic ~= 0 then + lastfont = font + lastattr = attr + postchar = char + posthead = post + updated = true + end + end + end + end + postfont = font + end + break + else + current = getprev(current) + end + end + end + if replacefont or postfont then + lastfont = replacefont or postfont + end + if updated then + setdisc(current,pre,post,replace) + end + elseif id == kern_code then -- how about fontkern ? + previnserted = nil + previtalic = 0 + replaceinserted = nil + replaceitalic = 0 + postinserted = nil + postitalic = 0 + elseif id == glue_code then + if previtalic ~= 0 then + if trace_italics then + report_italics("inserting %p between %s italic %C and glue",previtalic,"glyph",prevchar) + end + previnserted = correction_glue(previtalic,current) -- maybe just add ? else problem with penalties + previtalic = 0 + insert_node_after(prevhead,prev,previnserted) + else + if replaceitalic ~= 0 then + if trace_italics then + report_italics("inserting %p between %s italic %C and glue",replaceitalic,"replace",replacechar) + end + replaceinserted = correction_kern(replaceitalic,current) -- needs to be a kern + replaceitalic = 0 + insert_node_after(replacehead,replace,replaceinserted) + end + if postitalic ~= 0 then + if trace_italics then + report_italics("inserting %p between %s italic %C and glue",postitalic,"post",postchar) + end + postinserted = correction_kern(postitalic,current) -- needs to be a kern + postitalic = 0 + insert_node_after(posthead,post,postinserted) + end + end + elseif id == math_code then + -- is this still needed ... the current engine implementation has been redone + previnserted = nil + previtalic = 0 + replaceinserted = nil + replaceitalic = 0 + postinserted = nil + postitalic = 0 + if mathokay then + current = domath(head,current) + else + current = end_of_math(current) + end + else + if previtalic ~= 0 then + if trace_italics then + report_italics("inserting %p between %s italic %C and whatever",previtalic,"glyph",prevchar) + end + insert_node_after(prevhead,prev,correction_kern(previtalic,current)) + previnserted = nil + previtalic = 0 + replaceinserted = nil + replaceitalic = 0 + postinserted = nil + postitalic = 0 + else + if replaceitalic ~= 0 then + if trace_italics then + report_italics("inserting %p between %s italic %C and whatever",replaceitalic,"replace",replacechar) + end + insert_node_after(replacehead,replace,correction_kern(replaceitalic,current)) + previnserted = nil + previtalic = 0 + replaceinserted = nil + replaceitalic = 0 + postinserted = nil + postitalic = 0 + end + if postitalic ~= 0 then + if trace_italics then + report_italics("inserting %p between %s italic %C and whatever",postitalic,"post",postchar) + end + insert_node_after(posthead,post,correction_kern(postitalic,current)) + previnserted = nil + previtalic = 0 + replaceinserted = nil + replaceitalic = 0 + postinserted = nil + postitalic = 0 + end + end + end + current = getnext(current) + end + if lastattr and lastattr > 1 then -- more control is needed here + if previtalic ~= 0 then + if trace_italics then + report_italics("inserting %p between %s italic %C and end of list",previtalic,"glyph",prevchar) + end + insert_node_after(prevhead,prev,correction_kern(previtalic,current)) + else + if replaceitalic ~= 0 then + if trace_italics then + report_italics("inserting %p between %s italic %C and end of list",replaceitalic,"replace",replacechar) + end + insert_node_after(replacehead,replace,correction_kern(replaceitalic,current)) + end + if postitalic ~= 0 then + if trace_italics then + report_italics("inserting %p between %s italic %C and end of list",postitalic,"post",postchar) + end + insert_node_after(posthead,post,correction_kern(postitalic,current)) + end + end + end + return head +end + +function italics.handler(head) + if textokay then + return texthandler(head) + elseif mathokay then + return mathhandler(head) + else + return head, false + end +end + +enabletext = function() + enableaction("processors","typesetters.italics.handler") + if trace_italics then + report_italics("enabling text/text italics") + end + enabletext = false + textokay = true +end + +enablemath = function() + enableaction("processors","typesetters.italics.handler") + if trace_italics then + report_italics("enabling math/text italics") + end + enablemath = false + mathokay = true +end + +function italics.enabletext() + if enabletext then + enabletext() + end +end + +function italics.enablemath() + if enablemath then + enablemath() + end +end + +function italics.set(n) + if enabletext then + enabletext() + end + if n == variables.reset then + texsetattribute(a_italics,unsetvalue) + else + texsetattribute(a_italics,tonumber(n) or unsetvalue) + end +end + +function italics.reset() + texsetattribute(a_italics,unsetvalue) +end + +implement { + name = "setitaliccorrection", + actions = italics.set, + arguments = "string" +} + +implement { + name = "resetitaliccorrection", + actions = italics.reset, +} + +local variables = interfaces.variables +local settings_to_hash = utilities.parsers.settings_to_hash + +local function setupitaliccorrection(option) -- no grouping ! + if enabletext then + enabletext() + end + local options = settings_to_hash(option) + local variant = unsetvalue + if options[variables.text] then + variant = 1 + elseif options[variables.always] then + variant = 2 + end + -- maybe also keywords for threshold + if options[variables.global] then + forcedvariant = variant + texsetattribute(a_italics,unsetvalue) + else + forcedvariant = false + texsetattribute(a_italics,variant) + end + if trace_italics then + report_italics("forcing %a, variant %a",forcedvariant or "-",variant ~= unsetvalue and variant) + end +end + +implement { + name = "setupitaliccorrection", + actions = setupitaliccorrection, + arguments = "string" +} + +-- for manuals: + +local stack = { } + +implement { + name = "pushitaliccorrection", + actions = function() + table.insert(stack,{forcedvariant, texgetattribute(a_italics) }) + end +} + +implement { + name = "popitaliccorrection", + actions = function() + local top = table.remove(stack) + forcedvariant = top[1] + texsetattribute(a_italics,top[2]) + end +} |