OSDN Git Service

Merge branch 'master' into kitagawa_test
[luatex-ja/luatexja.git] / src / luatexja / jfmglue.lua
index 902d81c..c60003f 100644 (file)
@@ -28,6 +28,7 @@ local set_attr = node.set_attribute
 local node_insert_before = node.insert_before
 local node_insert_after = node.insert_after
 local round = tex.round
+local table_insert = table.insert
 
 local id_glyph = node.id('glyph')
 local id_hlist = node.id('hlist')
@@ -70,24 +71,32 @@ local attr_autospc = luatexbase.attributes['ltj@autospc']
 local attr_autoxspc = luatexbase.attributes['ltj@autoxspc']
 local max_dimen = 1073741823
 
-
+local ltjs_get_penalty_table = ltjs.get_penalty_table
+local ltjs_get_skip_table = ltjs.get_skip_table
+local ltjf_find_char_class = ltjf.find_char_class
+local ltjf_font_metric_table = ltjf.font_metric_table
+local ltjf_metrics = ltjf.metrics
+local box_stack_level
+local par_indented -- is the paragraph indented?
 
 -------------------- Helper functions
 
-local function find_char_class(c,m)
+-- This function is called only for acquiring `special' characters.
+local function fast_find_char_class(c,m)
    return m.chars[c] or 0
 end
 
+local spec_zero_glue = node_new(id_glue_spec)
+   spec_zero_glue.width = 0; spec_zero_glue.stretch_order = 0; spec_zero_glue.stretch = 0
+   spec_zero_glue.shrink_order = 0; spec_zero_glue.shrink = 0
+
 local function get_zero_glue()
-   local g = node_new(id_glue_spec)
-   g.width = 0; g.stretch_order = 0; g.stretch = 0
-   g.shrink_order = 0; g.shrink = 0
-   return g
+   return node_copy(spec_zero_glue)
 end
 
 local function skip_table_to_spec(n)
    local g = node_new(id_glue_spec)
-   local st = ltjs.get_skip_table(n, ltjp.box_stack_level)
+   local st = ltjs_get_skip_table(n, box_stack_level)
    g.width = st.width; g.stretch = st.stretch; g.shrink = st.shrink
    g.stretch_order = st.stretch_order; g.shrink_order = st.shrink_order
    return g
@@ -123,6 +132,13 @@ function math.two_average(a,b) return (a+b)/2 end
 --   実際の glue は Np.last, Nq.first の間に挿入される
 --  Bp: Np.last, Nq.first の間の penalty node 達の配列
 
+-- Np, Nq, Bp, widow_Bp について
+-- Np, Nq は別々のテーブル.
+-- 1回のループごとに Nq = Np, Np = (new table) となるのは効率が悪いので,
+-- Np <-> Nq 入れ替え,その後 Np をクリアすることでテーブルを再利用.
+-- 同様の関係は Bp, widow_Bp にも.
+
+
 -- 核の定義:
 --  node x が non-char node のときは,x のみ
 --  x が char_node のときは,
@@ -232,25 +248,27 @@ end
 
 local function calc_np()
    -- We assume lp = node_next(Np.last)
-   local pBp = Bp; local lpi, lpa
-   Nq = Np; Bp = {}; Bp[0] = 0; Np = {}; ihb_flag = false 
+   local lpi, lpa, Nr
+   Nr = Nq; for k in pairs(Nr) do Nr[k] = nil end
+   Nq = Np; Np = Nr
+   for k in pairs(Bp) do Bp[k] = nil end
+   ihb_flag = false 
    while true do
       lpi = lp.id; lpa = has_attr(lp, attr_icflag) or 0
       if lp==last then Np = nil; return
       elseif lpa>=PACKED then
         if lpa == BOXBDD then
            local lq = node_next(lp)
-           head = node_remove(head, lp); lp = lq
+           head = node_remove(head, lp); node_free(lp); lp = lq
         else calc_np_pbox(); return end -- id_pbox
       elseif lpi == id_ins or lpi == id_mark or lpi == id_adjust then
         set_attr_icflag_processed(lp); lp = node_next(lp)
       elseif lpi == id_penalty then
-        table.insert(Bp, lp); Bp[0] = Bp[0] + 1
-        set_attr_icflag_processed(lp); lp = node_next(lp)
+        table_insert(Bp, lp); set_attr_icflag_processed(lp); lp = node_next(lp)
       elseif lpi == id_whatsit then
         if lp.subtype==sid_user and lp.user_id==30111 then
            local lq = node_next(lp)
-           head = node_remove(head, lp); lp = lq; ihb_flag = true
+           head = node_remove(head, lp); node_free(lp); lp = lq; ihb_flag = true
         else
            set_attr_icflag_processed(lp); lp = node_next(lp)
         end
@@ -295,25 +313,27 @@ end
 --            "Np is not a character" otherwise.
 
 -- 和文文字のデータを取得
-local function set_np_xspc_jachar(c,x)
-   Np.class = has_attr(x, attr_jchar_class)
-   Np.char = c
-   local z = ltjf.font_metric_table[x.font]
+local function set_np_xspc_jachar(x)
+   local z = ltjf_font_metric_table[x.font]
+   local cls = ltjf_find_char_class(x.char, z)
+   set_attr(x, attr_jchar_class, cls)
+   Np.class = cls
+   Np.char = x.char
    Np.size= z.size
-   Np.met = ltjf.metrics[z.jfm]
+   Np.met = ltjf_metrics[z.jfm]
    Np.var = z.var
-   Np.pre = ltjs.get_penalty_table('pre', c, 0, ltjp.box_stack_level)
-   Np.post = ltjs.get_penalty_table('post', c, 0, ltjp.box_stack_level)
-   z = find_char_class('lineend', Np.met)
+   Np.pre = ltjs_get_penalty_table('pre', x.char, 0, box_stack_level)
+   Np.post = ltjs_get_penalty_table('post', x.char, 0, box_stack_level)
+   z = fast_find_char_class('lineend', Np.met)
    local y = Np.met.size_cache[Np.size].char_type[Np.class]
    if y.kern and y.kern[z] then 
       Np.lend = y.kern[z]
    else 
       Np.lend = 0 
    end
-   y = ltjs.get_penalty_table('xsp', c, 3, ltjp.box_stack_level)
-   Np.xspc_before = (y>=2)
-   Np.xspc_after  = (y%2==1)
+   y = ltjs_get_penalty_table('xsp', x.char, 3, box_stack_level)
+   Np.xspc_before = (y%2==1)
+   Np.xspc_after  = (y>=2)
    Np.auto_kspc = (has_attr(x, attr_autospc)==1)
    Np.auto_xspc = (has_attr(x, attr_autoxspc)==1)
 end
@@ -332,13 +352,13 @@ local function set_np_xspc_alchar(c,x, lig)
            x = node_tail(x.components); c = x.char
         end
       end
-      Np.pre = ltjs.get_penalty_table('pre', c, 0, ltjp.box_stack_level)
-      Np.post = ltjs.get_penalty_table('post', c, 0, ltjp.box_stack_level)
+      Np.pre = ltjs_get_penalty_table('pre', c, 0, box_stack_level)
+      Np.post = ltjs_get_penalty_table('post', c, 0, box_stack_level)
    else
       Np.pre = 0; Np.post = 0
    end
    Np.met = nil
-   local y = ltjs.get_penalty_table('xsp', c, 3, ltjp.box_stack_level)
+   local y = ltjs_get_penalty_table('xsp', c, 3, box_stack_level)
    Np.xspc_before = (y%2==1)
    Np.xspc_after  = (y>=2)
    Np.auto_xspc = (has_attr(x, attr_autoxspc)==1)
@@ -348,7 +368,7 @@ end
 local function extract_np()
    local x = Np.nuc
    if Np.id ==  id_jglyph then
-      set_np_xspc_jachar(x.char, x)
+      set_np_xspc_jachar(x)
    elseif Np.id == id_glyph then
       set_np_xspc_alchar(x.char, x, ligature_head)
    elseif Np.id == id_hlist then
@@ -356,7 +376,7 @@ local function extract_np()
       if check_box(x.head, nil) then
         if first_char then
            if first_char.font == has_attr(first_char, attr_curjfnt) then 
-              set_np_xspc_jachar(first_char.char,first_char)
+              set_np_xspc_jachar(first_char)
            else
               set_np_xspc_alchar(first_char.char,first_char, ligature_head)
            end
@@ -367,7 +387,7 @@ local function extract_np()
       if check_box(Np.first, node_next(Np.last)) then
         if first_char then
            if first_char.font == has_attr(first_char, attr_curjfnt) then 
-              set_np_xspc_jachar(first_char.char,first_char)
+              set_np_xspc_jachar(first_char)
            else
               set_np_xspc_alchar(first_char.char,first_char, ligature_head)
            end
@@ -378,7 +398,7 @@ local function extract_np()
       if check_box(x.replace, nil) then
         if first_char then
            if first_char.font == has_attr(first_char, attr_curjfnt) then 
-              set_np_xspc_jachar(first_char.char,first_char)
+              set_np_xspc_jachar(first_char)
            else
               set_np_xspc_alchar(first_char.char,first_char, ligature_head)
            end
@@ -394,7 +414,7 @@ end
 local function after_hlist()
    if last_char then
       if last_char.font == has_attr(last_char, attr_curjfnt) then 
-        set_np_xspc_jachar(last_char.char,last_char, ligature_after)
+        set_np_xspc_jachar(last_char, ligature_after)
       else
         set_np_xspc_alchar(last_char.char,last_char, ligature_after)
       end
@@ -413,7 +433,7 @@ end
 local function lineend_fix(g)
    if g and g.id==id_kern then 
       Nq.lend = 0
-   elseif Nq.lend and Nq.lend~=0 then
+   elseif Nq.lend~=0 then
       if not g then
         g = node_new(id_kern); g.subtype = 1
         g.kern = -Nq.lend; set_attr(g, attr_icflag, LINEEND)
@@ -429,41 +449,41 @@ end
 -- change penalties (or create a new penalty, if needed)
 local function handle_penalty_normal(post, pre, g)
    local a = (pre or 0) + (post or 0)
-   if Bp[0] == 0 then
-      if (a~=0 and not(g and g.id==id_kern)) or (Nq.lend and Nq.lend~=0) then
+   if #Bp == 0 then
+      if (a~=0 and not(g and g.id==id_kern)) or Nq.lend~=0 then
         local p = node_new(id_penalty)
         if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
         p.penalty = a
         head = node_insert_before(head, Np.first, p)
-        Bp[1] = p; Bp[0] = 1; set_attr(p, attr_icflag, KINSOKU)
+        table_insert(Bp, p); set_attr(p, attr_icflag, KINSOKU)
       end
-   else for i, v in ipairs(Bp) do add_penalty(v,a) end
+   else for i, v in pairs(Bp) do add_penalty(v,a) end
    end
 end
 
 local function handle_penalty_always(post, pre, g)
    local a = (pre or 0) + (post or 0)
-   if Bp[0] == 0 then
-      if not (g and g.id==id_glue) or (Nq.lend and Nq.lend~=0) then
+   if #Bp == 0 then
+      if not (g and g.id==id_glue) or Nq.lend~=0 then
         local p = node_new(id_penalty)
         if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
         p.penalty = a
         head = node_insert_before(head, Np.first, p)
-        Bp[1] = p; Bp[0] = 1; set_attr(p, attr_icflag, KINSOKU)
+        table_insert(Bp, p); set_attr(p, attr_icflag, KINSOKU)
       end
-   else for i, v in ipairs(Bp) do add_penalty(v,a) end
+   else for i, v in pairs(Bp) do add_penalty(v,a) end
    end
 end
 
 local function handle_penalty_suppress(post, pre, g)
    local a = (pre or 0) + (post or 0)
-   if Bp[0] == 0 then
+   if #Bp == 0 then
       if g and g.id==id_glue then
         local p = node_new(id_penalty)
         p.penalty = 10000; head = node_insert_before(head, Np.first, p)
-        Bp[1] = p; Bp[0] = 1; set_attr(p, attr_icflag, KINSOKU)
+        table_insert(Bp, p); set_attr(p, attr_icflag, KINSOKU)
       end
-   else for i, v in ipairs(Bp) do add_penalty(v,a) end
+   else for i, v in pairs(Bp) do add_penalty(v,a) end
    end
 end
 
@@ -493,7 +513,7 @@ local function real_insert(w, g)
    if w~=0 then
       local h = node_new(id_kern)
       set_attr(h, attr_icflag, LINE_END)
-      h.kern = Nq.lend; h.subtype = 1
+      h.kern = w; h.subtype = 1
       head = node_insert_after(head, Nq.last, h)
    end
    if g then
@@ -535,13 +555,12 @@ local function get_kanjiskip()
            end
         elseif ak then
            gx.width = ak[1]; gx.stretch = ak[2]; gx.shrink = ak[3]
-        else gx = get_zero_glue() -- fallback
+        else node_free(gx); gx = get_zero_glue() -- fallback
         end
         g.spec = gx
-      else g.spec=node_copy(kanji_skip) end
+      else g.spec=node_copy(kanji_skip); node_free(gx) end
    else
-      local gx = get_zero_glue()
-      g.spec = gx
+      g.spec =  get_zero_glue(); node_free(gx)
    end
    set_attr(g, attr_icflag, KANJI_SKIP)
    return g
@@ -589,8 +608,8 @@ local function calc_ja_ja_glue()
       return new_jfm_glue(Nq, Nq.class, Np.class)
    else
       local g = new_jfm_glue(Nq, Nq.class,
-                            find_char_class('diffmet',Nq.met))
-      local h = new_jfm_glue(Np, find_char_class('diffmet',Np.met),
+                            fast_find_char_class('diffmet',Nq.met))
+      local h = new_jfm_glue(Np, fast_find_char_class('diffmet',Np.met),
                             Np.class)
       return calc_ja_ja_aux(g,h)
    end
@@ -615,13 +634,12 @@ local function get_xkanjiskip(Nn)
         local bk = get_xkanji_skip_from_jfm(Nn)
         if bk then
            gx.width = bk[1]; gx.stretch = bk[2]; gx.shrink = bk[3]
-        else gx = get_zero_glue() -- fallback
+        else node_free(gx); gx = get_zero_glue() -- fallback
         end
         g.spec = gx
       else g.spec=node_copy(xkanji_skip) end
    else
-      local gx = get_zero_glue()
-      g.spec = gx
+      g.spec = get_zero_glue()
    end
    set_attr(g, attr_icflag, XKANJI_SKIP)
    return g
@@ -632,20 +650,24 @@ end
 
 local function get_OA_skip()
    if not ihb_flag then
-      return new_jfm_glue(Np, find_char_class('jcharbdd',Np.met), Np.class)
+      local c
+      if Nq.id == id_math then c = -1 else c = 'jcharbdd' end
+      return new_jfm_glue(Np, fast_find_char_class(c,Np.met), Np.class)
    else return nil
    end
 end
 local function get_OB_skip()
    if not ihb_flag then
-      return new_jfm_glue(Nq, Nq.class, find_char_class('jcharbdd',Nq.met))
+      local c
+      if Np.id == id_math then c = -1 else c = 'jcharbdd' end
+      return new_jfm_glue(Nq, Nq.class, fast_find_char_class(c,Nq.met))
    else return nil
    end
 end
 
 -- (anything) .. jachar
 local function handle_np_jachar()
-   local g = nil
+   local g
    if Nq.id==id_jglyph or (Nq.id==id_pbox and Nq.met) then 
       g = calc_ja_ja_glue() or get_kanjiskip() -- M->K
       g = lineend_fix(g)
@@ -665,14 +687,15 @@ local function handle_np_jachar()
       real_insert(0, g)
    end
    -- \jcharwidowpenalty 挿入予定箇所更新
-   if ltjs.get_penalty_table('kcat', Np.char, 0, ltjp.box_stack_level)%2~=1 then
-      widow_Np = Np; widow_Bp = Bp
+   if mode and ltjs_get_penalty_table('kcat', Np.char, 0, box_stack_level)%2~=1 then
+      widow_Np.first = Np.first; 
+      local Bpr = widow_Bp; widow_Bp = Bp; Bp = Bpr
    end
 end
 
 -- jachar .. (anything)
 local function handle_nq_jachar()
-   local g = nil
+   local g
    if Np.pre then 
       g = get_OB_skip() or get_xkanjiskip(Nq) -- O_B->X
       g = lineend_fix(g)
@@ -689,7 +712,7 @@ end
 
 -- (anything) .. (和文文字で終わる hlist)
 local function handle_np_ja_hlist()
-   local g = nil
+   local g
    if Nq.id==id_jglyph or (Nq.id==id_pbox and Nq.met) then 
       g = get_OB_skip() or get_kanjiskip() -- O_B->K
       g = lineend_fix(g)
@@ -727,48 +750,70 @@ local function handle_list_tail()
         end
       end
       -- Insert \jcharwidowpenalty
-      Bp = widow_Bp; Np = widow_Np
-      if Np then
+      Bp = widow_Bp; Np = widow_Np; Nq.lend = 0
+      if Np.first then
         handle_penalty_normal(0,
-                              ltjs.get_penalty_table('jwp', 0, 0, ltjp.box_stack_level))
+                              ltjs_get_penalty_table('jwp', 0, 0, box_stack_level))
       end
    else
       -- the current list is the contents of a hbox
       if Np.id == id_jglyph or (Np.id==id_pbox and Np.met) then 
-        local g = new_jfm_glue(Np, Np.class, find_char_class('boxbdd',Np.met))
+        local g = new_jfm_glue(Np, Np.class, fast_find_char_class('boxbdd',Np.met))
         if g then
            set_attr(g, attr_icflag, BOXBDD)
            head = node_insert_after(head, Np.last, g)
         end
       end
-      head = node_remove(head, last) -- remove the sentinel
+      head = node_remove(head, last); node_free(last);-- remove the sentinel
    end
+   node_free(kanji_skip); node_free(xkanji_skip)
 end
 
 -- リスト先頭の処理
 local function handle_list_head()
    if Np.id ==  id_jglyph or (Np.id==id_pbox and Np.met) then 
-      local g = new_jfm_glue(Np, find_char_class('boxbdd',Np.met), Np.class)
-      if g then
-        set_attr(g, attr_icflag, BOXBDD)
-        if g.id==id_glue and Bp[0]==0 then
-           local h = node_new(id_penalty)
-           h.penalty = 10000; set_attr(h, attr_icflag, BOXBDD)
+      if not ihb_flag then
+        local g
+        if par_indented then
+           g = new_jfm_glue(Np, fast_find_char_class('parbdd',Np.met), Np.class)
+        else
+           g = new_jfm_glue(Np, fast_find_char_class('boxbdd',Np.met), Np.class)
+        end
+        if g then
+           set_attr(g, attr_icflag, BOXBDD)
+           if g.id==id_glue and #Bp==0 then
+              local h = node_new(id_penalty)
+              h.penalty = 10000; set_attr(h, attr_icflag, BOXBDD)
+           end
+           head = node_insert_before(head, Np.first, g)
         end
-        head = node_insert_before(head, Np.first, g)
       end
    end
 end
 
 -- initialize
 local function init_var()
-   lp = head; widow_Bp = nil; widow_Np = nil
+   lp = head; Bp = {}; widow_Bp = {}; widow_Np = {first = nil}
+   par_indented = false 
+   box_stack_level = ltjp.box_stack_level
    kanji_skip=skip_table_to_spec('kanjiskip')
    xkanji_skip=skip_table_to_spec('xkanjiskip')
+   Np = {
+      auto_kspc=nil, auto_xspc=nil, char=nil, class=nil, 
+      first=nil, id=nil, last=nil, lend=0, met=nil, nuc=nil, 
+      post=nil, pre=nil, var=nil, xspc_after=nil, xspc_before=nil, 
+   }
+   Nq = {
+      auto_kspc=nil, auto_xspc=nil, char=nil, class=nil, 
+      first=nil, id=nil, last=nil, lend=0, met=nil, nuc=nil, 
+      post=nil, pre=nil, var=nil, xspc_after=nil, xspc_before=nil, 
+   }
    if mode then 
       -- the current list is to be line-breaked:
       -- hbox from \parindent is skipped.
-      while lp and (lp.id==id_whatsit or ((lp.id==id_hlist) and (lp.subtype==3))) do
+      while lp and ((lp.id==id_whatsit and lp.subtype~=sid_user) 
+                or ((lp.id==id_hlist) and (lp.subtype==3))) do
+        if (lp.id==id_hlist) and (lp.subtype==3) then par_indented = true end
         lp=node_next(lp) end
       last=node.tail(head)
    else 
@@ -791,7 +836,7 @@ function main(ahead, amode)
       elseif Np.id==id_hlist or Np.id==id_pbox or Np.id==id_disc then after_hlist()
       end
    else
-      if not mode then head = node_remove(head, last) end
+      if not mode then head = node_remove(head, last); node_free(last) end
       return head
    end
    calc_np()
@@ -820,7 +865,9 @@ function main(ahead, amode)
 end
 
 -- \inhibitglue
+local inhibitglue_node=node_new(id_whatsit, sid_user)
+inhibitglue_node.user_id=30111; inhibitglue_node.type=100; inhibitglue_node.value=1
+
 function create_inhibitglue_node()
-   local g=node_new(id_whatsit, sid_user)
-   g.user_id=30111; g.type=100; g.value=1; node.write(g)
+   node.write(node.copy(inhibitglue_node))
 end