From e41d9b25d1b44b28206a44d6baf3635b014f3d87 Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Tue, 8 Dec 2020 19:15:53 +0100 Subject: 2020-12-08 18:43:00 --- tex/context/base/mkii/cont-new.mkii | 2 +- tex/context/base/mkii/context.mkii | 2 +- tex/context/base/mkiv/cont-new.mkiv | 2 +- tex/context/base/mkiv/context.mkiv | 2 +- tex/context/base/mkiv/mult-prm.lua | 1 + tex/context/base/mkiv/node-ext.lua | 30 -- tex/context/base/mkiv/node-ini.mkiv | 18 +- tex/context/base/mkiv/status-files.pdf | Bin 26088 -> 26079 bytes tex/context/base/mkiv/status-lua.pdf | Bin 254200 -> 254129 bytes tex/context/base/mkxl/cont-new.mkxl | 2 +- tex/context/base/mkxl/context.mkxl | 2 +- tex/context/base/mkxl/node-ini.mkxl | 4 +- tex/context/base/mkxl/node-pro.lmt | 219 ++++++++++ tex/context/base/mkxl/strc-num.mkxl | 14 +- tex/context/base/mkxl/syst-ini.mkxl | 4 + tex/context/base/mkxl/typo-ini.lmt | 11 + tex/context/base/mkxl/typo-ini.mkxl | 2 +- tex/context/base/mkxl/typo-itc.lmt | 703 +++++++++++++++++++++++++++++++++ tex/context/base/mkxl/typo-itc.mklx | 2 +- tex/context/base/mkxl/typo-rub.lmt | 397 +++++++++++++++++++ tex/context/base/mkxl/typo-rub.mkxl | 2 +- tex/context/base/mkxl/typo-sus.lmt | 308 +++++++++++++++ tex/context/base/mkxl/typo-tal.lmt | 439 ++++++++++++++++++++ tex/context/base/mkxl/typo-tal.mkxl | 2 +- 24 files changed, 2110 insertions(+), 58 deletions(-) delete mode 100644 tex/context/base/mkiv/node-ext.lua create mode 100644 tex/context/base/mkxl/node-pro.lmt create mode 100644 tex/context/base/mkxl/typo-ini.lmt create mode 100644 tex/context/base/mkxl/typo-itc.lmt create mode 100644 tex/context/base/mkxl/typo-rub.lmt create mode 100644 tex/context/base/mkxl/typo-sus.lmt create mode 100644 tex/context/base/mkxl/typo-tal.lmt (limited to 'tex/context') diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii index 96a3acfec..81663319d 100644 --- a/tex/context/base/mkii/cont-new.mkii +++ b/tex/context/base/mkii/cont-new.mkii @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2020.12.08 11:06} +\newcontextversion{2020.12.08 18:41} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/mkii/context.mkii b/tex/context/base/mkii/context.mkii index 864df0c99..4ca1964c8 100644 --- a/tex/context/base/mkii/context.mkii +++ b/tex/context/base/mkii/context.mkii @@ -20,7 +20,7 @@ %D your styles an modules. \edef\contextformat {\jobname} -\edef\contextversion{2020.12.08 11:06} +\edef\contextversion{2020.12.08 18:41} %D For those who want to use this: diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv index f60590c20..d2d71822b 100644 --- a/tex/context/base/mkiv/cont-new.mkiv +++ b/tex/context/base/mkiv/cont-new.mkiv @@ -13,7 +13,7 @@ % \normalend % uncomment this to get the real base runtime -\newcontextversion{2020.12.08 11:06} +\newcontextversion{2020.12.08 18:41} %D This file is loaded at runtime, thereby providing an excellent place for hacks, %D patches, extensions and new features. There can be local overloads in cont-loc diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv index 907146f68..40329734b 100644 --- a/tex/context/base/mkiv/context.mkiv +++ b/tex/context/base/mkiv/context.mkiv @@ -45,7 +45,7 @@ %D {YYYY.MM.DD HH:MM} format. \edef\contextformat {\jobname} -\edef\contextversion{2020.12.08 11:06} +\edef\contextversion{2020.12.08 18:41} %D Kind of special: diff --git a/tex/context/base/mkiv/mult-prm.lua b/tex/context/base/mkiv/mult-prm.lua index 97bc83ff1..38e13aa3e 100644 --- a/tex/context/base/mkiv/mult-prm.lua +++ b/tex/context/base/mkiv/mult-prm.lua @@ -350,6 +350,7 @@ return { "letprotected", "linedirection", "linepar", + "listcallbackmode", "localbrokenpenalty", "localcontrol", "localcontrolled", diff --git a/tex/context/base/mkiv/node-ext.lua b/tex/context/base/mkiv/node-ext.lua deleted file mode 100644 index 82ec04ee5..000000000 --- a/tex/context/base/mkiv/node-ext.lua +++ /dev/null @@ -1,30 +0,0 @@ -if not modules then modules = { } end modules ['node-ext'] = { - version = 1.001, - comment = "companion to node-ini.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - ---[[ldx-- -

Serializing nodes can be handy for tracing. Also, saving and -loading node lists can come in handy as soon we are going to -use external applications to process node lists.

---ldx]]-- - -function nodes.show(stack) --- logs.writer(table.serialize(stack)) -end - -function nodes.save(stack,name) -- *.ltn : luatex node file --- if name then --- file.savedata(name,table.serialize(stack)) --- else --- logs.writer(table.serialize(stack)) --- end -end - -function nodes.load(name) --- return file.loaddata(name) --- -- todo -end diff --git a/tex/context/base/mkiv/node-ini.mkiv b/tex/context/base/mkiv/node-ini.mkiv index 10a486e88..072cb319a 100644 --- a/tex/context/base/mkiv/node-ini.mkiv +++ b/tex/context/base/mkiv/node-ini.mkiv @@ -17,27 +17,27 @@ \newcount\filterstate \filterstate\plusone % hm, public -\registerctxluafile{node-ini}{autosuffix} +\registerctxluafile{node-ini}{} \registerctxluafile{node-met}{} -\registerctxluafile{node-nut}{autosuffix} +\registerctxluafile{node-nut}{} \registerctxluafile{node-res}{} %registerctxluafile{node-ppt}{} % experimental, not used so probably useless -\registerctxluafile{node-aux}{autosuffix} -\registerctxluafile{node-gcm}{autosuffix} +\registerctxluafile{node-aux}{} +\registerctxluafile{node-gcm}{} \registerctxluafile{node-tst}{} \registerctxluafile{node-tra}{} % we might split it off (module) -\registerctxluafile{node-snp}{autosuffix} +\registerctxluafile{node-snp}{} \registerctxluafile{node-tsk}{} -\registerctxluafile{node-tex}{autosuffix} +\registerctxluafile{node-tex}{} \registerctxluafile{node-dir}{} % experimental, not yet (and maybe never) used \registerctxluafile{node-pro}{} -\registerctxluafile{node-ser}{autosuffix} -\registerctxluafile{node-ext}{} +\registerctxluafile{node-ser}{} +%registerctxluafile{node-ext}{} \registerctxluafile{node-acc}{} % experimental %registerctxluafile{node-prp}{} % makes no sense (yet) \registerctxluafile{node-scn}{} \registerctxluafile{node-syn}{} -\registerctxluafile{node-par}{autosuffix} +\registerctxluafile{node-par}{} \newcount\c_node_tracers_show_box % box number diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf index 1f59f1008..da580c467 100644 Binary files a/tex/context/base/mkiv/status-files.pdf and b/tex/context/base/mkiv/status-files.pdf differ diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf index c39495b08..063329df8 100644 Binary files a/tex/context/base/mkiv/status-lua.pdf and b/tex/context/base/mkiv/status-lua.pdf differ diff --git a/tex/context/base/mkxl/cont-new.mkxl b/tex/context/base/mkxl/cont-new.mkxl index ac5966acb..2f27d306e 100644 --- a/tex/context/base/mkxl/cont-new.mkxl +++ b/tex/context/base/mkxl/cont-new.mkxl @@ -13,7 +13,7 @@ % \normalend % uncomment this to get the real base runtime -\newcontextversion{2020.12.08 11:06} +\newcontextversion{2020.12.08 18:41} %D This file is loaded at runtime, thereby providing an excellent place for hacks, %D patches, extensions and new features. There can be local overloads in cont-loc diff --git a/tex/context/base/mkxl/context.mkxl b/tex/context/base/mkxl/context.mkxl index 5dde967b5..f0b07a3ad 100644 --- a/tex/context/base/mkxl/context.mkxl +++ b/tex/context/base/mkxl/context.mkxl @@ -29,7 +29,7 @@ %D {YYYY.MM.DD HH:MM} format. \immutable\edef\contextformat {\jobname} -\immutable\edef\contextversion{2020.12.08 11:06} +\immutable\edef\contextversion{2020.12.08 18:41} %overloadmode 1 % check frozen / warning %overloadmode 2 % check frozen / error diff --git a/tex/context/base/mkxl/node-ini.mkxl b/tex/context/base/mkxl/node-ini.mkxl index abdff7b5b..4de4e00b4 100644 --- a/tex/context/base/mkxl/node-ini.mkxl +++ b/tex/context/base/mkxl/node-ini.mkxl @@ -31,9 +31,9 @@ \registerctxluafile{node-tsk}{} \registerctxluafile{node-tex}{autosuffix} \registerctxluafile{node-dir}{} % experimental, not yet (and maybe never) used -\registerctxluafile{node-pro}{} +\registerctxluafile{node-pro}{autosuffix} \registerctxluafile{node-ser}{autosuffix} -\registerctxluafile{node-ext}{} +%registerctxluafile{node-ext}{} \registerctxluafile{node-acc}{autosuffix} % experimental %registerctxluafile{node-prp}{} % makes no sense (yet) \registerctxluafile{node-scn}{autosuffix} diff --git a/tex/context/base/mkxl/node-pro.lmt b/tex/context/base/mkxl/node-pro.lmt new file mode 100644 index 000000000..e736f2b76 --- /dev/null +++ b/tex/context/base/mkxl/node-pro.lmt @@ -0,0 +1,219 @@ +if not modules then modules = { } end modules ['node-pro'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local trace_callbacks = false trackers .register("nodes.callbacks", function(v) trace_callbacks = v end) +local force_processors = false directives.register("nodes.processors.force", function(v) force_processors = v end) + +local report_nodes = logs.reporter("nodes","processors") + +local nodes = nodes +local tasks = nodes.tasks +local nuts = nodes.nuts +local tonut = nodes.tonut + +nodes.processors = nodes.processors or { } +local processors = nodes.processors + +-- vbox: grouptype: vbox vtop output split_off split_keep | box_type: exactly|aditional +-- hbox: grouptype: hbox adjusted_hbox(=hbox_in_vmode) | box_type: exactly|aditional + +local actions = tasks.actions("processors") + +do + + local isglyph = nuts.isglyph + local getnext = nuts.getnext + + local utfchar = utf.char + local concat = table.concat + + local n = 0 + + local function reconstruct(head) -- we probably have a better one + local t, n, h = { }, 0, head + while h do + n = n + 1 + local char, id = isglyph(h) + if char then -- todo: disc etc + t[n] = utfchar(char) + else + t[n] = "[]" + end + h = getnext(h) + end + return concat(t) + end + + function processors.tracer(what,head,groupcode,before,after,show) + if not groupcode then + groupcode = "unknown" + elseif groupcode == "" then + groupcode = "mvl" + end + n = n + 1 + if show then + report_nodes("%s: location %a, group %a, # before %a, # after %s, stream: %s",what,n,groupcode,before,after,reconstruct(head)) + else + report_nodes("%s: location %a, group %a, # before %a, # after %s",what,n,groupcode,before,after) + end + end + +end + +processors.enabled = true -- this will become a proper state (like trackers) + +do + + -- local has_glyph = nodes.has_glyph + local count_nodes = nodes.countall + + local texget = tex.get + + local tracer = processors.tracer + + -- We've set \hlistcallbackmode=1 so glyph checking happens at the other end! + + local function pre_linebreak_filter(head,groupcode) + -- local found = force_processors or has_glyph(head) + -- if found then + if trace_callbacks then + local before = count_nodes(head,true) + head = actions(head,groupcode) + local after = count_nodes(head,true) + tracer("pre_linebreak",head,groupcode,before,after,true) + else + head = actions(head,groupcode) + end + -- elseif trace_callbacks then + -- local n = count_nodes(head,false) + -- tracer("pre_linebreak",head,groupcode,n,n) + -- end + return head + end + + local function hpack_filter(head,groupcode,size,packtype,direction,attributes) + -- local found = force_processors or has_glyph(head) + -- if found then + -- + -- yes or no or maybe an option + -- + if not direction then + direction = texget("textdir") + end + -- + if trace_callbacks then + local before = count_nodes(head,true) + head = actions(head,groupcode,size,packtype,direction,attributes) + local after = count_nodes(head,true) + tracer("hpack",head,groupcode,before,after,true) + else + head = actions(head,groupcode,size,packtype,direction,attributes) + end + -- elseif trace_callbacks then + -- local n = count_nodes(head,false) + -- tracer("hpack",head,groupcode,n,n) + -- end + return head + end + + processors.pre_linebreak_filter = pre_linebreak_filter + processors.hpack_filter = hpack_filter + + do + + local hpack = nodes.hpack + + function nodes.fullhpack(head,...) + return hpack((hpack_filter(head)),...) + end + + end + + do + + local hpack = nuts.hpack + + function nuts.fullhpack(head,...) + return hpack(tonut(hpack_filter(tonode(head))),...) + end + + end + + callbacks.register('pre_linebreak_filter', pre_linebreak_filter, "horizontal manipulations (before par break)") + callbacks.register('hpack_filter' , hpack_filter, "horizontal manipulations (before hbox creation)") + +end + +do + -- Beware, these are packaged boxes so no first_glyph test needed. Maybe some day I'll add a hash + -- with valid groupcodes. Watch out, much can pass twice, for instance vadjust passes two times, + + local actions = tasks.actions("finalizers") -- head, where + local count_nodes = nodes.countall + + local tracer = processors.tracer + + local function post_linebreak_filter(head,groupcode) + if trace_callbacks then + local before = count_nodes(head,true) + head = actions(head,groupcode) + local after = count_nodes(head,true) + tracer("post_linebreak",head,groupcode,before,after,true) + else + head = actions(head,groupcode) + end + return head + end + + processors.post_linebreak_filter = post_linebreak_filter + + callbacks.register("post_linebreak_filter", post_linebreak_filter,"horizontal manipulations (after par break)") + +end + +do + + ----- texnest = tex.nest + local getnest = tex.getnest + + local getlist = nuts.getlist + local setlist = nuts.setlist + local getsubtype = nuts.getsubtype + + local linelist_code = nodes.listcodes.line + + local actions = tasks.actions("contributers") + + function processors.contribute_filter(groupcode) + if groupcode == "box" then -- "pre_box" + local whatever = getnest() + if whatever then + local line = whatever.tail + if line then + line = tonut(line) + if getsubtype(line) == linelist_code then + local head = getlist(line) + if head then + local result = actions(head,groupcode,line) + if result and result ~= head then + setlist(line,result) + end + end + end + end + end + end + end + + callbacks.register("contribute_filter", processors.contribute_filter,"things done with lines") + +end + +statistics.register("h-node processing time", function() + return statistics.elapsedseconds(nodes,"including kernel") -- hm, ok here? +end) diff --git a/tex/context/base/mkxl/strc-num.mkxl b/tex/context/base/mkxl/strc-num.mkxl index 8cf6039b1..b34fa4604 100644 --- a/tex/context/base/mkxl/strc-num.mkxl +++ b/tex/context/base/mkxl/strc-num.mkxl @@ -103,42 +103,42 @@ \permanent\tolerant\protected\def\setcounter[#1]#*[#2]#*[#3]% {\ifarguments\or\or - \clf_setcounter {\namedcounterparameter{#1}\s!name}\numexpr#2\relax\or + \clf_setcounter {\namedcounterparameter{#1}\s!name}\numexpr#2\relax \else \clf_setsubcounter{\namedcounterparameter{#1}\s!name}\numexpr#2\relax\numexpr#3\relax \fi} \permanent\tolerant\protected\def\setcounterown[#1]#*[#2]#*[#3]% {\ifarguments\or\or - \clf_setowncounter {\namedcounterparameter{#1}\s!name}{#2}\or + \clf_setowncounter {\namedcounterparameter{#1}\s!name}{#2}% \else \clf_setownsubcounter{\namedcounterparameter{#1}\s!name}\numexpr#2\relax{#3}% \fi} \permanent\tolerant\protected\def\restartcounter[#1]#*[#2]#*[#3]% {\ifarguments\or\or - \clf_restartcounter {\namedcounterparameter{#1}\s!name}\numexpr#2\relax\or + \clf_restartcounter {\namedcounterparameter{#1}\s!name}\numexpr#2\relax \else \clf_restartsubcounter{\namedcounterparameter{#1}\s!name}\numexpr#2\relax\numexpr#3\relax \fi} \permanent\tolerant\protected\def\resetcounter[#1]#*[#2]% {\ifarguments\or - \clf_resetcounter {\namedcounterparameter{#1}\s!name}\or + \clf_resetcounter {\namedcounterparameter{#1}\s!name}% \else \clf_resetsubcounter{\namedcounterparameter{#1}\s!name}\numexpr#2\relax \fi} \permanent\tolerant\protected\def\incrementcounter[#1]#*[#2]% {\ifarguments\or - \strc_counters_increment_sub{#1}\plusone\or + \strc_counters_increment_sub{#1}\plusone \else \strc_counters_increment_sub{#1}{#2}% \fi} \permanent\tolerant\protected\def\decrementcounter[#1]#*[#2]% {\ifarguments\or - \clf_decrementcounter {\namedcounterparameter{#1}\s!name}\or + \clf_decrementcounter {\namedcounterparameter{#1}\s!name}% \else \clf_decrementsubcounter{\namedcounterparameter{#1}\s!name}\numexpr#2\relax \fi} @@ -166,7 +166,7 @@ \permanent\tolerant\protected\def\prevcounter[#1]#*[#2]% {\ifarguments\or - \clf_previouscountervalue {\namedcounterparameter{#1}\s!name}%} + \clf_previouscountervalue {\namedcounterparameter{#1}\s!name}% \else \clf_previoussubcountervalue{\namedcounterparameter{#1}\s!name}\numexpr#2\relax\or \fi} diff --git a/tex/context/base/mkxl/syst-ini.mkxl b/tex/context/base/mkxl/syst-ini.mkxl index 57cd20759..2aad49bb5 100644 --- a/tex/context/base/mkxl/syst-ini.mkxl +++ b/tex/context/base/mkxl/syst-ini.mkxl @@ -1117,4 +1117,8 @@ \mutable\let\par\par \popoverloadmode +%D Also here: + +\listcallbackmode\plusone + \protect \endinput diff --git a/tex/context/base/mkxl/typo-ini.lmt b/tex/context/base/mkxl/typo-ini.lmt new file mode 100644 index 000000000..c45d29664 --- /dev/null +++ b/tex/context/base/mkxl/typo-ini.lmt @@ -0,0 +1,11 @@ +if not modules then modules = { } end modules ['typo-ini'] = { + version = 1.001, + comment = "companion to typo-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- nothing yet + +typesetters = typesetters or { } diff --git a/tex/context/base/mkxl/typo-ini.mkxl b/tex/context/base/mkxl/typo-ini.mkxl index 3e0790af0..3c081d8ca 100644 --- a/tex/context/base/mkxl/typo-ini.mkxl +++ b/tex/context/base/mkxl/typo-ini.mkxl @@ -20,7 +20,7 @@ \writestatus{loading}{ConTeXt Typographic Macros / Initialization} -\registerctxluafile{typo-ini}{} +\registerctxluafile{typo-ini}{autosuffix} \unprotect diff --git a/tex/context/base/mkxl/typo-itc.lmt b/tex/context/base/mkxl/typo-itc.lmt new file mode 100644 index 000000000..4350053b3 --- /dev/null +++ b/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: ] : 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: ] : 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 +} diff --git a/tex/context/base/mkxl/typo-itc.mklx b/tex/context/base/mkxl/typo-itc.mklx index f979261c8..141ee9968 100644 --- a/tex/context/base/mkxl/typo-itc.mklx +++ b/tex/context/base/mkxl/typo-itc.mklx @@ -22,7 +22,7 @@ %D \setupitaliccorrection[global,always] %D \stoptyping -\registerctxluafile{typo-itc}{} +\registerctxluafile{typo-itc}{autosuffix} \definesystemattribute[italics] [public] \definesystemattribute[mathitalics][public] diff --git a/tex/context/base/mkxl/typo-rub.lmt b/tex/context/base/mkxl/typo-rub.lmt new file mode 100644 index 000000000..82aa3c269 --- /dev/null +++ b/tex/context/base/mkxl/typo-rub.lmt @@ -0,0 +1,397 @@ +if not modules then modules = { } end modules ['typo-rub'] = { + version = 1.001, + comment = "companion to typo-rub.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- todo: recycle slots better +-- todo: hoffset +-- todo: auto-increase line height +-- todo: only hpack when start <> stop + +-- A typical bit of afternoon hackery ... with some breaks for watching +-- Ghost-Note on youtube (Robert Searight and Nate Werth) ... which expands +-- my to-be-had cd/dvd list again. + +local lpegmatch = lpeg.match +local utfcharacters = utf.characters +local setmetatableindex = table.setmetatableindex + +local variables = interfaces.variables +local implement = interfaces.implement + +local texsetattribute = tex.setattribute + +local v_flushleft = variables.flushleft +local v_middle = variables.middle +local v_flushright = variables.flushright +local v_yes = variables.yes +local v_no = variables.no +local v_auto = variables.auto + +local nuts = nodes.nuts + +local getid = nuts.getid +local getsubtype = nuts.getsubtype +local getattr = nuts.getattr +local setattr = nuts.setattr +local getnext = nuts.getnext +local setnext = nuts.setnext +local getprev = nuts.getprev +local setprev = nuts.setprev +local setlink = nuts.setlink +local getlist = nuts.getlist +local setlist = nuts.setlist +local setshift = nuts.setshift +local getwidth = nuts.getwidth +local setwidth = nuts.setwidth + +local hpack = nuts.hpack +local insert_after = nuts.insert_after +local takebox = nuts.takebox + +local nextlist = nuts.traversers.list + +local nodecodes = nodes.nodecodes +local glyph_code = nodecodes.glyph +local disc_code = nodecodes.disc +local kern_code = nodecodes.kern +local glue_code = nodecodes.glue +local penalty_code = nodecodes.penalty +local hlist_code = nodecodes.hlist +local vlist_code = nodecodes.vlist +local par_code = nodecodes.par +local dir_code = nodecodes.dir + +local kerncodes = nodes.kerncodes +local fontkern_code = kerncodes.font + +local nodepool = nuts.pool +local new_kern = nodepool.kern + +local setprop = nuts.setprop +local getprop = nuts.getprop + +local enableaction = nodes.tasks.enableaction + +local nofrubies = 0 +local rubylist = { } + +local a_ruby = attributes.private("ruby") + +local rubies = { } +typesetters.rubies = rubies + +local trace_rubies = false trackers.register("typesetters.rubies",function(v) trace_rubies = v end) +local report_rubies = logs.reporter("rubies") + +do + + local shared = nil + local splitter = lpeg.tsplitat("|") + + local function enable() + enableaction("processors","typesetters.rubies.check") + enableaction("shipouts", "typesetters.rubies.attach") + enable = false + end + + local ctx_setruby = context.core.setruby + + local function ruby(settings) + local base = settings.base + local comment = settings.comment + shared = settings + local c = lpegmatch(splitter,comment) + if #c == 1 then + ctx_setruby(base,comment) + if trace_rubies then + report_rubies("- %s -> %s",base,comment) + end + else + local i = 0 + for b in utfcharacters(base) do + i = i + 1 + local r = c[i] + if r then + ctx_setruby(b,r) + if trace_rubies then + report_rubies("%i: %s -> %s",i,b,r) + end + else + ctx_setruby(b,"") + if trace_rubies then + report_rubies("%i: %s",i,b) + end + end + end + end + if enable then + enable() + end + end + + local function startruby(settings) + shared = settings + if enable then + enable() + end + end + + implement { + name = "ruby", + actions = ruby, + arguments = { + { + { "align" }, + { "stretch" }, + { "hoffset", "dimension" }, + { "voffset", "dimension" }, + { "comment" }, + { "base" }, + } + }, + } + + implement { + name = "startruby", + actions = startruby, + arguments = { + { + { "align" }, + { "stretch" }, + { "hoffset", "dimension" }, + { "voffset", "dimension" }, + } + }, + } + + local function setruby(n,m) + nofrubies = nofrubies + 1 + local r = takebox(n) + rubylist[nofrubies] = setmetatableindex({ + text = r, + width = getwidth(r), + basewidth = 0, + start = false, + stop = false, + }, shared) + texsetattribute(a_ruby,nofrubies) + end + + implement { + name = "setruby", + actions = setruby, + arguments = "integer", + } + +end + +function rubies.check(head) + local current = head + local start = nil + local stop = nil + local found = nil + + local function flush(where) + local r = rubylist[found] + if r then + local prev = getprev(start) + local next = getnext(stop) + setprev(start) + setnext(stop) + local h = hpack(start) + if start == head then + head = h + else + setlink(prev,h) + end + setlink(h,next) + local bwidth = getwidth(h) + local rwidth = r.width + r.basewidth = bwidth + r.start = start + r.stop = stop + setprop(h,"ruby",found) + if rwidth > bwidth then + -- ruby is wider + setwidth(h,rwidth) + end + end + end + + while current do + local nx = getnext(current) + local id = getid(current) + if id == glyph_code then + local a = getattr(current,a_ruby) + if not a then + if found then + flush("flush 1") + found = nil + end + elseif a == found then + stop = current + else + if found then + flush("flush 2") + end + found = a + start = current + stop = current + end + -- go on + elseif id == kern_code and getsubtype(current,fontkern_code) then + -- go on + elseif found and id == disc_code then + -- go on (todo: look into disc) + elseif found then + flush("flush 3") + found = nil + end + current = nx + end + if found then + flush("flush 4") + end + return head, true -- no need for true +end + +local attach + +local function whatever(current,list) + local a = getprop(current,"ruby") + if a then + local ruby = rubylist[a] + local align = ruby.align or v_middle + local stretch = ruby.stretch or v_no + local hoffset = ruby.hoffset or 0 + local voffset = ruby.voffset or 0 + local start = ruby.start + local stop = ruby.stop + local text = ruby.text + local rwidth = ruby.width + local bwidth = ruby.basewidth + local delta = rwidth - bwidth + setwidth(text,0) + if voffset ~= 0 then + setshift(text,voffset) + end + -- center them + if delta > 0 then + -- ruby is wider + if stretch == v_yes then + setlink(text,start) + while start and start ~= stop do + local s = nodepool.stretch() + local n = getnext(start) + setlink(start,s,n) + start = n + end + text = hpack(text,rwidth,"exactly") + else + local left = new_kern(delta/2) + local right = new_kern(delta/2) + setlink(text,left,start) + setlink(stop,right) + end + setlist(current,text) + elseif delta < 0 then + -- ruby is narrower + if align == v_auto then + local l = true + local c = getprev(current) + while c do + local id = getid(c) + if id == glue_code or id == penalty_code or id == kern_code then + -- go on + elseif id == hlist_code and getwidth(c) == 0 then + -- go on + elseif id == whatsit_code or id == par_code or id == dir_code then + -- go on + else + l = false + break + end + c = getprev(c) + end + local r = true + local c = getnext(current) + while c do + local id = getid(c) + if id == glue_code or id == penalty_code or id == kern_code then + -- go on + elseif id == hlist_code and getwidth(c) == 0 then + -- go on + else + r = false + break + end + c = getnext(c) + end + if l and not r then + align = v_flushleft + elseif r and not l then + align = v_flushright + else + align = v_middle + end + end + if align == v_flushleft then + setlink(text,start) + setlist(current,text) + elseif align == v_flushright then + local left = new_kern(-delta) + local right = new_kern(delta) + setlink(left,text,right,start) + setlist(current,left) + else + local left = new_kern(-delta/2) + local right = new_kern(delta/2) + setlink(left,text,right,start) + setlist(current,left) + end + else + setlink(text,start) + setlist(current,text) + end + setprop(current,"ruby",false) + rubylist[a] = nil + elseif list then + attach(list) + end +end + +attach = function(head) + for current, id, subtype, list in nextlist, head do + if id == hlist_code or id == vlist_code then + whatever(current,list) + end + end + return head +end + +rubies.attach = attach + +-- for now there is no need to be compact + +-- local data = { } +-- rubies.data = data +-- +-- function rubies.define(settings) +-- data[#data+1] = settings +-- return #data +-- end +-- +-- implement { +-- name = "defineruby", +-- actions = { rubies.define, context }, +-- arguments = { +-- { +-- { "align" }, +-- { "stretch" }, +-- } +-- } +-- } diff --git a/tex/context/base/mkxl/typo-rub.mkxl b/tex/context/base/mkxl/typo-rub.mkxl index ecae30334..b4b0cbc5e 100644 --- a/tex/context/base/mkxl/typo-rub.mkxl +++ b/tex/context/base/mkxl/typo-rub.mkxl @@ -19,7 +19,7 @@ \unprotect -\registerctxluafile{typo-rub}{} +\registerctxluafile{typo-rub}{autosuffix} \definesystemattribute[ruby][public] diff --git a/tex/context/base/mkxl/typo-sus.lmt b/tex/context/base/mkxl/typo-sus.lmt new file mode 100644 index 000000000..2c6d9ea69 --- /dev/null +++ b/tex/context/base/mkxl/typo-sus.lmt @@ -0,0 +1,308 @@ +if not modules then modules = { } end modules ['typo-sus'] = { + version = 1.001, + comment = "companion to typo-sus.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local next = next + +local punctuation = { + po = true, +} + +local openquote = { + ps = true, + pi = true, +} + +local closequote = { + pe = true, + pf = true, +} + +local weird = { + lm = true, + no = true, +} + +local categories = characters.categories + +local nodecodes = nodes.nodecodes + +local glyph_code = nodecodes.glyph +local kern_code = nodecodes.kern +local penalty_code = nodecodes.penalty +local glue_code = nodecodes.glue +local math_code = nodecodes.math +local hlist_code = nodecodes.hlist +local vlist_code = nodecodes.vlist + +local nuts = nodes.nuts + +local nextnode = nuts.traversers.node + +local getid = nuts.getid +local getprev = nuts.getprev +local getnext = nuts.getnext +local getattr = nuts.getattr +local getfont = nuts.getfont +local getlist = nuts.getlist +local getkern = nuts.getkern +local getpenalty = nuts.getpenalty +local getwidth = nuts.getwidth +local getwhd = nuts.getwhd +local isglyph = nuts.isglyph + +local setattr = nuts.setattr +local setlist = nuts.setlist + +local setcolor = nodes.tracers.colors.set +local insert_before = nuts.insert_before +local insert_after = nuts.insert_after +local end_of_math = nuts.end_of_math + +local nodepool = nuts.pool + +local new_rule = nodepool.rule +local new_kern = nodepool.kern +local new_hlist = nodepool.hlist +----- new_penalty = nodepool.penalty + +local a_characters = attributes.private("characters") +local a_suspecting = attributes.private('suspecting') +local a_suspect = attributes.private('suspect') +local texsetattribute = tex.setattribute +local unsetvalue = attributes.unsetvalue +local enabled = false + +local enableaction = nodes.tasks.enableaction + +local threshold = 65536 / 4 + +local function special(n) + if n then + local id = getid(n) + if id == kern_code then + return getkern(n) < threshold + elseif id == penalty_code then + return true + elseif id == glue_code then + return getwidth(n) < threshold + elseif id == hlist_code then + return getwidth(n) < threshold + end + else + return false + end +end + +local function goback(current) + local prev = getprev(current) + while prev and special(prev) do + prev = getprev(prev) + end + if prev then + return prev, getid(prev) + end +end + +local function goforward(current) + local next = getnext(current) + while next and special(next) do + next = getnext(next) + end + if next then + return next, getid(next) + end +end + +-- We can cache rules if needed but there are not that many. + +local function mark(head,current,id,color) + if id == glue_code then + -- the glue can have stretch and/or shrink so the rule can overlap with the + -- following glyph .. no big deal as that one then sits on top of the rule + local width = getwidth(current) + local rule = new_rule(width) + local kern = new_kern(-width) + head = insert_before(head,current,rule) + head = insert_before(head,current,kern) + setcolor(rule,color) + -- elseif id == kern_code then + -- local width = getkern(current) + -- local rule = new_rule(width) + -- local kern = new_kern(-width) + -- head = insert_before(head,current,rule) + -- head = insert_before(head,current,kern) + -- setcolor(rule,color) + else + local width, height, depth = getwhd(current) + local extra = fonts.hashes.xheights[getfont(current)] / 2 + local rule = new_rule(width,height+extra,depth+extra) + local hlist = new_hlist(rule) + head = insert_before(head,current,hlist) + setcolor(rule,color) + setcolor(current,"white") + end + return head, current +end + +-- we can cache the font and skip ahead to next but it doesn't +-- save enough time and it makes the code looks bad too ... after +-- all, we seldom use this + +local colors = { + "darkred", + "darkgreen", + "darkblue", + "darkcyan", + "darkmagenta", + "darkyellow", + "darkgray", + "orange", +} + +local found = 0 + +function typesetters.marksuspects(head) + local current = head + local lastdone = nil + while current do + if getattr(current,a_suspecting) then + local char, id = isglyph(current) + if char then + local code = categories[char] + local done = false + if punctuation[code] then + local prev, pid = goback(current) + if prev and pid == glue_code then + done = 3 -- darkblue + elseif prev and pid == math_code then + done = 3 -- darkblue + else + local next, nid = goforward(current) + if next and nid ~= glue_code then + done = 3 -- darkblue + end + end + elseif openquote[code] then + local next, nid = goforward(current) + if next and nid == glue_code then + done = 1 -- darkred + end + elseif closequote[code] then + local prev, pid = goback(current) + if prev and pid == glue_code then + done = 1 -- darkred + end + elseif weird[code] then + done = 2 -- darkgreen + else + local prev, pid = goback(current) + if prev then + if pid == math_code then + done = 7-- darkgray + elseif pid == glyph_code and getfont(current) ~= getfont(prev) then + if lastdone ~= prev then + done = 2 -- darkgreen + end + end + end + if not done then + local next, nid = goforward(current) + if next then + if nid == math_code then + done = 7 -- darkgray + elseif nid == glyph_code and getfont(current) ~= getfont(next) then + if lastdone ~= prev then + done = 2 -- darkgreen + end + end + end + end + end + if done then + setattr(current,a_suspect,done) + lastdone = current + found = found + 1 + end + current = getnext(current) + elseif id == math_code then + current = getnext(end_of_math(current)) + elseif id == glue_code then + local a = getattr(current,a_characters) + if a then + local prev = getprev(current) + local prid = prev and getid(prev) + local done = false + if prid == penalty_code and getpenalty(prev) == 10000 then + done = 8 -- orange + else + done = 5 -- darkmagenta + end + if done then + setattr(current,a_suspect,done) + -- lastdone = current + found = found + 1 + end + end + current = getnext(current) + else + current = getnext(current) + end + else + current = getnext(current) + end + end + return head +end + +local function showsuspects(head) + -- we inject before so we're okay with a loop + for current, id, subtype in nextnode, head do + if id == glyph_code then + local a = getattr(current,a_suspect) + if a then + head, current = mark(head,current,id,colors[a]) + end + elseif id == glue_code then + local a = getattr(current,a_suspect) + if a then + head, current = mark(head,current,id,colors[a]) + end + elseif id == math_code then + current = end_of_math(current) + elseif id == hlist_code or id == vlist_code then + local list = getlist(current) + if list then + local l = showsuspects(list) + if l ~= list then + setlist(current,l) + end + end + end + end + return head +end + +function typesetters.showsuspects(head) + if found > 0 then + return showsuspects(head) + else + return head + end +end + +-- or maybe a directive + +trackers.register("typesetters.suspects",function(v) + texsetattribute(a_suspecting,v and 1 or unsetvalue) + if v and not enabled then + enableaction("processors","typesetters.marksuspects") + enableaction("shipouts", "typesetters.showsuspects") + enabled = true + end +end) + diff --git a/tex/context/base/mkxl/typo-tal.lmt b/tex/context/base/mkxl/typo-tal.lmt new file mode 100644 index 000000000..5c32e9ef9 --- /dev/null +++ b/tex/context/base/mkxl/typo-tal.lmt @@ -0,0 +1,439 @@ +if not modules then modules = { } end modules ['typo-tal'] = { + version = 1.001, + comment = "companion to typo-tal.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- I'll make it a bit more efficient and provide named instances too which is needed for +-- nested tables. +-- +-- Currently we have two methods: text and number with some downward compatible +-- defaulting. + +-- We can speed up by saving the current fontcharacters[font] + lastfont. + +local next, type, tonumber = next, type, tonumber +local div = math.div +local utfbyte = utf.byte + +local splitmethod = utilities.parsers.splitmethod + +local nodecodes = nodes.nodecodes +local glyph_code = nodecodes.glyph +local glue_code = nodecodes.glue + +local fontcharacters = fonts.hashes.characters +----- unicodes = fonts.hashes.unicodes +local categories = characters.categories -- nd + +local variables = interfaces.variables +local v_text = variables.text +local v_number = variables.number + +local nuts = nodes.nuts +local tonut = nuts.tonut + +local getnext = nuts.getnext +local getprev = nuts.getprev +local getboth = nuts.getboth +local getid = nuts.getid +local getfont = nuts.getfont +local getchar = nuts.getchar +local getattr = nuts.getattr +local isglyph = nuts.isglyph + +local setattr = nuts.setattr +local setchar = nuts.setchar + +local insert_node_before = nuts.insert_before +local insert_node_after = nuts.insert_after +local nextglyph = nuts.traversers.glyph +local getdimensions = nuts.dimensions + +local setglue = nuts.setglue + +local nodepool = nuts.pool +local new_kern = nodepool.kern + +local tracers = nodes.tracers +local setcolor = tracers.colors.set +local tracedrule = tracers.pool.nuts.rule + +local enableaction = nodes.tasks.enableaction + +local characteralign = { } +typesetters.characteralign = characteralign + +local trace_split = false trackers.register("typesetters.characteralign", function(v) trace_split = true end) +local report = logs.reporter("aligning") + +local a_characteralign = attributes.private("characteralign") +local a_character = attributes.private("characters") + +local enabled = false + +local datasets = false + +local implement = interfaces.implement + +local comma = 0x002C +local period = 0x002E +local punctuationspace = 0x2008 + +local validseparators = { + [comma] = true, + [period] = true, + [punctuationspace] = true, +} + +local validsigns = { + [0x002B] = 0x002B, -- plus + [0x002D] = 0x2212, -- hyphen + [0x00B1] = 0x00B1, -- plusminus + [0x2212] = 0x2212, -- minus + [0x2213] = 0x2213, -- minusplus +} + +-- If needed we can have more modes which then also means a faster simple handler +-- for non numbers. + +local function setcharacteralign(column,separator,before,after) + if not enabled then + enableaction("processors","typesetters.characteralign.handler") + enabled = true + end + if not datasets then + datasets = { } + end + local dataset = datasets[column] -- we can use a metatable + if not dataset then + local method, token + if separator then + method, token = splitmethod(separator) + if method and token then + separator = utfbyte(token) or comma + else + separator = utfbyte(separator) or comma + method = validseparators[separator] and v_number or v_text + end + else + separator = comma + method = v_number + end + local before = tonumber(before) or 0 + local after = tonumber(after) or 0 + dataset = { + separator = separator, + list = { }, + maxbefore = before, + maxafter = after, + predefined = before > 0 or after > 0, + collected = false, + method = method, + separators = validseparators, + signs = validsigns, + } + datasets[column] = dataset + used = true + end + return dataset +end + +local function resetcharacteralign() + datasets = false +end + +characteralign.setcharacteralign = setcharacteralign +characteralign.resetcharacteralign = resetcharacteralign + +implement { + name = "setcharacteralign", + actions = setcharacteralign, + arguments = { "integer", "string" } +} + +implement { + name = "setcharacteraligndetail", + actions = setcharacteralign, + arguments = { "integer", "string", "dimension", "dimension" } +} + +implement { + name = "resetcharacteralign", + actions = resetcharacteralign +} + +local function traced_kern(w) + return tracedrule(w,nil,nil,"darkgray") +end + +function characteralign.handler(head,where) + if not datasets then + return head + end + local first + for n in nextglyph, head do + first = n + break + end + if not first then + return head + end + local a = getattr(first,a_characteralign) + if not a or a == 0 then + return head + end + local column = div(a,0xFFFF) + local row = a % 0xFFFF + local dataset = datasets and datasets[column] or setcharacteralign(column) + local separator = dataset.separator + local list = dataset.list + local b_start = nil + local b_stop = nil + local a_start = nil + local a_stop = nil + local c = nil + local current = first + local sign = nil + -- + local validseparators = dataset.separators + local validsigns = dataset.signs + local method = dataset.method + -- we can think of constraints + if method == v_number then + +-- local function bothdigit(current) -- this could become a helper +-- local prev, next = getboth(current) +-- if next and prev and getid(next) == glyph_code and getid(prev) == glyph_code then +-- local pchar = getchar(prev) +-- local nchar = getchar(next) +-- local pdata = fontcharacters[getfont(prev)][pchar] +-- local ndata = fontcharacters[getfont(next)][nchar] +-- local punicode = pdata and pdata.unicode or pchar -- we ignore tables +-- local nunicode = ndata and ndata.unicode or nchar -- we ignore tables +-- if punicode and nunicode and categories[punicode] == "nd" and categories[nunicode] == "nd" then +-- return true +-- end +-- end +-- end + + local function bothdigit(current) -- this could become a helper + local prev, next = getboth(current) + if next and prev then + local char, font = isglyph(prev) + if char then + local data = fontcharacters[font][char] + if data and categories[data.unicode or char] == "nd" then -- we ignore tables + char, font = isglyph(next) + if char then + data = fontcharacters[font][char] + if data and categories[data.unicode or char] == "nd" then -- we ignore tables + return true + end + end + end + end + end + end + + while current do + local char, id = isglyph(current) + if char then + local font = id --- nicer + local data = fontcharacters[font][char] + local unicode = data and data.unicode or char -- ignore tables + if not unicode then -- type(unicode) ~= "number" + -- no unicode so forget about it + elseif unicode == separator then + c = current + if trace_split then + setcolor(current,"darkred") + end + dataset.hasseparator = true + elseif categories[unicode] == "nd" or validseparators[unicode] then + if c then + if not a_start then + a_start = current + end + a_stop = current + if trace_split then + setcolor(current,validseparators[unicode] and "darkcyan" or "darkblue") + end + else + if not b_start then + if sign then + b_start = sign + local c, f = isglyph(sign) + local new = validsigns[c] + if char == new or not fontcharacters[f][new] then + if trace_split then + setcolor(sign,"darkyellow") + end + else + setchar(sign,new) + if trace_split then + setcolor(sign,"darkmagenta") + end + end + sign = nil + b_stop = current + else + b_start = current + b_stop = current + end + else + b_stop = current + end + if trace_split and current ~= sign then + setcolor(current,validseparators[unicode] and "darkcyan" or "darkblue") + end + end + elseif not b_start then + sign = validsigns[unicode] and current + -- if trace_split then + -- setcolor(current,"darkgreen") + -- end + end + elseif (b_start or a_start) and id == glue_code then + -- maybe only in number mode + -- somewhat inefficient + if bothdigit(current) then + local width = fontcharacters[getfont(b_start or a_start)][separator or period].width + setglue(current,width,0,0) + setattr(current,a_character,punctuationspace) + if a_start then + a_stop = current + elseif b_start then + b_stop = current + end + end + end + current = getnext(current) + end + else + while current do + local char, id = isglyph(current) + if char then + local font = id -- nicer + -- local unicode = unicodes[font][char] + local unicode = fontcharacters[font][char].unicode or char -- ignore tables + if not unicode then + -- no unicode so forget about it + elseif unicode == separator then + c = current + if trace_split then + setcolor(current,"darkred") + end + dataset.hasseparator = true + else + if c then + if not a_start then + a_start = current + end + a_stop = current + if trace_split then + setcolor(current,"darkgreen") + end + else + if not b_start then + b_start = current + end + b_stop = current + if trace_split then + setcolor(current,"darkblue") + end + end + end + end + current = getnext(current) + end + end + local predefined = dataset.predefined + local before, after + if predefined then + before = b_start and getdimensions(b_start,getnext(b_stop)) or 0 + after = a_start and getdimensions(a_start,getnext(a_stop)) or 0 + else + local entry = list[row] + if entry then + before = entry.before or 0 + after = entry.after or 0 + else + before = b_start and getdimensions(b_start,getnext(b_stop)) or 0 + after = a_start and getdimensions(a_start,getnext(a_stop)) or 0 + list[row] = { + before = before, + after = after, + } + return head, true + end + if not dataset.collected then + -- print("[maxbefore] [maxafter]") + local maxbefore = 0 + local maxafter = 0 + for k, v in next, list do + local before = v.before + local after = v.after + if before and before > maxbefore then + maxbefore = before + end + if after and after > maxafter then + maxafter = after + end + end + dataset.maxbefore = maxbefore + dataset.maxafter = maxafter + dataset.collected = true + end + end + local maxbefore = dataset.maxbefore + local maxafter = dataset.maxafter + local new_kern = trace_split and traced_kern or new_kern + if b_start then + if before < maxbefore then + head = insert_node_before(head,b_start,new_kern(maxbefore-before)) + end + if not c then + -- print("[before]") + if dataset.hasseparator then + local width = fontcharacters[getfont(b_start)][separator].width + insert_node_after(head,b_stop,new_kern(maxafter+width)) + end + elseif a_start then + -- print("[before] [separator] [after]") + if after < maxafter then + insert_node_after(head,a_stop,new_kern(maxafter-after)) + end + else + -- print("[before] [separator]") + if maxafter > 0 then + insert_node_after(head,c,new_kern(maxafter)) + end + end + elseif a_start then + if c then + -- print("[separator] [after]") + if maxbefore > 0 then + head = insert_node_before(head,c,new_kern(maxbefore)) + end + else + -- print("[after]") + local width = fontcharacters[getfont(b_stop)][separator].width + head = insert_node_before(head,a_start,new_kern(maxbefore+width)) + end + if after < maxafter then + insert_node_after(head,a_stop,new_kern(maxafter-after)) + end + elseif c then + -- print("[separator]") + if maxbefore > 0 then + head = insert_node_before(head,c,new_kern(maxbefore)) + end + if maxafter > 0 then + insert_node_after(head,c,new_kern(maxafter)) + end + end + return head +end diff --git a/tex/context/base/mkxl/typo-tal.mkxl b/tex/context/base/mkxl/typo-tal.mkxl index 86a90020c..d96d1348b 100644 --- a/tex/context/base/mkxl/typo-tal.mkxl +++ b/tex/context/base/mkxl/typo-tal.mkxl @@ -22,7 +22,7 @@ \unprotect -\registerctxluafile{typo-tal}{} +\registerctxluafile{typo-tal}{autosuffix} \definesystemattribute[characteralign][public] -- cgit v1.2.3