summaryrefslogtreecommitdiff
path: root/tex/context/base/typo-dua.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/typo-dua.lua')
-rw-r--r--tex/context/base/typo-dua.lua758
1 files changed, 0 insertions, 758 deletions
diff --git a/tex/context/base/typo-dua.lua b/tex/context/base/typo-dua.lua
deleted file mode 100644
index ec85a3d9f..000000000
--- a/tex/context/base/typo-dua.lua
+++ /dev/null
@@ -1,758 +0,0 @@
-if not modules then modules = { } end modules ['typo-dua'] = {
- version = 1.001,
- comment = "companion to typo-dir.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team / See below",
- license = "see context related readme files / whatever applies",
- comment = "Unicode bidi (sort of) variant a",
- derived = "derived from t-bidi by Khaled Hosny who derived from minibidi.c by Arabeyes",
-}
-
--- Comment by Khaled Hosny:
---
--- This code started as a line for line translation of Arabeyes' minibidi.c from C to Lua,
--- excluding parts that of no use to us like shaping. The C code is Copyright (c) 2004
--- Ahmad Khalifa, and is distributed under the MIT Licence. The full license text can be
--- found at: http://svn.arabeyes.org/viewvc/projects/adawat/minibidi/LICENCE.
---
--- Comment by Hans Hagen:
---
--- The initial conversion to Lua has been done by Khaled Hosny. As a first step I optimized the
--- code (to suit todays context mkiv). Next I fixed the foreign object handling, for instance,
--- we can skip over math but we need to inject before the open math node and after the close node,
--- so we need to keep track of the endpoint. After I fixed that bit I realized that it was possible
--- to generalize the object skipper if only because it saves memory (and processing time). The
--- current implementation is about three times as fast (roughly measured) and I can probably squeeze
--- out some more, only to sacrifice soem when I start adding features. A next stage will be to have
--- more granularity in foreign objects. Of course all errors are mine. I'll also added the usual bit
--- of context tracing and reshuffled some code. A memory optimization is on the agenda (already sort
--- of prepared). It is no longer line by line.
---
--- The first implementation of bidi in context started out from examples of mixed usage (including
--- more than text) with an at that point bugged r2l support. It has some alternatives for letting
--- the tex markup having a bit higher priority. I will probably add some local (style driven)
--- overrides to the following code as well. It also means that we can selectively enable and disable
--- the parser (because a document wide appliance migh tnot be what we want). This will bring a
--- slow down but not that much. (I need to check with Idris why we have things like isol there.)
---
--- We'll probably keep multiple methods around (this is just a side track of improving the already
--- available scanner). I need to look into the changed unicode recomendations anyway as a first
--- impression is that some fuzzyness has been removed. I finally need to spend time on those specs. So,
--- there will be a third variant (written from scratch) so some point. The fun about TeX is that we
--- can provide alternative solutions (given that it doesn't bloat the engine!)
---
--- A test with some hebrew, mixed with hboxes with latin/hebrew and simple math. In fact this triggered
--- playing with bidi again:
---
--- 0.11 : nothing
--- 0.14 : 0.03 node list only, one pass
--- 0.23 : 0.12 close to unicode bidi, multipass
--- 0.44 : 0.33 original previous
---
--- todo: check for introduced errors
--- todo: reuse list, we have size, so we can just change values (and auto allocate when not there)
--- todo: reuse the stack
--- todo: no need for a max check
--- todo: collapse bound similar ranges (not ok yet)
--- tood: combine some sweeps
---
--- This one wil get frozen (or if needed in sync with basic t-bidi) and I will explore more options
--- in typo-dub.lua. There I might also be able to improve performance a bit.
-
-local insert, remove, unpack, concat = table.insert, table.remove, table.unpack, table.concat
-local utfchar = utf.char
-local formatters = string.formatters
-
-local directiondata = characters.directions
-local mirrordata = characters.mirrors
-
-local remove_node = nodes.remove
-local insert_node_after = nodes.insert_after
-local insert_node_before = nodes.insert_before
-
-local nodepool = nodes.pool
-local new_textdir = nodepool.textdir
-
-local nodecodes = nodes.nodecodes
-local whatsitcodes = nodes.whatsitcodes
-local skipcodes = nodes.skipcodes
-
-local glyph_code = nodecodes.glyph
-local glue_code = nodecodes.glue
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local math_code = nodecodes.math
-local whatsit_code = nodecodes.whatsit
-local dir_code = whatsitcodes.dir
-local localpar_code = whatsitcodes.localpar
-local parfillskip_code = skipcodes.skipcodes
-
------ object_replacement = 0xFFFC -- object replacement character
-local maximum_stack = 60 -- probably spec but not needed
-
-local directions = typesetters.directions
-local setcolor = directions.setcolor
-
-local a_directions = attributes.private('directions')
-
-local remove_controls = true directives.register("typesetters.directions.one.removecontrols",function(v) remove_controls = v end)
-
-local trace_directions = false trackers .register("typesetters.directions.one", function(v) trace_directions = v end)
-local trace_details = false trackers .register("typesetters.directions.one.details", function(v) trace_details = v end)
-
-local report_directions = logs.reporter("typesetting","directions one")
-
-local whitespace = {
- lre = true,
- rle = true,
- lro = true,
- rlo = true,
- pdf = true,
- bn = true,
- ws = true,
-}
-
-local b_s_ws_on = {
- b = true,
- s = true,
- ws = true,
- on = true
-}
-
--- tracing
-
-local function show_list(list,size,what)
- local what = what or "direction"
- local joiner = utfchar(0x200C)
- local result = { }
- for i=1,size do
- local entry = list[i]
- local character = entry.char
- local direction = entry[what]
- if character == 0xFFFC then
- local first = entry.id
- local last = entry.last
- local skip = entry.skip
- if last then
- result[i] = formatters["%-3s:%s %s..%s (%i)"](direction,joiner,nodecodes[first],nodecodes[last],skip or 0)
- else
- result[i] = formatters["%-3s:%s %s (%i)"](direction,joiner,nodecodes[first],skip or 0)
- end
- elseif character >= 0x202A and character <= 0x202C then
- result[i] = formatters["%-3s:%s %U"](direction,joiner,character)
- else
- result[i] = formatters["%-3s:%s %c %U"](direction,joiner,character,character)
- end
- end
- return concat(result,joiner .. " | " .. joiner)
-end
-
--- preparation
-
-local function show_done(list,size)
- local joiner = utfchar(0x200C)
- local result = { }
- for i=1,size do
- local entry = list[i]
- local character = entry.char
- local begindir = entry.begindir
- local enddir = entry.enddir
- if begindir then
- result[#result+1] = formatters["<%s>"](begindir)
- end
- if entry.remove then
- -- continue
- elseif character == 0xFFFC then
- result[#result+1] = formatters["<%s>"]("?")
- elseif character == 0x0020 then
- result[#result+1] = formatters["<%s>"](" ")
- elseif character >= 0x202A and character <= 0x202C then
- result[#result+1] = formatters["<%s>"](entry.original)
- else
- result[#result+1] = utfchar(character)
- end
- if enddir then
- result[#result+1] = formatters["<%s>"](enddir)
- end
- end
- return concat(result,joiner)
-end
-
--- keeping the list and overwriting doesn't save much runtime, only a few percent
--- char is only used for mirror, so in fact we can as well only store it for
--- glyphs only
-
-local function build_list(head) -- todo: store node pointer ... saves loop
- -- P1
- local current = head
- local list = { }
- local size = 0
- while current do
- size = size + 1
- local id = current.id
- if id == glyph_code then
- local chr = current.char
- local dir = directiondata[chr]
- list[size] = { char = chr, direction = dir, original = dir, level = 0 }
- current = current.next
- elseif id == glue_code then
- list[size] = { char = 0x0020, direction = "ws", original = "ws", level = 0 }
- current = current.next
- elseif id == whatsit_code and current.subtype == dir_code then
- local dir = current.dir
- if dir == "+TLT" then
- list[size] = { char = 0x202A, direction = "lre", original = "lre", level = 0 }
- elseif dir == "+TRT" then
- list[size] = { char = 0x202B, direction = "rle", original = "rle", level = 0 }
- elseif dir == "-TLT" or dir == "-TRT" then
- list[size] = { char = 0x202C, direction = "pdf", original = "pdf", level = 0 }
- else
- list[size] = { char = 0xFFFC, direction = "on", original = "on", level = 0, id = id } -- object replacement character
- end
- current = current.next
- elseif id == math_code then
- local skip = 0
- current = current.next
- while current.id ~= math_code do
- skip = skip + 1
- current = current.next
- end
- skip = skip + 1
- current = current.next
- list[size] = { char = 0xFFFC, direction = "on", original = "on", level = 0, skip = skip, id = id }
- else
- local skip = 0
- local last = id
- current = current.next
- while n do
- local id = current.id
- if id ~= glyph_code and id ~= glue_code and not (id == whatsit_code and current.subtype == dir_code) then
- skip = skip + 1
- last = id
- current = current.next
- else
- break
- end
- end
- if id == last then
- list[size] = { char = 0xFFFC, direction = "on", original = "on", level = 0, skip = skip, id = id }
- else
- list[size] = { char = 0xFFFC, direction = "on", original = "on", level = 0, skip = skip, id = id, last = last }
- end
- end
- end
- return list, size
-end
-
--- the action
-
--- local function find_run_limit_et(list,run_start,limit)
--- local run_limit = run_start
--- local i = run_start
--- while i <= limit and list[i].direction == "et" do
--- run_limit = i
--- i = i + 1
--- end
--- return run_limit
--- end
-
-local function find_run_limit_et(list,start,limit) -- returns last match
- for i=start,limit do
- if list[i].direction == "et" then
- start = i
- else
- return start
- end
- end
- return start
-end
-
--- local function find_run_limit_b_s_ws_on(list,run_start,limit)
--- local run_limit = run_start
--- local i = run_start
--- while i <= limit and b_s_ws_on[list[i].direction] do
--- run_limit = i
--- i = i + 1
--- end
--- return run_limit
--- end
-
-local function find_run_limit_b_s_ws_on(list,start,limit)
- for i=start,limit do
- if b_s_ws_on[list[i].direction] then
- start = i
- else
- return start
- end
- end
- return start
-end
-
-local function get_baselevel(head,list,size) -- todo: skip if first is object (or pass head and test for local_par)
- if head.id == whatsit_code and head.subtype == localpar_code then
- if head.dir == "TRT" then
- return 1, "TRT", true
- else
- return 0, "TLT", true
- end
- else
- -- P2, P3
- for i=1,size do
- local entry = list[i]
- local direction = entry.direction
- if direction == "r" or direction == "al" then
- return 1, "TRT", true
- elseif direction == "l" then
- return 0, "TLT", true
- end
- end
- return 0, "TLT", false
- end
-end
-
-local function resolve_explicit(list,size,baselevel)
- -- X1
- local level = baselevel
- local override = "on"
- local stack = { }
- local nofstack = 0
- for i=1,size do
- local entry = list[i]
- local direction = entry.direction
- -- X2
- if direction == "rle" then
- if nofstack < maximum_stack then
- nofstack = nofstack + 1
- stack[nofstack] = { level, override }
- level = level + (level % 2 == 1 and 2 or 1) -- least_greater_odd(level)
- override = "on"
- entry.level = level
- entry.direction = "bn"
- entry.remove = true
- elseif trace_directions then
- report_directions("stack overflow at position %a with direction %a",i,direction)
- end
- -- X3
- elseif direction == "lre" then
- if nofstack < maximum_stack then
- nofstack = nofstack + 1
- stack[nofstack] = { level, override }
- level = level + (level % 2 == 1 and 1 or 2) -- least_greater_even(level)
- override = "on"
- entry.level = level
- entry.direction = "bn"
- entry.remove = true
- elseif trace_directions then
- report_directions("stack overflow at position %a with direction %a",i,direction)
- end
- -- X4
- elseif direction == "rlo" then
- if nofstack < maximum_stack then
- nofstack = nofstack + 1
- stack[nofstack] = { level, override }
- level = level + (level % 2 == 1 and 2 or 1) -- least_greater_odd(level)
- override = "r"
- entry.level = level
- entry.direction = "bn"
- entry.remove = true
- elseif trace_directions then
- report_directions("stack overflow at position %a with direction %a",i,direction)
- end
- -- X5
- elseif direction == "lro" then
- if nofstack < maximum_stack then
- nofstack = nofstack + 1
- stack[nofstack] = { level, override }
- level = level + (level % 2 == 1 and 1 or 2) -- least_greater_even(level)
- override = "l"
- entry.level = level
- entry.direction = "bn"
- entry.remove = true
- elseif trace_directions then
- report_directions("stack overflow at position %a with direction %a",i,direction)
- end
- -- X7
- elseif direction == "pdf" then
- if nofstack < maximum_stack then
- local stacktop = stack[nofstack]
- nofstack = nofstack - 1
- level = stacktop[1]
- override = stacktop[2]
- entry.level = level
- entry.direction = "bn"
- entry.remove = true
- elseif trace_directions then
- report_directions("stack overflow at position %a with direction %a",i,direction)
- end
- -- X6
- else
- entry.level = level
- if override ~= "on" then
- entry.direction = override
- end
- end
- end
- -- X8 (reset states and overrides after paragraph)
-end
-
-local function resolve_weak(list,size,start,limit,sor,eor)
- -- W1
- for i=start,limit do
- local entry = list[i]
- if entry.direction == "nsm" then
- if i == start then
- entry.direction = sor
- else
- entry.direction = list[i-1].direction
- end
- end
- end
- -- W2
- for i=start,limit do
- local entry = list[i]
- if entry.direction == "en" then
- for j=i-1,start,-1 do
- local prev = list[j]
- local direction = prev.direction
- if direction == "al" then
- entry.direction = "an"
- break
- elseif direction == "r" or direction == "l" then
- break
- end
- end
- end
- end
- -- W3
- for i=start,limit do
- local entry = list[i]
- if entry.direction == "al" then
- entry.direction = "r"
- end
- end
- -- W4
- for i=start+1,limit-1 do
- local entry = list[i]
- local direction = entry.direction
- if direction == "es" then
- if list[i-1].direction == "en" and list[i+1].direction == "en" then
- entry.direction = "en"
- end
- elseif direction == "cs" then
- local prevdirection = list[i-1].direction
- if prevdirection == "en" then
- if list[i+1].direction == "en" then
- entry.direction = "en"
- end
- elseif prevdirection == "an" and list[i+1].direction == "an" then
- entry.direction = "an"
- end
- end
- end
- -- W5
- local i = start
- while i <= limit do
- if list[i].direction == "et" then
- local runstart = i
- local runlimit = find_run_limit_et(list,runstart,limit) -- when moved inline we can probably collapse a lot
- local rundirection = runstart == start and sor or list[runstart-1].direction
- if rundirection ~= "en" then
- rundirection = runlimit == limit and eor or list[runlimit+1].direction
- end
- if rundirection == "en" then
- for j=runstart,runlimit do
- list[j].direction = "en"
- end
- end
- i = runlimit
- end
- i = i + 1
- end
- -- W6
- for i=start,limit do
- local entry = list[i]
- local direction = entry.direction
- if direction == "es" or direction == "et" or direction == "cs" then
- entry.direction = "on"
- end
- end
- -- W7
- for i=start,limit do
- local entry = list[i]
- if entry.direction == "en" then
- local prev_strong = sor
- for j=i-1,start,-1 do
- local direction = list[j].direction
- if direction == "l" or direction == "r" then
- prev_strong = direction
- break
- end
- end
- if prev_strong == "l" then
- entry.direction = "l"
- end
- end
- end
-end
-
-local function resolve_neutral(list,size,start,limit,sor,eor)
- -- N1, N2
- for i=start,limit do
- local entry = list[i]
- if b_s_ws_on[entry.direction] then
- local leading_direction, trailing_direction, resolved_direction
- local runstart = i
- local runlimit = find_run_limit_b_s_ws_on(list,runstart,limit)
- if runstart == start then
- leading_direction = sor
- else
- leading_direction = list[runstart-1].direction
- if leading_direction == "en" or leading_direction == "an" then
- leading_direction = "r"
- end
- end
- if runlimit == limit then
- trailing_direction = eor
- else
- trailing_direction = list[runlimit+1].direction
- if trailing_direction == "en" or trailing_direction == "an" then
- trailing_direction = "r"
- end
- end
- if leading_direction == trailing_direction then
- -- N1
- resolved_direction = leading_direction
- else
- -- N2 / does the weird period
- resolved_direction = entry.level % 2 == 1 and "r" or "l" -- direction_of_level(entry.level)
- end
- for j=runstart,runlimit do
- list[j].direction = resolved_direction
- end
- i = runlimit
- end
- i = i + 1
- end
-end
-
-local function resolve_implicit(list,size,start,limit,sor,eor)
- -- I1
- for i=start,limit do
- local entry = list[i]
- local level = entry.level
- if level % 2 ~= 1 then -- not odd(level)
- local direction = entry.direction
- if direction == "r" then
- entry.level = level + 1
- elseif direction == "an" or direction == "en" then
- entry.level = level + 2
- end
- end
- end
- -- I2
- for i=start,limit do
- local entry = list[i]
- local level = entry.level
- if level % 2 == 1 then -- odd(level)
- local direction = entry.direction
- if direction == "l" or direction == "en" or direction == "an" then
- entry.level = level + 1
- end
- end
- end
-end
-
-local function resolve_levels(list,size,baselevel)
- -- X10
- local start = 1
- while start < size do
- local level = list[start].level
- local limit = start + 1
- while limit < size and list[limit].level == level do
- limit = limit + 1
- end
- local prev_level = start == 1 and baselevel or list[start-1].level
- local next_level = limit == size and baselevel or list[limit+1].level
- local sor = (level > prev_level and level or prev_level) % 2 == 1 and "r" or "l" -- direction_of_level(max(level,prev_level))
- local eor = (level > next_level and level or next_level) % 2 == 1 and "r" or "l" -- direction_of_level(max(level,next_level))
- -- W1 .. W7
- resolve_weak(list,size,start,limit,sor,eor)
- -- N1 .. N2
- resolve_neutral(list,size,start,limit,sor,eor)
- -- I1 .. I2
- resolve_implicit(list,size,start,limit,sor,eor)
- start = limit
- end
- -- L1
- for i=1,size do
- local entry = list[i]
- local direction = entry.original
- -- (1)
- if direction == "s" or direction == "b" then
- entry.level = baselevel
- -- (2)
- for j=i-1,1,-1 do
- local entry = list[j]
- if whitespace[entry.original] then
- entry.level = baselevel
- else
- break
- end
- end
- end
- end
- -- (3)
- for i=size,1,-1 do
- local entry = list[i]
- if whitespace[entry.original] then
- entry.level = baselevel
- else
- break
- end
- end
- -- L4
- for i=1,size do
- local entry = list[i]
- if entry.level % 2 == 1 then -- odd(entry.level)
- local mirror = mirrordata[entry.char]
- if mirror then
- entry.mirror = mirror
- end
- end
- end
-end
-
-local function insert_dir_points(list,size)
- -- L2, but no actual reversion is done, we simply annotate where
- -- begindir/endddir node will be inserted.
- local maxlevel = 0
- local finaldir = false
- for i=1,size do
- local level = list[i].level
- if level > maxlevel then
- maxlevel = level
- end
- end
- for level=0,maxlevel do
- local started = false
- local begindir = nil
- local enddir = nil
- if level % 2 == 1 then
- begindir = "+TRT"
- enddir = "-TRT"
- else
- begindir = "+TLT"
- enddir = "-TLT"
- end
- for i=1,size do
- local entry = list[i]
- if entry.level >= level then
- if not started then
- entry.begindir = begindir
- started = true
- end
- else
- if started then
- list[i-1].enddir = enddir
- started = false
- end
- end
- end
- -- make sure to close the run at end of line
- if started then
- finaldir = enddir
- end
- end
- if finaldir then
- list[size].enddir = finaldir
- end
-end
-
-local function apply_to_list(list,size,head,pardir)
- local index = 1
- local current = head
- local done = false
- while current do
- if index > size then
- report_directions("fatal error, size mismatch")
- break
- end
- local id = current.id
- local entry = list[index]
- local begindir = entry.begindir
- local enddir = entry.enddir
- if id == glyph_code then
- local mirror = entry.mirror
- if mirror then
- current.char = mirror
- end
- if trace_directions then
- local direction = entry.direction
- setcolor(current,direction,direction ~= entry.original,mirror)
- end
- elseif id == hlist_code or id == vlist_code then
- current.dir = pardir -- is this really needed?
- elseif id == glue_code then
- if enddir and current.subtype == parfillskip_code then
- -- insert the last enddir before \parfillskip glue
- head = insert_node_before(head,current,new_textdir(enddir))
- enddir = false
- done = true
- end
- elseif id == whatsit_code then
- if begindir and current.subtype == localpar_code then
- -- local_par should always be the 1st node
- head, current = insert_node_after(head,current,new_textdir(begindir))
- begindir = nil
- done = true
- end
- end
- if begindir then
- head = insert_node_before(head,current,new_textdir(begindir))
- done = true
- end
- local skip = entry.skip
- if skip and skip > 0 then
- for i=1,skip do
- current = current.next
- end
- end
- if enddir then
- head, current = insert_node_after(head,current,new_textdir(enddir))
- done = true
- end
- if not entry.remove then
- current = current.next
- elseif remove_controls then
- -- X9
- head, current = remove_node(head,current,true)
- done = true
- else
- current = current.next
- end
- index = index + 1
- end
- return head, done
-end
-
-local function process(head)
- local list, size = build_list(head)
- local baselevel, pardir, dirfound = get_baselevel(head,list,size) -- we always have an inline dir node in context
- if not dirfound and trace_details then
- report_directions("no initial direction found, gambling")
- end
- if trace_details then
- report_directions("before : %s",show_list(list,size,"original"))
- end
- resolve_explicit(list,size,baselevel)
- resolve_levels(list,size,baselevel)
- insert_dir_points(list,size)
- if trace_details then
- report_directions("after : %s",show_list(list,size,"direction"))
- report_directions("result : %s",show_done(list,size))
- end
- head, done = apply_to_list(list,size,head,pardir)
- return head, done
-end
-
-directions.installhandler(interfaces.variables.one,process)