diff options
Diffstat (limited to 'tex/context/base/mkxl/typo-dha.lmt')
-rw-r--r-- | tex/context/base/mkxl/typo-dha.lmt | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/tex/context/base/mkxl/typo-dha.lmt b/tex/context/base/mkxl/typo-dha.lmt new file mode 100644 index 000000000..e1a6662c4 --- /dev/null +++ b/tex/context/base/mkxl/typo-dha.lmt @@ -0,0 +1,481 @@ +if not modules then modules = { } end modules ['typo-dha'] = { + version = 1.001, + comment = "companion to typo-dir.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- Some analysis by Idris: +-- +-- 1. Assuming the reading- vs word-order distinction (bidi-char types) is governing; +-- 2. Assuming that 'ARAB' represents an actual arabic string in raw input order, not word-order; +-- 3. Assuming that 'BARA' represent the correct RL word order; +-- +-- Then we have, with input: LATIN ARAB +-- +-- \textdirection 1 LATIN ARAB => LATIN BARA +-- \textdirection 1 LATIN ARAB => LATIN BARA +-- \textdirection 1 LRO LATIN ARAB => LATIN ARAB +-- \textdirection 1 LRO LATIN ARAB => LATIN ARAB +-- \textdirection 1 RLO LATIN ARAB => NITAL ARAB +-- \textdirection 1 RLO LATIN ARAB => NITAL ARAB + +-- elseif d == "es" then -- European Number Separator +-- elseif d == "et" then -- European Number Terminator +-- elseif d == "cs" then -- Common Number Separator +-- elseif d == "nsm" then -- Non-Spacing Mark +-- elseif d == "bn" then -- Boundary Neutral +-- elseif d == "b" then -- Paragraph Separator +-- elseif d == "s" then -- Segment Separator +-- elseif d == "ws" then -- Whitespace +-- elseif d == "on" then -- Other Neutrals + +-- todo : use new dir functions +-- todo : make faster +-- todo : move dir info into nodes +-- todo : swappable tables and floats i.e. start-end overloads (probably loop in builders) + +-- I removed the original tracing code and now use the colorful one. If I ever want to change +-- something I will just inject prints for tracing. + +local nodes, node = nodes, node + +local trace_directions = false trackers.register("typesetters.directions", function(v) trace_directions = v end) + +local report_directions = logs.reporter("typesetting","text directions") + +local nuts = nodes.nuts + +local getnext = nuts.getnext +local getprev = nuts.getprev +local getchar = nuts.getchar +local getid = nuts.getid +local getsubtype = nuts.getsubtype +local getlist = nuts.getlist +local getattr = nuts.getattr +local getprop = nuts.getprop +local getdirection = nuts.getdirection +local isglyph = nuts.isglyph -- or ischar + +local setprop = nuts.setprop +local setstate = nuts.setstate +local setchar = nuts.setchar + +local insertnodebefore = nuts.insertbefore +local insertnodeafter = nuts.insertafter +local remove_node = nuts.remove +local endofmath = nuts.endofmath + +local startofpar = nuts.startofpar + +local nodepool = nuts.pool + +local nodecodes = nodes.nodecodes +local gluecodes = nodes.gluecodes + +local glyph_code = nodecodes.glyph +local math_code = nodecodes.math +local kern_code = nodecodes.kern +local glue_code = nodecodes.glue +local dir_code = nodecodes.dir +local par_code = nodecodes.par + +local dirvalues = nodes.dirvalues +local lefttoright_code = dirvalues.lefttoright +local righttoleft_code = dirvalues.righttoleft + +local parfillskip_code = gluecodes.parfillskip + +local new_direction = nodepool.direction + +local insert = table.insert + +local fonthashes = fonts.hashes +local fontchar = fonthashes.characters + +local chardirections = characters.directions +local charmirrors = characters.mirrors +local charclasses = characters.textclasses + +local directions = typesetters.directions +local setcolor = directions.setcolor +local getglobal = directions.getglobal + +local a_directions = attributes.private('directions') + +local strip = false + +local s_isol = fonts.analyzers.states.isol + +local function stopdir(finish) -- we could use finish directly + local n = new_direction(finish == righttoleft_code and righttoleft_code or lefttoright_code,true) + setprop(n,"direction",true) + return n +end + +local function startdir(finish) -- we could use finish directly + local n = new_direction(finish == righttoleft_code and righttoleft_code or lefttoright_code) + setprop(n,"direction",true) + return n +end + +local function nextisright(current) + current = getnext(current) + local character, id = isglyph(current) + if character then + local direction = chardirections[character] + return direction == "r" or direction == "al" or direction == "an" + end +end + +local function previsright(current) + current = getprev(current) + local character, id = isglyph(current) + if character then + local direction = chardirections[character] + return direction == "r" or direction == "al" or direction == "an" + end +end + +local function process(start) + + local head = start + local current = head + local autodir = 0 + local embedded = 0 + local override = 0 + local pardir = 0 + local textdir = 0 + local done = false + local stack = { } + local top = 0 + local obsolete = { } + local rlo = false + local lro = false + local prevattr = false + local fences = { } + + while current do + -- no isglyph here as we test for skips first + local id = getid(current) + local next = getnext(current) + if id == math_code then + current = getnext(endofmath(next)) + elseif getprop(current,"direction") then + -- this handles unhbox etc + current = next + else + local attr = getattr(current,a_directions) + if attr and attr > 0 then + if attr ~= prevattr then + if not getglobal(a) then + lro = false + rlo = false + end + prevattr = attr + end + end + local prop = true + if id == glyph_code then + if attr and attr > 0 then + local character, font = isglyph(current) + if character == 0 then + -- skip signals + -- setprop(current,"direction",true) + else + local direction = chardirections[character] + local reversed = false + if rlo or override > 0 then + if direction == "l" then + direction = "r" + reversed = true + end + elseif lro or override < 0 then + if direction == "r" or direction == "al" then + setstate(current,s_isol) -- hm + direction = "l" + reversed = true + end + end + if direction == "on" then + local mirror = charmirrors[character] + if mirror and fontchar[font][mirror] then + local class = charclasses[character] + if class == "open" then + if nextisright(current) then + setchar(current,mirror) + -- setprop(current,"direction","r") + prop = "r" + elseif autodir < 0 then + setchar(current,mirror) + -- setprop(current,"direction","r") + prop = "r" + else + mirror = false + -- setprop(current,"direction","l") + prop = "l" + end + local fencedir = autodir == 0 and textdir or autodir + fences[#fences+1] = fencedir + elseif class == "close" and #fences > 0 then + local fencedir = fences[#fences] + fences[#fences] = nil + if fencedir < 0 then + setchar(current,mirror) + -- setprop(current,"direction","r") + prop = "r" + else + -- setprop(current,"direction","l") + prop = "l" + mirror = false + end + elseif autodir < 0 then + setchar(current,mirror) + -- setprop(current,"direction","r") + prop = "r" + else + -- setprop(current,"direction","l") + prop = "l" + mirror = false + end + else + -- setprop(current,"direction",true) + end + if trace_directions then + setcolor(current,direction,false,mirror) + end + elseif direction == "l" then + if trace_directions then + setcolor(current,"l",reversed) + end + -- setprop(current,"direction","l") + prop = "l" + elseif direction == "r" then + if trace_directions then + setcolor(current,"r",reversed) + end + -- setprop(current,"direction","r") + prop = "r" + elseif direction == "en" then -- european number + if trace_directions then + setcolor(current,"l") + end + -- setprop(current,"direction","l") + prop = "l" + elseif direction == "al" then -- arabic letter + if trace_directions then + setcolor(current,"r") + end + -- setprop(current,"direction","r") + prop = "r" + elseif direction == "an" then -- arabic number + -- needs a better scanner as it can be a float + if trace_directions then + setcolor(current,"l") -- was r + end + -- setprop(current,"direction","n") -- was r + prop = "n" + elseif direction == "lro" then -- Left-to-Right Override -> right becomes left + top = top + 1 + stack[top] = { override, embedded } + override = -1 + obsolete[#obsolete+1] = current + goto obsolete + elseif direction == "rlo" then -- Right-to-Left Override -> left becomes right + top = top + 1 + stack[top] = { override, embedded } + override = 1 + obsolete[#obsolete+1] = current + goto obsolete + elseif direction == "lre" then -- Left-to-Right Embedding -> lefttoright_code + top = top + 1 + stack[top] = { override, embedded } + embedded = 1 + obsolete[#obsolete+1] = current + goto obsolete + elseif direction == "rle" then -- Right-to-Left Embedding -> righttoleft_code + top = top + 1 + stack[top] = { override, embedded } + embedded = -1 + obsolete[#obsolete+1] = current + goto obsolete + elseif direction == "pdf" then -- Pop Directional Format + if top > 0 then + local s = stack[top] + override = s[1] + embedded = s[2] + top = top - 1 + else + override = 0 + embedded = 0 + end + obsolete[#obsolete+1] = current + goto obsolete + elseif trace_directions then + setcolor(current) + -- setprop(current,"direction",true) + else + -- setprop(current,"direction",true) + end + end + else + -- setprop(current,"direction",true) + end + elseif id == glue_code then + if getsubtype(current) == parfillskip_code then + -- setprop(current,"direction","!") + prop = "!" + else + -- setprop(current,"direction","g") + prop = "g" + end + elseif id == kern_code then + -- setprop(current,"direction","k") + prop = "k" + elseif id == dir_code then + local direction, pop = getdirection(current) + if direction == righttoleft_code then + if not pop then + autodir = -1 + elseif embedded and embedded~= 0 then + autodir = embedded + else + autodir = 0 + end + elseif direction == lefttoright_code then + if not pop then + autodir = 1 + elseif embedded and embedded~= 0 then + autodir = embedded + else + autodir = 0 + end + end + textdir = autodir + -- setprop(current,"direction",true) + elseif id == par_code and startofpar(current) then + local direction = getdirection(current) + if direction == righttoleft_code then + autodir = -1 + elseif direction == lefttoright_code then + autodir = 1 + end + pardir = autodir + textdir = pardir + -- setprop(current,"direction",true) + else + -- setprop(current,"direction",true) + end + setprop(current,"direction",prop) + ::obsolete:: + current = next + end + end + + -- todo: track if really needed + -- todo: maybe we need to set the property (as it can be a copied list) + + if done and strip then + local n = #obsolete + if n > 0 then + for i=1,n do + remove_node(head,obsolete[i],true) + end + if trace_directions then + report_directions("%s character nodes removed",n) + end + end + end + + local state = false + local last = false + local collapse = true + current = head + + -- todo: textdir + -- todo: inject before parfillskip + + while current do + local id = getid(current) + if id == math_code then + -- todo: this might be tricky nesting + current = getnext(endofmath(getnext(current))) + else + local cp = getprop(current,"direction") + if cp == "n" then + local swap = state == "r" + if swap then + head = insertnodebefore(head,current,startdir(lefttoright_code)) + end + setprop(current,"direction",true) + while true do + local n = getnext(current) + if n and getprop(n,"direction") == "n" then + current = n + setprop(current,"direction",true) + else + break + end + end + if swap then + head, current = insertnodeafter(head,current,stopdir(lefttoright_code)) + end + elseif cp == "l" then + if state ~= "l" then + if state == "r" then + head = insertnodebefore(head,last or current,stopdir(righttoleft_code)) + end + head = insertnodebefore(head,current,startdir(lefttoright_code)) + state = "l" + done = true + end + last = false + elseif cp == "r" then + if state ~= "r" then + if state == "l" then + head = insertnodebefore(head,last or current,stopdir(lefttoright_code)) + end + head = insertnodebefore(head,current,startdir(righttoleft_code)) + state = "r" + done = true + end + last = false + elseif collapse then + if cp == "k" or cp == "g" then + last = last or current + else + last = false + end + else + if state == "r" then + head = insertnodebefore(head,current,stopdir(righttoleft_code)) + elseif state == "l" then + head = insertnodebefore(head,current,stopdir(lefttoright_code)) + end + state = false + last = false + end + setprop(current,"direction",true) + end + local next = getnext(current) + if next then + current = next + else + local sd = (state == "r" and stopdir(righttoleft_code)) or (state == "l" and stopdir(lefttoright_code)) + if sd then + if id == glue_code and getsubtype(current) == parfillskip_code then + head = insertnodebefore(head,current,sd) + else + head = insertnodeafter(head,current,sd) + end + end + break + end + end + + return head + +end + +directions.installhandler(interfaces.variables.default,process) |