From 8e857efd2c61e8ffa4c991b5f1ddacfb4ebedc3c Mon Sep 17 00:00:00 2001
From: Hans Hagen <pragma@wxs.nl>
Date: Mon, 10 Jan 2022 09:19:22 +0100
Subject: 2022-01-10 08:36:00

---
 tex/context/base/mkii/cont-new.mkii                |   2 +-
 tex/context/base/mkii/context.mkii                 |   2 +-
 tex/context/base/mkii/mult-cs.mkii                 |   4 +-
 tex/context/base/mkiv/cont-new.mkiv                |   2 +-
 tex/context/base/mkiv/context.mkiv                 |   2 +-
 tex/context/base/mkiv/font-cff.lua                 |   7 +-
 tex/context/base/mkiv/font-dsp.lua                 |   2 +-
 tex/context/base/mkiv/font-osd.lua                 |  53 +--
 tex/context/base/mkiv/font-ttf.lua                 |   6 +-
 tex/context/base/mkiv/status-files.pdf             | Bin 24763 -> 24781 bytes
 tex/context/base/mkiv/status-lua.pdf               | Bin 257564 -> 257575 bytes
 tex/context/base/mkxl/cont-new.mkxl                |   2 +-
 tex/context/base/mkxl/context.mkxl                 |   2 +-
 tex/context/base/mkxl/font-fbk.lmt                 |  75 +++-
 tex/context/base/mkxl/font-pre.mkxl                |   2 +-
 tex/context/base/mkxl/math-frc.lmt                 |   2 +-
 tex/context/base/mkxl/strc-itm.mklx                |   7 +-
 tex/context/base/mkxl/syst-ini.mkxl                |  14 +-
 tex/context/base/mkxl/task-ini.lmt                 |   4 +-
 tex/context/base/mkxl/trac-deb.lmt                 |  21 +
 tex/context/base/mkxl/typo-syn.lmt                 | 452 ++++++++++++++++-----
 tex/context/base/mkxl/typo-syn.mkxl                | 171 +++++++-
 tex/context/interface/mkii/keys-cs.xml             |   4 +-
 tex/context/modules/mkiv/s-fonts-variable.mkiv     |   8 +-
 tex/generic/context/luatex/luatex-fonts-merged.lua |  50 ++-
 25 files changed, 703 insertions(+), 191 deletions(-)

diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii
index 0b5ff689e..b138a8254 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{2022.01.06 19:47}
+\newcontextversion{2022.01.10 08:33}
 
 %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 a76dea83f..38091b490 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{2022.01.06 19:47}
+\edef\contextversion{2022.01.10 08:33}
 
 %D For those who want to use this:
 
diff --git a/tex/context/base/mkii/mult-cs.mkii b/tex/context/base/mkii/mult-cs.mkii
index a3e6285ee..37e93983e 100644
--- a/tex/context/base/mkii/mult-cs.mkii
+++ b/tex/context/base/mkii/mult-cs.mkii
@@ -300,6 +300,7 @@
 \setinterfacevariable{july}{cervenec}
 \setinterfacevariable{june}{cerven}
 \setinterfacevariable{keep}{drzet}
+\setinterfacevariable{keeptogether}{keeptogether}
 \setinterfacevariable{kerncharacters}{kerncharacters}
 \setinterfacevariable{knockout}{knockout}
 \setinterfacevariable{label}{popisek}
@@ -1229,7 +1230,7 @@
 \setinterfaceconstant{solution}{solution}
 \setinterfaceconstant{sort}{sort}
 \setinterfaceconstant{sorttype}{sorttype}
-\setinterfaceconstant{source}{zdroj}
+\setinterfaceconstant{source}{source}
 \setinterfaceconstant{space}{mezera}
 \setinterfaceconstant{spaceafter}{mezeraza}
 \setinterfaceconstant{spaceafterside}{spaceafterside}
@@ -1280,6 +1281,7 @@
 \setinterfaceconstant{synonymcommand}{synonymcommand}
 \setinterfaceconstant{synonymstyle}{stylsynonyma}
 \setinterfaceconstant{tab}{tab}
+\setinterfaceconstant{target}{target}
 \setinterfaceconstant{text}{text}
 \setinterfaceconstant{textalign}{textalign}
 \setinterfaceconstant{textalternative}{textalternative}
diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv
index 2e2e914a1..9180cc403 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{2022.01.06 19:47}
+\newcontextversion{2022.01.10 08:33}
 
 %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 5ca6fbb50..0037516fe 100644
--- a/tex/context/base/mkiv/context.mkiv
+++ b/tex/context/base/mkiv/context.mkiv
@@ -49,7 +49,7 @@
 %D {YYYY.MM.DD HH:MM} format.
 
 \edef\contextformat {\jobname}
-\edef\contextversion{2022.01.06 19:47}
+\edef\contextversion{2022.01.10 08:33}
 
 %D Kind of special:
 
diff --git a/tex/context/base/mkiv/font-cff.lua b/tex/context/base/mkiv/font-cff.lua
index 8ae4d755b..4258991c6 100644
--- a/tex/context/base/mkiv/font-cff.lua
+++ b/tex/context/base/mkiv/font-cff.lua
@@ -1931,7 +1931,8 @@ local result = { }
                 -- cff 1: (when cff2 strip them)
                 elseif t == 1 or t == 3 or t == 18 or operation == 23 then
                     p_getstem() -- at the start
-                    if true then
+                    if version == "cff" then
+--                     if true then
                         if top > 0 then
                             for i=1,top do
                                 r = r + 1 ; result[r] = encode[stack[i]]
@@ -1946,6 +1947,7 @@ local result = { }
                 -- cff 1: (when cff2 strip them)
                 elseif t == 19 or t == 20 then
                     local s = p_getmask() or 0 -- after the stems
+--                     if version == "cff" then
                     if true then
                         if top > 0 then
                             for i=1,top do
@@ -1969,7 +1971,8 @@ local result = { }
                     i = i + 1
                 elseif t == 13 then
                     hsbw()
-                    if version == "cff" then
+--                     if version == "cff" then
+                    if true then
                         -- we do a moveto over lsb
                         r = r + 1 ; result[r] = encode[lsb]
                         r = r + 1 ; result[r] = chars[22]
diff --git a/tex/context/base/mkiv/font-dsp.lua b/tex/context/base/mkiv/font-dsp.lua
index 8d7c3d359..7028f7c08 100644
--- a/tex/context/base/mkiv/font-dsp.lua
+++ b/tex/context/base/mkiv/font-dsp.lua
@@ -3806,6 +3806,7 @@ function readers.hvar(f,fontdata,specification)
     end
     local tableoffset = gotodatatable(f,fontdata,"hvar",specification.variable)
     if not tableoffset then
+        report("no hvar table, expect problems due to messy widths")
         return
     end
 
@@ -3823,7 +3824,6 @@ function readers.hvar(f,fontdata,specification)
     if variationoffset > 0 then
         regions, deltas = readvariationdata(f,variationoffset,factors)
     end
-
     if not regions then
         -- for now .. what to do ?
         return
diff --git a/tex/context/base/mkiv/font-osd.lua b/tex/context/base/mkiv/font-osd.lua
index 1bdd20fd9..16228b88b 100644
--- a/tex/context/base/mkiv/font-osd.lua
+++ b/tex/context/base/mkiv/font-osd.lua
@@ -877,23 +877,16 @@ local function initializeconjuncts(tfmdata,value)
         local resources  = tfmdata.resources
         local devanagari = resources.devanagari
         if devanagari then
-            local conjuncts = "auto"
-            local conjuncts = "continue"
-            local movematra = "auto"
+            -- quit was the old situation
+            local conjuncts = "auto" -- mixed|continue|quit|auto
+            local movematra = "auto" -- default|leftbeforebase|auto
             if type(value) == "string" and value ~= "auto" then
                 value     = settings_to_hash(value)
                 conjuncts = rawget(value,"conjuncts") or conjuncts
                 movematra = rawget(value,"movematra") or movematra
             end
             if conjuncts == "auto" then
-                conjuncts = "continue"
-                -- maybe some day we can figure out bad-fonts logic
-             -- if script == "mlym" or script == "mlm2" or
-             --    script == "taml" or script == "tml2" then
-             --     conjuncts = "continue"
-             -- else
-             --     conjuncts = "quit"
-             -- end
+                conjuncts = "mixed" -- for all scripts ?
             end
             if movematra == "auto" and
                   script == "mlym" or
@@ -905,9 +898,9 @@ local function initializeconjuncts(tfmdata,value)
             devanagari.conjuncts = conjuncts
             devanagari.movematra = movematra
             --
---             if trace_steps then
+            if trace_steps then
                 report("conjuncts %a, movematra %a",conjuncts,movematra)
---             end
+            end
             --
         end
     end
@@ -2470,21 +2463,28 @@ local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowe
                     already_below_mark = true
                 elseif post_mark[v] and not already_post_mark then
                     already_post_mark = true
-                elseif devanagarihash[font].conjuncts == "quit" then
+                elseif devanagarihash[font].conjuncts == "continue" then
+                    -- for testing
+                else
                     return c
                 end
             end
         else
-            if devanagarihash[font].conjuncts == "quit" then
-                return c
-            elseif pre_mark[v] and not already_pre_mark then
+            if pre_mark[v] and not already_pre_mark then
                 already_pre_mark = true
             elseif post_mark[v] and not already_post_mark then
-                already_post_mark = true
+                if devanagarihash[font].conjuncts == "mixed" then
+                    -- for messy fonts
+                    return c
+                else
+                    already_post_mark = true
+                end
             elseif below_mark[v] and not already_below_mark then
                 already_below_mark = true
             elseif above_mark[v] and not already_above_mark then
                 already_above_mark = true
+            elseif devanagarihash[font].conjuncts == "continue" then
+                -- for testing
             else
                 return c
             end
@@ -2678,21 +2678,28 @@ local function analyze_next_chars_two(c,font)
                         already_below_mark = true
                     elseif post_mark[v] and not already_post_mark then
                         already_post_mark = true
-                    elseif devanagarihash[font].conjuncts == "quit" then
+                    elseif devanagarihash[font].conjuncts == "continue" then
+                        -- for testing
+                    else
                         return c
                     end
                 end
             else
-                if devanagarihash[font].conjuncts == "quit" then
-                    return c
-                elseif pre_mark[v] and not already_pre_mark then
+                if pre_mark[v] and not already_pre_mark then
                     already_pre_mark = true
                 elseif post_mark[v] and not already_post_mark then
-                    already_post_mark = true
+                    if devanagarihash[font].conjuncts == "mixed" then
+                        -- for messy fonts
+                        return c
+                    else
+                        already_post_mark = true
+                    end
                 elseif below_mark[v] and not already_below_mark then
                     already_below_mark = true
                 elseif above_mark[v] and not already_above_mark then
                     already_above_mark = true
+                elseif devanagarihash[font].conjuncts == "continue" then
+                    -- for testing
                 else
                     return c
                 end
diff --git a/tex/context/base/mkiv/font-ttf.lua b/tex/context/base/mkiv/font-ttf.lua
index fc987d74c..a2658d595 100644
--- a/tex/context/base/mkiv/font-ttf.lua
+++ b/tex/context/base/mkiv/font-ttf.lua
@@ -207,6 +207,8 @@ end
 -- We had two loops (going backward) but can do it in one loop .. but maybe we
 -- should only accept fonts with proper hvar tables.
 
+-- dowidth is kind of hack ... fonts are not always ok wrt these extra points
+
 local xv = { } -- we share this cache
 local yv = { } -- we share this cache
 
@@ -241,7 +243,7 @@ local function applyaxis(glyph,shape,deltas,dowidth)
                                 p[2] = p[2] + y
                             end
                         end
-                    else
+                    elseif cnt > 0 then
                         -- Not the most efficient solution but we seldom do this. We
                         -- actually need to avoid the extra points here but I'll deal
                         -- with that when needed.
@@ -365,6 +367,8 @@ local function applyaxis(glyph,shape,deltas,dowidth)
                                 pi[2] = pi[2] + factor * fy
                             end
                         end
+                    else
+                        report("bad deltapoint data, maybe a missing hvar table")
                     end
                 else
                     for i=1,nofpoints do
diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf
index cda4f4219..c0645fa90 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 e8b4cfefc..2b7f03fa1 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 d9a326406..e73df27b9 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{2022.01.06 19:47}
+\newcontextversion{2022.01.10 08:33}
 
 %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 392895fe6..dc5d14244 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{2022.01.06 19:47}
+\immutable\edef\contextversion{2022.01.10 08:33}
 
 %overloadmode 1 % check frozen / warning
 %overloadmode 2 % check frozen / error
diff --git a/tex/context/base/mkxl/font-fbk.lmt b/tex/context/base/mkxl/font-fbk.lmt
index 47d284c84..bdc5265ae 100644
--- a/tex/context/base/mkxl/font-fbk.lmt
+++ b/tex/context/base/mkxl/font-fbk.lmt
@@ -17,7 +17,7 @@ local next = next
 local trace_visualize    = false  trackers.register("fonts.composing.visualize", function(v) trace_visualize = v end)
 local trace_define       = false  trackers.register("fonts.composing.define",    function(v) trace_define    = v end)
 
-local report             = logs.reporter("fonts","combining")
+local report             = logs.reporter("fonts","composing")
 
 local allocate           = utilities.storage.allocate
 
@@ -50,6 +50,73 @@ local fraction           = 0.15  -- 30 units for lucida
 -- todo: we also need to update the feature hashes ... i'll do that when i'm in the mood
 -- and/or when i need it
 
+-- \starttext
+--     \definefontfeature[default][default][fakecombining=yes,compose=yes]
+--     \setupbodyfont[minion]
+--     [x][\char"2D9][x][\char"323] ṭḍṃḥ
+-- \stoptext
+
+local missing = {
+ -- [0x323] = { kind = "bottom", top = 0x307 }, -- dot below
+    [0x323] = { kind = "bottom", top = 0x2D9 }, -- dot below
+    --
+    -- TODO
+    --
+}
+
+local function fakecharacters(tfmdata,value)
+    if value then
+        local characters   = tfmdata.characters
+        local descriptions = tfmdata.descriptions
+        for unicode, detail in next, missing do
+            local c = characters[k]
+            if not c then
+                local kind = detail.kind
+                if kind == "bottom" then
+                    local u = detail.top
+                    local d = characters[u]
+                    if d then
+                        local ex = tfmdata.parameters.xheight
+                        characters[unicode] = {
+                            width    = d.width,
+                            height   = 0,
+                            depth    = ex/2,
+                            commands = { { "offset", 0, -d.height - ex/5 , u } },
+                            unicode  = unicode,
+                        }
+                        -- we need this for the composer
+                        local d = descriptions[u]
+                        local x = descriptions[0x78]
+                        if d and x then
+                            local t = table.copy(d)
+                            local x = -x.boundingbox[4]/2
+                            local b = t.boundingbox
+                            b[2] = x
+                            b[4] = x
+                            descriptions[unicode] = t
+                        end
+                        if trace then
+                            report("deriving %C from %C",unicode,u)
+                        end
+                    end
+                end
+            end
+        end
+    end
+end
+
+local specification = {
+    name        = "fakecombining",
+    description = "add missing combining accents",
+    manipulators = {
+     -- position = 1, -- todo: just before compose
+        base     = fakecharacters,
+        node     = fakecharacters,
+    }
+}
+
+fonts.handlers.otf.features.register(specification)
+
 local function composecharacters(tfmdata)
     -- this assumes that slot 1 is self, there will be a proper self some day
     local characters   = tfmdata.characters
@@ -110,8 +177,10 @@ local function composecharacters(tfmdata)
                                     report("composed %C, base %C, accent %C",i,chr,acc)
                                 end
                                 local acc_t = charcommand[acc]
-                                local cb = descriptions[chr].boundingbox
-                                local ab = descriptions[acc].boundingbox
+                                local cb = descriptions[chr]
+                                local ab = descriptions[acc]
+                                local cb = cb and cb.boundingbox
+                                local ab = ab and ab.boundingbox
                                 -- todo: adapt height
                                 if cb and ab then
                                     local c_llx = scale*cb[1]
diff --git a/tex/context/base/mkxl/font-pre.mkxl b/tex/context/base/mkxl/font-pre.mkxl
index c8dac54e6..aba14d8b2 100644
--- a/tex/context/base/mkxl/font-pre.mkxl
+++ b/tex/context/base/mkxl/font-pre.mkxl
@@ -203,7 +203,7 @@
   [mode=node,
    language=dflt,
    % localized
- % indic=auto,
+   indic=auto, % we need to handle fuzzy fonts
    % localized
    locl=yes,
    % positioning
diff --git a/tex/context/base/mkxl/math-frc.lmt b/tex/context/base/mkxl/math-frc.lmt
index cf35897ce..488c95056 100644
--- a/tex/context/base/mkxl/math-frc.lmt
+++ b/tex/context/base/mkxl/math-frc.lmt
@@ -37,7 +37,7 @@ local function mathfraction(how,left,right,width)
             context("\\atopwithdelims%s%s",resolved[left],resolved[right])
         end
     elseif how == v_yes or how == v_hidden then
-        local norule = how == v_hidden and LUATEXFUNCTIONALITY > 7361 and " norule " or ""
+        local norule = how == v_hidden and " norule " or ""
         if left == 0x002E and right == 0x002E then
             context("\\normalabove%s%s%ssp",norule,width)
         else
diff --git a/tex/context/base/mkxl/strc-itm.mklx b/tex/context/base/mkxl/strc-itm.mklx
index ba21733c5..3ccc5358e 100644
--- a/tex/context/base/mkxl/strc-itm.mklx
+++ b/tex/context/base/mkxl/strc-itm.mklx
@@ -1314,12 +1314,7 @@
    \settrue\c_strc_itemgroups_txt
    \startitemgroupitem}
 
-% \protected\tolerant\def\strc_itemgroups_start_items_indeed[#1]%
-%   {\strc_itemgroups_start_edge
-%      {\dorecurse{0\itemgroupparameter\c!items}{\strc_itemgroups_used_symbol\hss}%
-%       \unskip}}
-
-\protected\tolerant\def\strc_itemgroups_start_items_indeed[#1]%
+\protected\tolerant\def\strc_itemgroups_start_items[#1]%
   {\strc_itemgroups_start_edge
      {\localcontrolledloop
         \plusone
diff --git a/tex/context/base/mkxl/syst-ini.mkxl b/tex/context/base/mkxl/syst-ini.mkxl
index 8a446277a..605a4c334 100644
--- a/tex/context/base/mkxl/syst-ini.mkxl
+++ b/tex/context/base/mkxl/syst-ini.mkxl
@@ -381,13 +381,25 @@
 %D as all engines now provide many registers we removed all traces. If you ever run
 %D into the \MKII\ \type {mptopdf} code, you'll notice that some register magic was
 %D needed to fit into existing macro packages. The same is true for \PICTEX\ that we
-%D used in (gheneric) \PPCHTEX.
+%D used in (generic) \PPCHTEX.
 
 \ifdefined\writestatus \else
    %\protected\def\writestatus#1#2{\immediate\write\statuswrite{#1: #2}}
     \frozen\protected\def\writestatus#1#2{\message{#1: #2}}
 \fi
 
+%D The above command will normally hook into the interface commands and is a
+%D protected command. We also have an \type {\diagnostic} predefined which is an
+%D expandable command:
+%D
+%D \starttyping
+%D test \diagnostic     {here 1} test
+%D test \diagnostic[2]  {here 2} test
+%D test \diagnostic[.05]{here 3} test
+%D \stoptyping
+%D
+%D The optional argument triggers a sleep in seconds.
+
 \def\syst_basics_allocate_yes#1#2#3#4#5% last class method max name
   {\ifnum#1<#4\relax
      \global\advance#1\plusone
diff --git a/tex/context/base/mkxl/task-ini.lmt b/tex/context/base/mkxl/task-ini.lmt
index 790b05442..7218f033f 100644
--- a/tex/context/base/mkxl/task-ini.lmt
+++ b/tex/context/base/mkxl/task-ini.lmt
@@ -141,7 +141,7 @@ appendaction("vboxbuilders", "normalizers", "nodes.handlers.backgroundsvbox",
 appendaction("vboxbuilders", "normalizers", "builders.vspacing.vboxhandler",                    nil, "nut",    "enabled"   )
 appendaction("vboxbuilders", "normalizers", "builders.profiling.vboxhandler",                   nil, "nut",    "disabled"  )
 appendaction("vboxbuilders", "normalizers", "typesetters.checkers.handler",                     nil, "nut",    "disabled"  )
-appendaction("vboxbuilders", "normalizers", "typesetters.parallels.handler",                    nil, "nut",    "disabled"  )
+appendaction("vboxbuilders", "normalizers", "typesetters.synchronize.handler",                  nil, "nut",    "disabled"  )
 
 appendaction("mvlbuilders",  "normalizers", "nodes.handlers.backgroundspage",                   nil, "nut",    "disabled"  )
 appendaction("mvlbuilders",  "normalizers", "typesetters.margins.globalhandler",                nil, "nut",    "disabled"  )
@@ -149,7 +149,7 @@ appendaction("mvlbuilders",  "normalizers", "nodes.handlers.migrate",
 appendaction("mvlbuilders",  "normalizers", "builders.vspacing.pagehandler",                    nil, "nut",    "enabled"   )
 appendaction("mvlbuilders",  "normalizers", "builders.profiling.pagehandler",                   nil, "nut",    "disabled"  )
 appendaction("mvlbuilders",  "normalizers", "typesetters.checkers.handler",                     nil, "nut",    "disabled"  )
-appendaction("mvlbuilders",  "normalizers", "typesetters.parallels.handler",                    nil, "nut",    "disabled"  )
+appendaction("mvlbuilders",  "normalizers", "typesetters.synchronize.handler",                  nil, "nut",    "disabled"  )
 
 appendaction("everypar",     "normalizers", "nodes.handlers.checkparcounter",                   nil, "nut",    "disabled"  )
 
diff --git a/tex/context/base/mkxl/trac-deb.lmt b/tex/context/base/mkxl/trac-deb.lmt
index 195a7cb8e..f54e0605e 100644
--- a/tex/context/base/mkxl/trac-deb.lmt
+++ b/tex/context/base/mkxl/trac-deb.lmt
@@ -24,6 +24,7 @@ local implement    = interfaces.implement
 
 local ioflush      = io.flush
 local ioread       = io.read
+local ossleep      = os.sleep
 local writenl      = texio.writenl
 local write        = texio.write
 
@@ -418,3 +419,23 @@ directives.register("system.profile",function(n)
     logs.report("system","profiler started")
     debugger.enable()
 end)
+
+local report = logs.reporter("[[diagnostic]]")
+
+implement {
+    name      = "diagnostic",
+    public    = true,
+ -- protected = false, -- expandable
+    arguments = { "optional", "string" },
+    actions   = function(t,s)
+        t = tonumber(t)
+        if t then
+            report("%s (sleep: %.3N)",s,t)
+            ioflush()
+            ossleep(t)
+        else
+            report(s)
+            ioflush()
+        end
+    end
+}
diff --git a/tex/context/base/mkxl/typo-syn.lmt b/tex/context/base/mkxl/typo-syn.lmt
index dbc5b27ff..fc03ccb3b 100644
--- a/tex/context/base/mkxl/typo-syn.lmt
+++ b/tex/context/base/mkxl/typo-syn.lmt
@@ -16,39 +16,38 @@ local enableaction      = tasks.enableaction
 local nuts              = nodes.nuts
 local tonut             = nodes.tonut
 
-local getnext           = nuts.getnext
-local getprev           = nuts.getprev
-local getid             = nuts.getid
-local getsubtype        = nuts.getsubtype
 local getattr           = nuts.getattr
-local setattrlist       = nuts.setattrlist
 local getattrlist       = nuts.getattrlist
-
+local getdepth          = nuts.getdepth
+local getdirection      = nuts.getdirection
+local getdisc           = nuts.getdisc
+local getglue           = nuts.getglue
+local getheight         = nuts.getheight
+local getid             = nuts.getid
+local getlist           = nuts.getlist
+local getnext           = nuts.getnext
+local getprev           = nuts.getprev
 local getprop           = nuts.getprop
-local setprop           = nuts.setprop
-
-local setlink           = nuts.setlink
-local setprev           = nuts.setprev
-local setnext           = nuts.setnext
-local setoffsets        = nuts.setoffsets
-
+local getsubtype        = nuts.getsubtype
+local gettotal          = nuts.gettotal
 local getwhd            = nuts.getwhd
-
 local getwidth          = nuts.getwidth
-local setwidth          = nuts.setwidth
-local getdepth          = nuts.getdepth
+local setattrlist       = nuts.setattrlist
 local setdepth          = nuts.setdepth
-local getheight         = nuts.getheight
+local setdisc           = nuts.setdisc
 local setheight         = nuts.setheight
-local gettotal          = nuts.gettotal
-local getlist           = nuts.getlist
+local setlink           = nuts.setlink
 local setlist           = nuts.setlist
-local getdisc           = nuts.getdisc
-local setdisc           = nuts.setdisc
+local setnext           = nuts.setnext
+local setoffsets        = nuts.setoffsets
+local setprev           = nuts.setprev
+local setprop           = nuts.setprop
+local setwidth          = nuts.setwidth
 
 local hpack             = nuts.hpack
 local rangedimensions   = nuts.rangedimensions
 local insertbefore      = nuts.insertbefore
+local insertafter       = nuts.insertafter
 local removenode        = nuts.remove
 local flushnode         = nuts.flush
 
@@ -57,28 +56,33 @@ local traverselist      = nuts.traverselist
 local nodecodes         = nodes.nodecodes
 local glyph_code        = nodecodes.glyph
 local rule_code         = nodecodes.rule
+local dir_code          = nodecodes.dir
 local disc_code         = nodecodes.disc
 local hlist_code        = nodecodes.hlist
 local vlist_code        = nodecodes.vlist
+local math_code         = nodecodes.math
 local glue_code         = nodecodes.glue
 local kern_code         = nodecodes.kern
 local penalty_code      = nodecodes.penalty
 
-local line_code             = nodes.listcodes.line
-local fontkern_code         = nodes.kerncodes.fontkern
-local parfillrightskip_code = nodes.gluecodes.parfillrightskip
-local baselineskip_code     = nodes.gluecodes.baselineskip
+local line_code         = nodes.listcodes.line
+local fontkern_code     = nodes.kerncodes.fontkern
+local parfillskip_code  = nodes.gluecodes.parfillrightskip
+local baselineskip_code = nodes.gluecodes.baselineskip
 
------------------
+local new_direction     = nuts.pool.direction
 
-local a_synchronize   = attributes.private("synchronize")
+local runningrule       = tex.magicconstants.runningrule
 
-local parallels       = typesetters.parallels or { }
-typesetters.parallels = parallels or { }
+-----------------
+
+local synchronize       = typesetters.synchronize or { }
+typesetters.synchronize = synchronize or { }
 
-local registervalue   = attributes.registervalue
-local getvalue        = attributes.getvalue
-local hasvalues       = attributes.hasvalues
+local a_synchronize     = attributes.private("synchronize")
+local registervalue     = attributes.registervalue
+local getvalue          = attributes.getvalue
+local hasvalues         = attributes.hasvalues
 
 local trace  = false
 -- local trace  = true
@@ -88,21 +92,21 @@ local pushsavelevel = tex.pushsavelevel -- token.expandmacro("bgroup")
 local popsavelevel  = tex.popsavelevel  -- token.expandmacro("egroup")
 local dontcomplain  = tex.dontcomplain
 
-local slack    = 6553.6
 local index    = 0
 local lastattr = nil
 local lastline = nil
 
 interfaces.implement {
     name      = "registersynchronize",
-    arguments = { "dimen", "dimen", "box" },
-    actions   = function(ht,dp,box)
+    arguments = { "dimen", "dimen", "dimen", "box" },
+    actions   = function(ht,dp,slack,box)
         index = index + 1
         box   = tonut(box)
         local t = {
             index      = index,
             lineheight = ht,
             linedepth  = dp,
+            slack      = slack,
             height     = getheight(height),
             depth      = getheight(depth),
             box        = box,
@@ -110,94 +114,147 @@ interfaces.implement {
         local v = registervalue(a_synchronize,t)
         tex.setattribute(a_synchronize,v)
         if index > 0 then
-            enableaction("vboxbuilders", "typesetters.parallels.handler")
-            enableaction("mvlbuilders",  "typesetters.parallels.handler")
+            enableaction("vboxbuilders", "typesetters.synchronize.handler")
+            enableaction("mvlbuilders",  "typesetters.synchronize.handler")
         end
     end,
-
 }
 
-local function hsplit(box,width)
-    local first    = getlist(box)
-    local last     = first
-    local current  = first
-    local previous = current
-    local sofar    = 0
-    local lastdisc = nil
-    -- todo: option
-    width = width - slack
-    --
+-- When this is stable it can become a proper helper and primitive.
+
+local function hsplit(box,targetwidth,targetheight,targetdepth,mcriterium,pcriterium,upto)
+    local first     = getlist(box)
+    local last      = first
+    local current   = first
+    local previous  = current
+    local lastdisc  = nil
+    local lastglyph = nil
+ -- local stretch   = 0
+ -- local shrink    = 0
+    local width     = 0
+    local height    = 0
+    local depth     = 0
+    local dirstack  = { } -- can move to outer
+    local dirtop    = 0
+    local minwidth  = targetwidth
+    local maxwidth  = targetwidth
+    local usedwidth = targetwidth
     while true do
         previous = current
         local id = getid(current)
         if id == glyph_code then
-            local wd = getwidth(current)
-            if sofar + wd > width then
+            local wd, ht, dp = getwhd(current)
+            -- find next break first
+            local newwidth = width + wd
+            if newwidth >= usedwidth then
+                if not lastdisc and lastglyph then
+                    last = getprev(lastglyph)
+                end
                 break
             else
-                sofar = sofar + wd
+                width = newwidth
+                if ht > height then
+                    height = ht
+                end
+                if dp > depth then
+                    depth = dp
+                end
+            end
+            if not lastglyph then
+                lastglyph = current
             end
         elseif id == kern_code then
+            local wd = getwidth(current)
+            local newwidth = width + wd
             if getsubtype(current) == fontkern_code then
                 -- assume sane kerns
-                local wd = getwidth(current)
-                sofar = sofar + wd
+                width = newwidth
             else
                 last = previous
-                local wd = getwidth(current)
-                if sofar + wd > width then
+                if newwidth >= usedwidth then
                     break
                 else
-                    sofar = sofar + wd
+                    width = newwidth
                 end
-                lastdisc = nil
+                lastdisc  = nil
+                lastglyph = nil
             end
         elseif id == disc_code then
-            -- move on / inject post if needed
             local pre, post, replace = getdisc(current)
-            local wdr = replace and rangedimensions(box,replace) or 0
-            if sofar + wdr > width then
-                -- handle post and pre here
-                last     = previous
+            local wd = replace and rangedimensions(box,replace) or 0
+            local newwidth = width + wd
+            if newwidth >= usedwidth then
                 break
             end
-            local wdp = pre and rangedimensions(box,pre) or 0
---            if sofar + wdp > width then
---                last     = previous
---lastdisc = current
---                break
---          end
-            sofar = sofar + wdr
-            lastdisc = current
-        elseif id == glue_code then
-            last = previous
-            local wd = getwidth(current)
-            if sofar + wd > width then
+            local wd = pre and rangedimensions(box,pre) or 0
+            local prewidth = width + wd
+            if prewidth >= usedwidth then
                 break
-            else
-                sofar = sofar + wd
             end
-            lastdisc = nil
-        elseif id == hlist_code or id == vlist_code then
-            last = previous
-            local wd = getwidth(current)
-            if sofar + wd > width then
-                break
-            else
-                sofar = sofar + wd
-            end
-            lastdisc = nil
-        elseif id == rule_code then
-            last = previous
-            local wd = getwidth(current)
-            if sofar + wd > width then
-                break
-            else
-                sofar = sofar + wd
-            end
-            lastdisc = nil
+            width    = newwidth
+            lastdisc = current
         else
-            lastdisc = nil
+            -- common code at the end
+            if id == glue_code or id == math_code then -- refactor : common code
+                -- leaders
+                last = previous
+                local wd, more, less = getglue(current)
+                local newwidth = width + wd
+                if newwidth >= usedwidth then
+                    break
+                else
+                    width   = newwidth
+                 -- stretch = stretch + more
+                 -- shrink  = shrink  + less
+                    -- also for statistics
+                    maxwidth = maxwidth + more
+                    minwidth = minwidth + less
+                    -- can become an option:
+                    usedwidth = minwidth
+                end
+            elseif id == hlist_code or id == vlist_code then
+                last = previous
+                local wd, ht, dp = getwhd(current)
+                local newwidth = width + wd
+                if newwidth >= usedwidth then
+                    break
+                else
+                    width = newwidth
+                    if ht > height then
+                        height = ht
+                    end
+                    if dp > depth then
+                        depth = dp
+                    end
+                end
+            elseif id == rule_code then
+                last = previous
+                local wd, ht, dp = getwhd(current)
+                local newwidth = width + wd
+                if newwidth >= usedwidth then
+                    break
+                else
+                    width = newwidth
+                    if ht ~= runningrule and ht > height then
+                        height = ht
+                    end
+                    if dp ~= runningrule and dp > depth then
+                        depth = dp
+                    end
+                end
+            elseif id == dir_code then
+                local dir, cancel = getdirection(current)
+                if cancel then
+                    if dirtop > 0 then
+                        dirtop = dirtop - 1
+                    end
+                else
+                    dirtop = dirtop + 1
+                    dirstack[dirtop] = dir
+                end
+            end
+            lastdisc  = nil
+            lastglyph = nil
         end
         local next = getnext(current)
         if next then
@@ -209,7 +266,7 @@ local function hsplit(box,width)
     end
     local next
     if lastdisc then
-        local pre, post, replace = getdisc(lastdisc)
+        local pre, post, replace, pretail, posttail, replacetail = getdisc(lastdisc,true)
         last = getprev(lastdisc)
         --
         next = getnext(lastdisc)
@@ -218,8 +275,9 @@ local function hsplit(box,width)
         end
         --
         setlink(last,pre)
+        last = pretail
         if post then
-            setlink(post,next)
+            setlink(posttail,next)
             next = post
         end
         setdisc(lastdisc,nil,nil,replace)
@@ -234,28 +292,140 @@ local function hsplit(box,width)
     while last do
         local id = getid(last)
         if id == glue_code or id == penalty_code then
+         -- if id == glue_code then
+         --     local wd, more, less = getglue(last)
+         --  -- stretch = stretch - more
+         --  -- shrink  = shrink  - less
+         --     width   = width   - wd
+         --     -- also for statistics
+         --     maxwidth = maxwidth - more
+         --     minwidth = minwidth - less
+         --     -- can become an option:
+         --     usedwidth = minwidth
+         -- end
             first, last = removenode(first,last,true)
         else
             break
         end
     end
+    if dirtop > 0 then
+        for i=dirtop,1,-1 do
+            local d = new_direction(dirstack[i],true)
+            first, last = insertafter(first,last,d)
+        end
+        for i=1,dirtop do
+            local d = new_direction(dirstack[i],false)
+            next = insertbefore(next,next,d)
+        end
+    end
     if first then
-        pushsavelevel()
-        dontcomplain()
-        local result, badness = hpack(first,width,"exactly")
-        if badness > 200 then
+     -- pushsavelevel()
+     -- dontcomplain()
+        local result
+        if upto then
             result = hpack(first)
+        else
+            local badness, overshoot
+            result, badness, overshoot = hpack(first,targetwidth,"exactly")
+            local pdone = pcriterium and badness > pcriterium
+            local mdone = mcriterium and badness > mcriterium
+            if overshoot > 0 then
+                if pdone then
+                    result = hpack(first)
+                end
+            elseif overshoot < 0 then
+                if mdone then
+                    result = hpack(first)
+                end
+            else
+                if pdone or mdone then
+                    result = hpack(first)
+                end
+            end
         end
-        popsavelevel()
+     -- popsavelevel()
         setattrlist(result,getattrlist(box)) -- useattrlist(result,box)
-        setheight(result,getheight(box))
-        setdepth(result,getdepth(box))
+        setheight(result,targetheight or height)
+        setdepth(result,targetdepth or depth)
         setlist(box,next)
         setwidth(box,rangedimensions(box,next))
         return result
     end
 end
 
+do
+
+    local scanners     = tokens.scanners
+    local scanword     = scanners.word
+    local scaninteger  = scanners.integer
+    local scandimen    = scanners.dimen
+
+    local tonode       = nuts.tonode
+    local getbox       = nuts.getbox
+    local setbox       = nuts.setbox
+
+    local direct_value = tokens.values.direct
+    local none_value   = tokens.values.none
+
+    interfaces.implement {
+        name      = "hsplit",
+        protected = true,
+        public    = true,
+        usage     = "value",
+        actions   = function(what)
+            local n = scaninteger()
+            local w = 0
+            local h = false
+            local d = false
+            local pcriterium = false
+            local mcriterium = false
+            local upto = false
+            while true do
+                local key = scanword()
+                if key == "to" then
+                    upto = false
+                    w = scandimen()
+                elseif key == "upto" then
+                    upto = true
+                    w = scandimen()
+                elseif key == "width" then
+                    w = scandimen()
+                elseif key == "height" then
+                    h = scandimen()
+                elseif key == "depth" then
+                    d = scandimen()
+                elseif key == "criterium" then
+                    pcriterium = scaninteger()
+                    mcriterium = scaninteger()
+                elseif key == "shrinkcriterium" then
+                    mcriterium = scaninteger()
+                elseif key == "stretchcriteriun" then
+                    pcriterium = scaninteger()
+                else
+                    break
+                end
+            end
+            pushsavelevel()
+            dontcomplain()
+            local r = hsplit(getbox(n),w,h,d,mcriterium,pcriterium,upto)
+            popsavelevel()
+            if r then
+                if what == "value" then
+                    return direct_value, r
+                else
+                    context(tonode(r))
+                end
+            else
+                setbox(n)
+                if what == "value" then
+                    return none_value, nil
+                end
+            end
+        end,
+    }
+
+end
+
 local function getproperties(parent)
     local props = getprop(parent,"parallel")
     if not props then
@@ -304,7 +474,7 @@ local function flush(head,first,last,a,parent,nesting)
         if upto and getid(upto) == penalty_code then
             upto = getnext(upto)
         end
-        if upto and getid(upto) == glue_code and getsubtype(upto) == parfillrightskip_code then
+        if upto and getid(upto) == glue_code and getsubtype(upto) == parfillskip_code then
             upto = getnext(upto)
         end
         local props = getproperties(parent)
@@ -322,7 +492,7 @@ local function flush(head,first,last,a,parent,nesting)
             local result = nil
             local cwidth = getwidth(content)
             local ctotal = gettotal(content)
-            if cwidth <= width then -- slack
+            if cwidth <= width then
                 if trace then
                     report("index %i, available %p, content %p, verdict %a",index,width,cwidth,"fit")
                 end
@@ -332,7 +502,7 @@ local function flush(head,first,last,a,parent,nesting)
                 if trace then
                     report("index %i, available %p, content %p, verdict %a",index,width,cwidth,"overflow")
                 end
-                result   = hsplit(content,width)
+                result   = hsplit(content,width-(data.slack or 0),nil,nil,200)
                 lastattr = a
                 lastline = parent
             else
@@ -383,7 +553,7 @@ local function lastflush(lastline,lastattr)
             local total  = 0
             local cwidth = getwidth(content)
             local ctotal = gettotal(content)
-            if cwidth <= width then -- slack
+            if cwidth <= width then
                 if trace then
                     report("index %i, available %p, content %p, verdict %a",index,width,cwidth,"fit")
                 end
@@ -393,7 +563,7 @@ local function lastflush(lastline,lastattr)
                 if trace then
                     report("index %i, available %p, content %p, verdict %a",index,width,cwidth,"overflow")
                 end
-                result = hsplit(content,width)
+                result = hsplit(content,width-(data.slack or 0),nil,nil,200)
             else
                 report("index %i, verdict %a",index,"weird")
             end
@@ -413,7 +583,7 @@ end
 
 local processranges = nuts.processranges
 
-function parallels.handler(head,where)
+function synchronize.handler(head,where)
     if where == "hmodepar" and hasvalues(a_synchronize) then
         lastattr = nil
         lastline = nil
@@ -433,3 +603,63 @@ function parallels.handler(head,where)
     end
     return head
 end
+
+--
+
+local settings_to_array  = utilities.parsers.settings_to_array
+local get_buffer_content = buffers.getcontent
+local splitlines         = string.splitlines
+
+interfaces.implement {
+    name      = "synchronizesteps",
+    arguments = { {
+        { "list" },
+        { "split" },
+        { "buffer" },
+        { "text" },
+    } },
+    actions   = function(t)
+        local split  = t.split -- not used yet
+        local list   = t.list
+        local buffer = t.buffer
+        local text   = t.text
+        local data   = false
+        if buffer and buffer ~= "" then
+            data = settings_to_array(buffer)
+            if #data == 2 then
+                for i=1,#data do
+                    data[i] = splitlines(get_buffer_content(data[i]) or "")
+                end
+            else
+                return
+            end
+        elseif text and text ~= "" then
+            data = settings_to_array(text)
+            if #data == 2 then
+                for i=1,#data do
+                    data[i] = settings_to_array(data[i])
+                end
+            else
+                return
+            end
+        else
+            return
+        end
+        if list and list ~= "" then
+            list = settings_to_array(list)
+        else
+            list = { }
+        end
+        local done = data[1]
+        local dtwo = data[2]
+        if #done == #dtwo then
+            local lone = list[1] or ""
+            local ltwo = list[2] or ""
+            for i=1,#done do
+                context.dosplitsynchronize(lone,ltwo,done[i],dtwo[i])
+            end
+        else
+            context.type("[different sizes in synchronize]")
+        end
+    end,
+}
diff --git a/tex/context/base/mkxl/typo-syn.mkxl b/tex/context/base/mkxl/typo-syn.mkxl
index adb3603d5..c89d485e2 100644
--- a/tex/context/base/mkxl/typo-syn.mkxl
+++ b/tex/context/base/mkxl/typo-syn.mkxl
@@ -14,6 +14,9 @@
 % Musical timestamp: this code was written when I start relistening my whole
 % digitized cd collection with the (new) r2r soekris dac in my setup.
 
+\writestatus{loading}{ConTeXt Typesetting Macros / Synchronizers}
+
+\registerctxluafile{typo-syn}{autosuffix}
 
 %D Yet another experiment (triggered by a question / demand from Ton Otten.)
 %D
@@ -48,46 +51,135 @@
 %D     {[\ignorespaces\samplefile{ward}\removeunwantedspaces]}%
 %D     {[\ignorespaces\samplefile{tufte}\removeunwantedspaces]}
 %D \par test line \page
+%D
+%D \startbuffer[en]
+%D     This is an english text and
+%D     this is also something english;
+%D     as is this.
+%D     Not that there is much meaning in it.
+%D \stopbuffer
+%D
+%D \startbuffer[nl]
+%D     Dit is een nederlandse tekst
+%D     en dit is ook wat nederlands;
+%D     net als dit.
+%D     Niet dat het veel te betekenen heeft.
+%D \stopbuffer
+%D
+%D \definesynchronizecontent
+%D   [nl]
+%D   [language=nl,
+%D    color=darkred]
+%D
+%D \definesynchronizecontent
+%D   [en]
+%D   [language=en,
+%D    color=darkgreen]
+%D
+%D \definesynchronize
+%D   [parallel-en-nl]
+%D   [list={en,nl}]
+%D
+%D \placesynchronize
+%D   [parallel-en-nl]
+%D   [buffer={en,nl}]
+%D
+%D \blank
+%D
+%D \placesynchronize
+%D   [parallel-en-nl]
+%D   [split={.},
+%D    text={
+%D     {This is an english text and this is also something english; as is this. Not that there is much meaning in it.},
+%D     {Dit is een nederlandse tekst en dit is ook wat nederlands; net als dit. Niet dat het veel te betekenen heeft.}
+%D    }]
+%D
+%D \blank
+%D
+%D \placesynchronize
+%D   [parallel-en-nl]
+%D   [split={.},
+%D    text={
+%D     {
+%D         {This is an english text and this is also something english;},
+%D         {as is this.},
+%D         {Not that there is much meaning in it.}
+%D     },
+%D     {
+%D         {Dit is een nederlandse tekst en dit is ook wat nederlands;},
+%D         {net als dit.},
+%D         {Niet dat het veel te betekenen heeft.}
+%D     }
+%D    }]
+%D
+%D \setupsynchronizecontent
+%D   [nl]
+%D   [language=nl,
+%D    style=\tx,
+%D    left={[},
+%D    right={]},
+%D    color=darkblue]
+%D
+%D \placesynchronize
+%D   [parallel-en-nl]
+%D   [distance=1pt,
+%D    buffer={en,nl}]
 %D \stoptyping
 
-\writestatus{loading}{ConTeXt Typesetting Macros / Synchronizers}
-
-\registerctxluafile{typo-syn}{autosuffix}
-
 \unprotect
 
 \definesystemattribute[synchronize][public]
 
 \installcorenamespace {synchronize}
+\installcorenamespace {synchronizecontent}
+
+\installcommandhandler \??synchronize        {synchronize}        \??synchronize
+\installcommandhandler \??synchronizecontent {synchronizecontent} \??synchronizecontent
 
-\installcommandhandler \??synchronize {synchronize} \??synchronize
+\setupsynchronize
+  [\c!buffer=,
+   \c!text=,
+   \c!list=,
+   \c!split=,
+   \c!distance=\zeropoint]
+
+\let\typo_sync_rule\novrule
+
+\installtextracker
+  {synchronize}
+  {\let\typo_sync_rule\vrule}
+  {\let\typo_sync_rule\novrule}
 
 \tolerant\protected\def\typo_synchronize#1#*[#2]#:#3#4%
   {\dontleavehmode
    \begingroup
    \def\currentsynchronize{#1}%
    \setupcurrentsynchronize[#2]%
+   \scratchdistance\synchronizeparameter\c!distance
    \dontcomplain
    \setbox\scratchboxtwo\hbox\bgroup
      \usesynchronizestyleandcolor\c!style\c!color
-     \setstrut
-     \strut
+     \begstrut
      \ignorespaces#4\removeunwantedspaces
+     \endstrut
    \egroup
    \scratchdimentwo\wd\scratchboxtwo
+   \advance\scratchdimentwo\scratchdistance
    \clf_registersynchronize
      \strutht
      \strutdp
+     .1\onepoint
      \box\scratchboxtwo
    \relax
-   \setbox\scratchboxone\hbox{#3}%
+   \setbox\scratchboxone\hbox\bgroup
+     \ignorespaces#3\removeunwantedspaces
+   \egroup
    \scratchdimenone\wd\scratchboxone
    \unhbox\scratchboxone
    \advance\scratchdimentwo-\scratchdimenone
    \ifdim\scratchdimentwo>\zeropoint
      \wordboundary
-     \novrule
-   % \vrule
+     \typo_sync_rule
         \s!width  \scratchdimentwo
         \s!height \exheight
         \s!depth  \zeropoint
@@ -95,6 +187,65 @@
    \fi
    \endgroup}
 
+\protected\def\dosplitsynchronize#1#2#3#4%
+  {\begingroup
+   \dontcomplain
+   \setbox\scratchboxtwo\hbox\bgroup
+     \def\currentsynchronizecontent{#2}%
+     \usesynchronizecontentstyleandcolor\c!style\c!color
+     \uselanguageparameter\synchronizecontentparameter
+     \synchronizecontentparameter\c!left
+     \begstrut
+     \ignorespaces
+     #4\removeunwantedspaces
+     \endstrut
+     \synchronizecontentparameter\c!right
+   \egroup
+   \scratchdimentwo\wd\scratchboxtwo
+   \advance\scratchdimentwo\scratchdistance
+   \clf_registersynchronize
+     \strutht
+     \strutdp
+     .1\onepoint
+     \box\scratchboxtwo
+   \relax
+   \setbox\scratchboxone\hbox\bgroup
+     \def\currentsynchronizecontent{#1}%
+     \usesynchronizecontentstyleandcolor\c!style\c!color
+     \uselanguageparameter\synchronizecontentparameter
+     \synchronizecontentparameter\c!left
+     \ignorespaces#3\removeunwantedspaces
+     \synchronizecontentparameter\c!right
+   \egroup
+   \scratchdimenone\wd\scratchboxone
+   \unhbox\scratchboxone
+   \advance\scratchdimentwo-\scratchdimenone
+   \ifdim\scratchdimentwo>\zeropoint
+     \wordboundary
+     \typo_sync_rule
+        \s!width  \scratchdimentwo
+        \s!height \exheight
+        \s!depth  \zeropoint
+     \relax
+   \fi
+   \endgroup
+   \space}
+
+\tolerant\protected\def\placesynchronize[#1]#*[#2]%
+  {\dontleavehmode
+   \begingroup
+   \def\currentsynchronize{#1}%
+   \setupcurrentsynchronize[#2]%
+   \scratchdistance\synchronizeparameter\c!distance
+   \clf_synchronizesteps
+      list   {\synchronizeparameter\c!list}%
+      split  {\synchronizeparameter\c!split}%
+      buffer {\synchronizeparameter\c!buffer}%
+      text   {\synchronizeparameter\c!text}%
+   \relax
+   \removeunwantedspaces
+   \endgroup}
+
 \appendtoks
     \protected\instance\edefcsname\currentsynchronize\endcsname{\typo_synchronize{\currentsynchronize}}%
 \to \everydefinesynchronize
diff --git a/tex/context/interface/mkii/keys-cs.xml b/tex/context/interface/mkii/keys-cs.xml
index 75f8fad3f..7cd92518b 100644
--- a/tex/context/interface/mkii/keys-cs.xml
+++ b/tex/context/interface/mkii/keys-cs.xml
@@ -303,6 +303,7 @@
 		<cd:variable name='july' value='cervenec'/>
 		<cd:variable name='june' value='cerven'/>
 		<cd:variable name='keep' value='drzet'/>
+		<cd:variable name='keeptogether' value='keeptogether'/>
 		<cd:variable name='kerncharacters' value='kerncharacters'/>
 		<cd:variable name='knockout' value='knockout'/>
 		<cd:variable name='label' value='popisek'/>
@@ -1235,7 +1236,7 @@
 		<cd:constant name='solution' value='solution'/>
 		<cd:constant name='sort' value='sort'/>
 		<cd:constant name='sorttype' value='sorttype'/>
-		<cd:constant name='source' value='zdroj'/>
+		<cd:constant name='source' value='source'/>
 		<cd:constant name='space' value='mezera'/>
 		<cd:constant name='spaceafter' value='mezeraza'/>
 		<cd:constant name='spaceafterside' value='spaceafterside'/>
@@ -1286,6 +1287,7 @@
 		<cd:constant name='synonymcommand' value='synonymcommand'/>
 		<cd:constant name='synonymstyle' value='stylsynonyma'/>
 		<cd:constant name='tab' value='tab'/>
+		<cd:constant name='target' value='target'/>
 		<cd:constant name='text' value='text'/>
 		<cd:constant name='textalign' value='textalign'/>
 		<cd:constant name='textalternative' value='textalternative'/>
diff --git a/tex/context/modules/mkiv/s-fonts-variable.mkiv b/tex/context/modules/mkiv/s-fonts-variable.mkiv
index cb8bd1f9a..43cb1d32c 100644
--- a/tex/context/modules/mkiv/s-fonts-variable.mkiv
+++ b/tex/context/modules/mkiv/s-fonts-variable.mkiv
@@ -112,8 +112,12 @@
 %  \showfontvariations
 %    [font=file:AmstelvarAlpha-VF.ttf]
 
- \showfontvariations
-   [font=file:bahnschrift.ttf]
+%  \showfontvariations
+%    [font=file:bahnschrift.ttf]
+
+%  \showfontvariations
+%    [font=file:junicodetwobetavf-roman.ttf]
+%    [font=file:junicodetwobeta-regular-vf.otf]
 
 % \showfontvariations
 %   [font=file:sitka.ttc]
diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua
index fdfde314f..6c7977ffa 100644
--- a/tex/generic/context/luatex/luatex-fonts-merged.lua
+++ b/tex/generic/context/luatex/luatex-fonts-merged.lua
@@ -1,6 +1,6 @@
 -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua
 -- parent file : c:/data/develop/context/sources/luatex-fonts.lua
--- merge date  : 2022-01-06 19:47
+-- merge date  : 2022-01-10 08:33
 
 do -- begin closure to overcome local limits and interference
 
@@ -14718,7 +14718,7 @@ local result={}
      i=i+s+1
     elseif t==1 or t==3 or t==18 or operation==23 then
      p_getstem() 
-     if true then
+     if version=="cff" then
       if top>0 then
        for i=1,top do
         r=r+1;result[r]=encode[stack[i]]
@@ -14731,7 +14731,7 @@ local result={}
      end
      i=i+1
     elseif t==19 or t==20 then
-     local s=p_getmask() or 0 
+     local s=p_getmask() or 0
      if true then
       if top>0 then
        for i=1,top do
@@ -14754,7 +14754,7 @@ local result={}
      i=i+1
     elseif t==13 then
      hsbw()
-     if version=="cff" then
+     if true then
       r=r+1;result[r]=encode[lsb]
       r=r+1;result[r]=chars[22]
      else
@@ -15557,7 +15557,7 @@ local function applyaxis(glyph,shape,deltas,dowidth)
         p[2]=p[2]+y
        end
       end
-     else
+     elseif cnt>0 then
       local function find(i)
        local prv=cnt
        for j=1,cnt do
@@ -15659,6 +15659,8 @@ local function applyaxis(glyph,shape,deltas,dowidth)
         pi[2]=pi[2]+factor*fy
        end
       end
+     else
+      report("bad deltapoint data, maybe a missing hvar table")
      end
     else
      for i=1,nofpoints do
@@ -19779,6 +19781,7 @@ function readers.hvar(f,fontdata,specification)
  end
  local tableoffset=gotodatatable(f,fontdata,"hvar",specification.variable)
  if not tableoffset then
+  report("no hvar table, expect problems due to messy widths")
   return
  end
  local version=readulong(f) 
@@ -32253,16 +32256,15 @@ local function initializeconjuncts(tfmdata,value)
   local resources=tfmdata.resources
   local devanagari=resources.devanagari
   if devanagari then
-   local conjuncts="auto"
-   local conjuncts="continue"
-   local movematra="auto"
+   local conjuncts="auto" 
+   local movematra="auto" 
    if type(value)=="string" and value~="auto" then
     value=settings_to_hash(value)
     conjuncts=rawget(value,"conjuncts") or conjuncts
     movematra=rawget(value,"movematra") or movematra
    end
    if conjuncts=="auto" then
-    conjuncts="continue"
+    conjuncts="mixed" 
    end
    if movematra=="auto" and
       script=="mlym" or
@@ -32273,7 +32275,9 @@ local function initializeconjuncts(tfmdata,value)
    end
    devanagari.conjuncts=conjuncts
    devanagari.movematra=movematra
+   if trace_steps then
     report("conjuncts %a, movematra %a",conjuncts,movematra)
+   end
   end
  end
 end
@@ -33598,21 +33602,25 @@ local function analyze_next_chars_one(c,font,variant)
      already_below_mark=true
     elseif post_mark[v] and not already_post_mark then
      already_post_mark=true
-    elseif devanagarihash[font].conjuncts=="quit" then
+    elseif devanagarihash[font].conjuncts=="continue" then
+    else
      return c
     end
    end
   else
-   if devanagarihash[font].conjuncts=="quit" then
-    return c
-   elseif pre_mark[v] and not already_pre_mark then
+   if pre_mark[v] and not already_pre_mark then
     already_pre_mark=true
    elseif post_mark[v] and not already_post_mark then
-    already_post_mark=true
+    if devanagarihash[font].conjuncts=="mixed" then
+     return c
+    else
+     already_post_mark=true
+    end
    elseif below_mark[v] and not already_below_mark then
     already_below_mark=true
    elseif above_mark[v] and not already_above_mark then
     already_above_mark=true
+   elseif devanagarihash[font].conjuncts=="continue" then
    else
     return c
    end
@@ -33799,21 +33807,25 @@ local function analyze_next_chars_two(c,font)
       already_below_mark=true
      elseif post_mark[v] and not already_post_mark then
       already_post_mark=true
-     elseif devanagarihash[font].conjuncts=="quit" then
+     elseif devanagarihash[font].conjuncts=="continue" then
+     else
       return c
      end
     end
    else
-    if devanagarihash[font].conjuncts=="quit" then
-     return c
-    elseif pre_mark[v] and not already_pre_mark then
+    if pre_mark[v] and not already_pre_mark then
      already_pre_mark=true
     elseif post_mark[v] and not already_post_mark then
-     already_post_mark=true
+     if devanagarihash[font].conjuncts=="mixed" then
+      return c
+     else
+      already_post_mark=true
+     end
     elseif below_mark[v] and not already_below_mark then
      already_below_mark=true
     elseif above_mark[v] and not already_above_mark then
      already_above_mark=true
+    elseif devanagarihash[font].conjuncts=="continue" then
     else
      return c
     end
-- 
cgit v1.2.3