OSDN Git Service

added \ltjghostjachar
[luatex-ja/luatexja.git] / src / ltj-adjust.lua
index 54af8cf..87c85dd 100644 (file)
@@ -1,11 +1,12 @@
 --
 -- ltj-adjust.lua
 --
-luatexja.load_module('jfont');     local ltjf = luatexja.jfont
-luatexja.load_module('jfmglue');   local ltjj = luatexja.jfmglue
-luatexja.load_module('stack');     local ltjs = luatexja.stack
-luatexja.load_module('direction'); local ltjd = luatexja.direction
-luatexja.load_module('lineskip');  local ltjl = luatexja.lineskip
+luatexja.load_module 'base';      local ltjb = luatexja.base
+luatexja.load_module 'jfont';     local ltjf = luatexja.jfont
+luatexja.load_module 'jfmglue';   local ltjj = luatexja.jfmglue
+luatexja.load_module 'stack';     local ltjs = luatexja.stack
+luatexja.load_module 'direction'; local ltjd = luatexja.direction
+luatexja.load_module 'lineskip';  local ltjl = luatexja.lineskip
 luatexja.adjust = luatexja.adjust or {}
 
 local to_node = node.direct.tonode
@@ -29,12 +30,12 @@ local has_attr = node.direct.has_attribute
 local set_attr = node.direct.set_attribute
 local insert_after = node.direct.insert_after
 
-local id_glyph = node.id('glyph')
-local id_kern = node.id('kern')
-local id_hlist = node.id('hlist')
-local id_glue  = node.id('glue')
-local id_whatsit = node.id('whatsit')
-local id_penalty = node.id('penalty')
+local id_glyph   = node.id 'glyph'
+local id_kern    = node.id 'kern'
+local id_hlist   = node.id 'hlist'
+local id_glue    = node.id 'glue'
+local id_whatsit = node.id 'whatsit'
+local id_penalty = node.id 'penalty'
 local attr_icflag = luatexbase.attributes['ltj@icflag']
 local attr_jchar_class = luatexbase.attributes['ltj@charclass']
 local lang_ja = luatexja.lang_ja
@@ -69,9 +70,9 @@ do
    local function make_priority_table(glue_sign)
       for i,_ in pairs(tmp) do tmp[i]=nil end
       if glue_sign==2 then -- shrink
-        for i=0,63 do tmp[#tmp+1] = { (i%8)-4, FROM_JFM+i } end
+         for i=0,63 do tmp[#tmp+1] = { (i%8)-4, FROM_JFM+i } end
       else -- stretch
-        for i=0,63 do tmp[#tmp+1] = { math.floor(i/8)-4, FROM_JFM+i } end
+         for i=0,63 do tmp[#tmp+1] = { math.floor(i/8)-4, FROM_JFM+i } end
       end    
       local pt = priority_table[glue_sign]
       tmp[#tmp+1] = { pt[2]/10, XKANJI_SKIP }
@@ -82,8 +83,8 @@ do
       table.sort(tmp, cmp)
       local a, m, n = at2pr[glue_sign], 10000000, 0
       for i=1,#tmp do
-        if tmp[i][1]<m then n,m = n+1,tmp[i][1] end
-        a[tmp[i][2]] = n
+         if tmp[i][1]<m then n,m = n+1,tmp[i][1] end
+         a[tmp[i][2]] = n
       end
       local o = a[-1]
       priority_num[glue_sign] = n
@@ -110,14 +111,14 @@ function get_total_stretched(p)
    for q in node_traverse_id(id_glue, ph) do
       local a = getfield(q, 'stretch_order')
       if a==0 then
-        local b = at2pr_st[get_attr_icflag(q)]; 
-        total_st[b] = total_st[b]+getfield(q, 'stretch')
+         local b = at2pr_st[get_attr_icflag(q)]; 
+         total_st[b] = total_st[b]+getfield(q, 'stretch')
       end
       total_st[a*65536] = total_st[a]+getfield(q, 'stretch')
       local a = getfield(q, 'shrink_order')
       if a==0 then
-        local b = at2pr_sh[get_attr_icflag(q)]; 
-        total_sh[b] = total_sh[b]+getfield(q, 'shrink')
+         local b = at2pr_sh[get_attr_icflag(q)]; 
+         total_sh[b] = total_sh[b]+getfield(q, 'shrink')
       end
       total_sh[a*65536] = total_sh[a]+getfield(q, 'shrink')
    end
@@ -170,16 +171,16 @@ local function aw_step1(p, total)
    for i, v in ipairs(eadt) do
       local t = total - v
       if t>0 then
-        eadt_ratio[i] = {i, t/total_st[65536*total_st.order], t, v}
+         eadt_ratio[i] = {i, t/total_st[65536*total_st.order], t, v}
       else
-        eadt_ratio[i] = {i, t/total_sh[65536*total_sh.order], t, v}
+         eadt_ratio[i] = {i, t/total_sh[65536*total_sh.order], t, v}
       end
    end
    table.sort(eadt_ratio, 
    function (a,b) 
        for i=2,4 do
-          local at, bt = abs(a[i]), abs(b[i])
-          if at~=bt then return at<bt end
+           local at, bt = abs(a[i]), abs(b[i])
+           if at~=bt then return at<bt end
        end
        return a[4]<b[4]
    end)
@@ -199,9 +200,9 @@ local function aw_step1_last(p, total)
    local head = getlist(p)
    local x = node_tail(head); if not x then return total, false end
    -- x: \rightskip
-   pf = node_prev(x); if not x then return total, false end
+   local pf = node_prev(x); if not x then return total, false end
    if getid(pf) ~= id_glue or getsubtype(pf) ~= 15 then return total, false end
-   x = node_prev(node_prev(pf)); xi = getid(x)
+   x = node_prev(node_prev(pf))
    local xi, xc = getid(x)
    if xi == id_glyph and getfield(x, 'lang')==lang_ja then
       -- 和文文字
@@ -218,14 +219,14 @@ local function aw_step1_last(p, total)
       if total_st.order ~= getfield(pf, 'stretch_order') then return total, false end
       if total_st[total_st.order*65536] ~= getfield(pf, 'stretch') then return total, false end
       for i=total_st.order-1, 1, -1 do
-        if total_st[i*65536] ~= 0 then return total, false end
+         if total_st[i*65536] ~= 0 then return total, false end
       end
    end
    if total<0 and total_sh.order>0 then
       if total_sh.order ~= getfield(pf, 'shrink_order') then return total, false end
       if total_sh[total_sh.order*65536] ~= getfield(pf, 'shrink') then return total, false end
       for i=total_sh.order-1, 1, -1 do
-        if total_sh[i*65536] ~= 0 then return total, false end
+         if total_sh[i*65536] ~= 0 then return total, false end
       end
    end
    local eadt = ltjf_font_metric_table[getfont(xc)]
@@ -257,16 +258,16 @@ local function aw_step1_last(p, total)
    for i, v in ipairs(eadt) do
       local t = total - v
       if t>0 then
-        eadt_ratio[i] = {i, t/total_st[65536*total_st.order], t, v}
+         eadt_ratio[i] = {i, t/total_st[65536*total_st.order], t, v}
       else
-        eadt_ratio[i] = {i, t/total_sh[65536*total_sh.order], t, v}
+         eadt_ratio[i] = {i, t/total_sh[65536*total_sh.order], t, v}
       end
    end
    table.sort(eadt_ratio, 
    function (a,b) 
        for i=2,4 do
-          local at, bt = abs(a[i]), abs(b[i])
-          if at~=bt then return at<bt end
+           local at, bt = abs(a[i]), abs(b[i])
+           if at~=bt then return at<bt end
        end
        return a[4]<b[4]
    end)
@@ -286,7 +287,10 @@ local aw_step2, aw_step2_dummy
 do
 local node_hpack = node.direct.hpack
 local function repack(p)
+   local orig_of, orig_hfuzz, orig_hbad = tex.overfullrule, tex.hfuzz, tex.hbadness
+   tex.overfullrule=0; tex.hfuzz=1073741823; tex.hbadness=10000
    local f = node_hpack(getlist(p), getfield(p, 'width'), 'exactly')
+   tex.overfullrule=orig_of; tex.hfuzz=orig_hfuzz; tex.hbadness=orig_hbad
    setfield(f, 'head', nil)
    setfield(p, 'glue_set', getfield(f, 'glue_set'))
    setfield(p, 'glue_order', getfield(f, 'glue_order'))
@@ -302,8 +306,7 @@ local function clear_stretch(p, ind, ap, name)
    for q in node_traverse_id(id_glue, getlist(p)) do
       local f = ap[get_attr_icflag(q)]
       if f == ind then
-         setfield(q, name..'_order', 0)
-         setfield(q, name, 0)
+         setfield(q, name..'_order', 0); setfield(q, name, 0)
       end
    end
 end
@@ -312,7 +315,7 @@ local function set_stretch(p, after, before, ind, ap, name)
    if before > 0 then
       local ratio = after/before
       for q in node_traverse_id(id_glue, getlist(p)) do
-        local f = ap[get_attr_icflag(q)]
+         local f = ap[get_attr_icflag(q)]
          if (f==ind) and getfield(q, name..'_order')==0 then
             setfield(q, name, getfield(q, name)*ratio)
          end
@@ -332,7 +335,7 @@ function aw_step2(p, total, added_flag)
    total = abs(total)
    for i = 1, pnum do
       if total <= res[i] then
-        local a = at2pr[id]  
+         local a = at2pr[id]  
          for j = i+1,pnum do
             clear_stretch(p, j, a, name)
          end
@@ -349,51 +352,51 @@ local insert_lineend_kern
 do
    local insert_before = node.direct.insert_before
    local KINSOKU      = luatexja.icflag_table.KINSOKU
-   function insert_lineend_kern(head, nq, np, Bp)
+   insert_lineend_kern = function (head, nq, np, Bp)
       if nq.met then 
          local eadt = nq.met.char_type[nq.class].end_adjust
-        if not eadt then return end
-        if eadt[1]~=0 then
-           local x = node_new(id_kern, 1)
-           setfield(x, 'kern', eadt[1]); set_attr(x, attr_icflag, LINEEND)
-           insert_before(head, np.first, x)
-        end
-        local eadt_num = #eadt
-        for i=2,eadt_num do
-           local x = node_new(id_penalty)
-           setfield(x, 'penalty', 0); set_attr(x, attr_icflag, KINSOKU)
-           insert_before(head, np.first, x); Bp[#Bp+1] = x
-           local x = node_new(id_kern, 1)
-           setfield(x, 'kern', eadt[i]-eadt[i-1]); set_attr(x, attr_icflag, LINEEND)
-           insert_before(head, np.first, x)
-        end
+         if not eadt then return end
+         if eadt[1]~=0 then
+            local x = node_new(id_kern, 1)
+            setfield(x, 'kern', eadt[1]); set_attr(x, attr_icflag, LINEEND)
+            insert_before(head, np.first, x)
+         end
+         local eadt_num = #eadt
+         for i=2,eadt_num do
+            local x = node_new(id_penalty)
+            setfield(x, 'penalty', 0); set_attr(x, attr_icflag, KINSOKU)
+            insert_before(head, np.first, x); Bp[#Bp+1] = x
+            local x = node_new(id_kern, 1)
+            setfield(x, 'kern', eadt[i]-eadt[i-1]); set_attr(x, attr_icflag, LINEEND)
+            insert_before(head, np.first, x)
+         end
          if eadt_num>1 or eadt[1]~=0 then
-           local x = node_new(id_penalty)
-           setfield(x, 'penalty', 0); set_attr(x, attr_icflag, KINSOKU)
-           insert_before(head, np.first, x); Bp[#Bp+1] = x
-           local x = node_new(id_kern, 1)
-           setfield(x, 'kern', -eadt[eadt_num]); set_attr(x, attr_icflag, LINEEND)
-           insert_before(head, np.first, x)
-           local x = node_new(id_penalty)
-           setfield(x, 'penalty', 10000); set_attr(x, attr_icflag, KINSOKU)
-           insert_before(head, np.first, x); Bp[#Bp+1] = x
-        end
+            local x = node_new(id_penalty)
+            setfield(x, 'penalty', 0); set_attr(x, attr_icflag, KINSOKU)
+            insert_before(head, np.first, x); Bp[#Bp+1] = x
+            local x = node_new(id_kern, 1)
+            setfield(x, 'kern', -eadt[eadt_num]); set_attr(x, attr_icflag, LINEEND)
+            insert_before(head, np.first, x)
+            local x = node_new(id_penalty)
+            setfield(x, 'penalty', 10000); set_attr(x, attr_icflag, KINSOKU)
+            insert_before(head, np.first, x); Bp[#Bp+1] = x
+         end
       end
    end
 end
 
 local adjust_width
 do
-   local myaw_atep1, myaw_step2, myaw_step1_last
+   local myaw_step1, myaw_step2, myaw_step1_last
    local dummy =  function(p,t,n) return t, false end
    local ltjs_fast_get_stack_skip = ltjs.fast_get_stack_skip
    function adjust_width(head)
       if not head then return head end
       local last_p
       for p in node_traverse_id(id_hlist, to_direct(head)) do
-        if last_p then
-           myaw_step2(last_p, myaw_step1(last_p, get_total_stretched(last_p)))
-        end
+         if last_p then
+            myaw_step2(last_p, myaw_step1(last_p, get_total_stretched(last_p)))
+         end
          last_p = p
       end
       if last_p then
@@ -402,19 +405,20 @@ do
       return to_node(head)
    end
    local is_reg = false
-   function enable_cb(status_le, status_pr, status_lp, status_ls)
+   local function enable_cb(status_le, status_pr, status_lp, status_ls)
       if (status_le>0 or status_pr>0) and (not is_reg) then
-        luatexbase.add_to_callback('post_linebreak_filter',
-                                   adjust_width, 'Adjust width', 100)
-        is_reg = true
+         ltjb.add_to_callback('post_linebreak_filter',
+            adjust_width, 'Adjust width', 
+            luatexbase.priority_in_callback('post_linebreak_filter', 'ltj.lineskip')-1)
+         is_reg = true
       elseif is_reg and (status_le==0 and status_pr==0) then
-        luatexbase.remove_from_callback('post_linebreak_filter', 'Adjust width')
-        is_reg = false
+         luatexbase.remove_from_callback('post_linebreak_filter', 'Adjust width')
+         is_reg = false
       end
       if status_le==2 then
-        if not luatexbase.in_callback('luatexja.adjust_jfmglue', 'luatexja.adjust') then
-           luatexbase.add_to_callback('luatexja.adjust_jfmglue', insert_lineend_kern, 'luatexja.adjust')
-        end
+         if not luatexbase.in_callback('luatexja.adjust_jfmglue', 'luatexja.adjust') then
+            ltjb.add_to_callback('luatexja.adjust_jfmglue', insert_lineend_kern, 'luatexja.adjust')
+         end
          myaw_step1, myaw_step1_last = dummy, aw_step1_last
       else
          if status_le==0 then
@@ -423,16 +427,16 @@ do
             myaw_step1, myaw_step1_last = aw_step1, aw_step1_last
          end
          if luatexbase.in_callback('luatexja.adjust_jfmglue', 'luatexja.adjust') then
-           luatexbase.remove_from_callback('luatexja.adjust_jfmglue', 'luatexja.adjust')
+               luatexbase.remove_from_callback('luatexja.adjust_jfmglue', 'luatexja.adjust')
          end
       end
       myaw_step2 = (status_pr>0) and aw_step2 or aw_step2_dummy
       luatexja.lineskip.setting(
          status_lp>0 and 'profile' or 'dummy',
-        status_ls>0 and 'step' or 'dummy'
-      )      
+         status_ls>0 and 'step' or 'dummy'
+      )
    end
-   function disable_cb() -- only for compatibility
+   local function disable_cb() -- only for compatibility
        enable_cs(0,0,0,0)
    end
    luatexja.adjust.enable_cb=enable_cb
@@ -443,61 +447,89 @@ luatexja.unary_pars.adjust = function(t)
    return is_reg and 1 or 0
 end
 
+-- ----------------------------------
+local init_range
+do
+  local max, ins, sort = math.max, table.insert, table.sort
+  local function insert(package, ind, d, b, e)
+    local bound = package[2]
+    bound[b], bound[e]=true, true
+    ins(package[1], {b,e,[ind]=d})
+  end
+  local function flatten(package)
+    local bd = {} for i,_ in pairs(package[2]) do ins(bd,{i}) end
+    sort(bd, function (a,b) return a[1]<b[1] end)
+    local bdc=#bd; local t = package[1]
+    sort(t, function (a,b) return a[1]<b[1] end)
+    local bdi =1
+    for i=1,#t do
+      while bd[bdi][1]<t[i][1] do bdi=bdi+1 end
+      local j = bdi
+      while j<bdc and bd[j+1][1]<=t[i][2] do
+        for k,w in pairs(t[i]) do
+          if k>=3 then
+            bd[j][k]=bd[j][k] and max(bd[j][k],w) or w
+          end
+        end
+        j = j + 1
+      end
+    end
+    package[2]=nil; package[1]=nil; package.flatten, package.insert=nil, nil
+    bd[#bd]=nil
+    return bd
+  end
+  init_range = function ()
+    return {{},{}, insert=insert, flatten=flatten}
+  end
+end
+
 -- -----------------------------------
 luatexja.adjust.step_factor = 0.5
+luatexja.unary_pars.linestep_factor = function(t)
+   return luatexja.adjust.step_factor
+end
+luatexja.adjust.profile_hgap_factor = 1
+luatexja.unary_pars.profile_hgap_factor = function(t)
+   return luatexja.adjust.profile_hgap_factor
+end
 do
   local insert = table.insert
   local rangedimensions, max = node.direct.rangedimensions, math.max
-  function ltjl.p_profile(before, after, mirrored, bw)
-    local t = {}
-    do
-      local w_acc, d_before = 0, 0
-      local x = getlist(before); local xn = node_next(x)
-      while x do
-        local w, d
-        if xn then w, _, d= rangedimensions(before,x,xn)
-        else w, _, d= rangedimensions(before,x) end
-        if d~=d_before then
-          d_before = d; t[w_acc] = t[w_acc] or {}
-          if t[w_acc][1] then t[w_acc][1]=max(t[w_acc][1],d)
-          else t[w_acc][1]=d end
-        end
-        w_acc = w_acc + w
-        x = xn; if x then xn = node_next(x) end
-      end
-    end
-    do
-      local w_acc, h_before = 0, 0
-      local x = getlist(after); local xn = node_next(x)
-      while x do
-        local w, h, d
-        if xn then w, h, d = rangedimensions(after,x,xn)
-       else w, h,d = rangedimensions(after,x) end
-       if mirrored then h=d end
-        if h~=h_before then
-          h_before = h; t[w_acc] = t[w_acc] or {}
-          if t[w_acc][2] then t[w_acc][2]=max(t[w_acc][2],h)
-          else t[w_acc][2]=h end
-        end
-        w_acc = w_acc + w
-        x = xn; if x then xn = node_next(x) end
+  local function profile_inner(box, range, ind, vmirrored, adj)
+    local w_acc, d_before = getfield(box,'shift'), 0
+    local x = getlist(box); local xn = node_next(x)
+    while x do
+      local w, h, d
+      if xn then w, h, d= rangedimensions(box,x,xn)
+      else w, h, d= rangedimensions(box,x) end
+      if vmirrored then h=d end
+      local w_new = w_acc + w
+      if w>=0 then
+        range:insert(ind, h, w_acc-adj, w_new)
+      else
+        range:insert(ind, h, w_new-adj, w_acc)
       end
+      w_acc = w_new; x = xn; if x then xn = node_next(x) end
     end
-    local t2 = {}
-    for i,v in pairs(t) do insert(t2, { i, v[1], v[2] } ) end
-    table.sort(t2, function(a,b) return a[1]<b[1] end)
+  end  
+  function ltjl.p_profile(before, after, mirrored, bw)
+    local range, tls 
+      = init_range(), luatexja.adjust.profile_hgap_factor*tex.lineskip.width
+    profile_inner(before, range, 3, true,     tls)
+    profile_inner(after,  range, 4, mirrored, tls)
+    range = range:flatten()
     do
       local dmax, d, hmax, h, lmin = 0, 0, 0, 0, 1/0
-      for i,v in ipairs(t2) do
-        d, h = (v[2] or d), (v[3] or h)
+      for i,v in ipairs(range) do
+        d, h = (v[3] or 0), (v[4] or 0)
         if d>dmax then dmax=d end
         if h>hmax then hmax=h end
-        if (bw-h-d)<lmin then lmin=bw-h-d end
+        if bw-h-d<lmin then lmin=bw-h-d end
       end
-      if lmin==1/0 then lmin = 0 end
+      if lmin==1/0 then lmin = bw end
       return lmin, 
          bw - lmin - getfield(before, 'depth')
-             - getfield(after, mirrored and 'depth' or 'height')
+            - getfield(after, mirrored and 'depth' or 'height')
     end
   end
 end
@@ -506,9 +538,12 @@ do
   local ltja = luatexja.adjust
   local copy_glue = ltjl.copy_glue
   local floor, max = math.floor, math.max
-  function ltjl.l_step(dist, g, adj, normal, bw)
+  function ltjl.l_step(dist, g, adj, normal, bw, loc)
+    if loc=='alignment' then
+      return ltjl.l_dummy(dist, g, adj, normal, bw, loc)
+    end
     if dist < tex.lineskiplimit then
-       local f = max(1, bw*ltja.step_factor)
+    local f = max(1, bw*ltja.step_factor)
        copy_glue(g, tex.baselineskip, 1, normal - f * floor((dist-tex.lineskip.width)/f))
     else
        copy_glue(g, tex.baselineskip, 2, normal)
@@ -516,4 +551,40 @@ do
   end
 end
 
-
+do
+  local ltja = luatexja.adjust
+  local sid_user = node.subtype 'user_defined'
+  local node_remove = node.direct.remove
+  local node_write = node.direct.write
+  local GHOST_JACHAR = luatexbase.newuserwhatsitid('ghost of a jachar',  'luatexja')
+  luatexja.userid_table.GHOST_JACHAR = GHOST_JACHAR
+  function ltja.create_ghost_jachar_node(cl)
+    local tn = node_new(id_whatsit, sid_user)
+    setfield(tn, 'user_id', GHOST_JACHAR)
+    setfield(tn, 'type', 100)
+    setfield(tn, 'value', cl)
+    node_write(tn)
+  end
+  local function whatsit_callback(Np, lp, Nq)
+    if Np and Np.nuc then return Np
+    elseif Np and getfield(lp, 'user_id') == GHOST_JACHAR then
+      Np.first = lp; Np.nuc = lp; Np.last = lp; Np.class = getfield(lp,'value')
+      if Nq then Np.met = Nq.met; Np.pre = 0; Np.post = 0; Np.xspc = 3 end
+      Np.auto_kspc, Np.auto_xspc = (has_attr(lp, attr_autospc)==1), (has_attr(lp, attr_autoxspc)==1)
+      return Np
+    else return Np end
+  end
+  local function whatsit_after_callback(s, Nq, Np, head)
+    if not s and getfield(Nq.nuc, 'user_id') == GHOST_JACHAR then
+      local x, y = node_prev(Nq.nuc), Nq.nuc
+      Nq.first, Nq.nuc, Nq.last = x, x, x
+      if Np then Nq.met = Np.met end
+      s = node_remove(head, y); node_free(y)
+    end
+    return s
+  end
+  luatexbase.add_to_callback("luatexja.jfmglue.whatsit_getinfo", whatsit_callback,
+                             "ghost of a JACHAR", 1)
+  luatexbase.add_to_callback("luatexja.jfmglue.whatsit_after", whatsit_after_callback,
+                             "ghost of a JACHAR", 1)
+end