summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/luatex/luatexja/ltj-jfmglue.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/luatex/luatexja/ltj-jfmglue.lua')
-rw-r--r--Master/texmf-dist/tex/luatex/luatexja/ltj-jfmglue.lua964
1 files changed, 964 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/luatex/luatexja/ltj-jfmglue.lua b/Master/texmf-dist/tex/luatex/luatexja/ltj-jfmglue.lua
new file mode 100644
index 00000000000..228d3763084
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/luatexja/ltj-jfmglue.lua
@@ -0,0 +1,964 @@
+--
+-- luatexja/jfmglue.lua
+--
+luatexbase.provides_module({
+ name = 'luatexja.jfmglue',
+ date = '2012/04/02',
+ version = '0.3',
+ description = 'Insertion process of JFM glues and kanjiskip',
+})
+module('luatexja.jfmglue', package.seeall)
+local err, warn, info, log = luatexbase.errwarinf(_NAME)
+
+luatexja.load_module('base'); local ltjb = luatexja.base
+luatexja.load_module('stack'); local ltjs = luatexja.stack
+luatexja.load_module('jfont'); local ltjf = luatexja.jfont
+luatexja.load_module('pretreat'); local ltjp = luatexja.pretreat
+
+local node_type = node.type
+local node_new = node.new
+local node_remove = node.remove
+local node_prev = node.prev
+local node_next = node.next
+local node_copy = node.copy
+local node_tail = node.tail
+local node_free = node.free
+local has_attr = node.has_attribute
+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 uniq_id = 0 -- unique id
+
+local id_glyph = node.id('glyph')
+local id_hlist = node.id('hlist')
+local id_vlist = node.id('vlist')
+local id_rule = node.id('rule')
+local id_ins = node.id('ins')
+local id_mark = node.id('mark')
+local id_adjust = node.id('adjust')
+local id_disc = node.id('disc')
+local id_whatsit = node.id('whatsit')
+local id_math = node.id('math')
+local id_glue = node.id('glue')
+local id_kern = node.id('kern')
+local id_penalty = node.id('penalty')
+
+local id_glue_spec = node.id('glue_spec')
+local id_jglyph = node.id('glyph') + 256 -- Japanese character
+local id_box_like = node.id('hlist') + 256 -- vbox, shifted hbox
+local id_pbox = node.id('hlist') + 512 -- already processed nodes (by \unhbox)
+local id_pbox_w = node.id('hlist') + 513 -- cluster which consists of a whatsit
+local sid_user = node.subtype('user_defined')
+
+local ITALIC = 1
+local PACKED = 2
+local KINSOKU = 3
+local FROM_JFM = 4
+local LINE_END = 5
+local KANJI_SKIP = 6
+local XKANJI_SKIP = 7
+local PROCESSED = 8
+local IC_PROCESSED = 9
+local BOXBDD = 15
+
+local kanji_skip
+local xkanji_skip
+
+local attr_jchar_class = luatexbase.attributes['ltj@charclass']
+local attr_curjfnt = luatexbase.attributes['ltj@curjfnt']
+local attr_icflag = luatexbase.attributes['ltj@icflag']
+local attr_autospc = luatexbase.attributes['ltj@autospc']
+local attr_autoxspc = luatexbase.attributes['ltj@autoxspc']
+local attr_uniqid = luatexbase.attributes['ltj@uniqid']
+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
+
+-- 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_spec()
+ 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, 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
+end
+
+-- penalty 値の計算
+local function add_penalty(p,e)
+ if p.penalty>=10000 then
+ if e<=-10000 then p.penalty = 0 end
+ elseif p.penalty<=-10000 then
+ if e>=10000 then p.penalty = 0 end
+ else
+ p.penalty = p.penalty + e
+ if p.penalty>=10000 then p.penalty = 10000
+ elseif p.penalty<=-10000 then p.penalty = -10000 end
+ end
+ return
+end
+
+-- 「異なる JFM」の間の調整方法
+diffmet_rule = math.two_average
+function math.two_add(a,b) return a+b end
+function math.two_average(a,b) return (a+b)/2 end
+
+-------------------- idea
+-- 2 node の間に glue/kern/penalty を挿入する.
+-- 基本方針: char node q と char node p の間
+
+--  Np: 「p を核とする塊」
+--   first: 最初の node,nuc: p,last: 最後の node
+--   id: 核 node の種類
+--  Nq: 「q を核とする塊」
+--   実際の 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 のときは,
+--  - x が \accent の第二引数だったとき
+--   [kern2 kern y kern2] x の 3 node が核に加わる
+--  - x の直後に \/ 由来 kern があったとき
+--   その \/ 由来の kern が核に加わる
+-- p, q の走査で無視するもの:
+--  ins, mark, adjust, whatsit, penalty
+--
+-- Nq.last .. + .. Bp.first .... Bp[last] .... * .. Np.first
+-- +: kern from LINEEND はここに入る
+-- *: jfm glue はここに入る
+
+local head -- the head of current list
+local last -- the last node of current list
+local lp -- 外側での list 走査時のカーソル
+
+local Np, Nq, Bp
+local widow_Bp, widow_Np -- \jcharwidowpenalty 挿入位置管理用
+
+local ihb_flag -- JFM グルー挿入抑止用 flag
+ -- on: \inhibitglue 指定時,hlist の周囲
+
+-------------------- hlist 内の文字の検索
+
+local first_char, last_char, find_first_char
+
+local function check_box(box_ptr, box_end)
+ local p = box_ptr; local found_visible_node = false
+ if not p then
+ find_first_char = false; first_char = nil; last_char = nil
+ return true
+ end
+ while p and p~=box_end do
+ local pid = p.id
+ if pid==id_kern then
+ if p.subtype==2 then
+ p = node_next(node_next(node_next(p))); pid = p.id
+ elseif has_attr(p, attr_icflag)==IC_PROCESSED then
+ p = node_next(p); pid = p.id
+ end
+ end
+ if pid==id_glyph then
+ repeat
+ if find_first_char then
+ first_char = p; find_first_char = false
+ end
+ last_char = p; found_visible_node = true; p=node_next(p)
+ if (not p) or p==box_end then return found_visible_node end
+ until p.id~=id_glyph
+ end
+ if pid==id_hlist then
+ if has_attr(p, attr_icflag)==PACKED then
+ for q in node.traverse_id(id_glyph, p.head) do
+ if find_first_char then
+ first_char = q; find_first_char = false
+ end
+ last_char = q; found_visible_node = true; break
+ end
+ else
+ if p.shift==0 then
+ if check_box(p.head, nil) then found_visible_node = true end
+ else if find_first_char then
+ find_first_char = false
+ else
+ last_char = nil
+ end
+ end
+ end
+ elseif not (pid==id_ins or pid==id_mark
+ or pid==id_adjust or pid==id_whatsit
+ or pid==id_penalty) then
+ found_visible_node = true
+ if find_first_char then
+ find_first_char = false
+ else
+ last_char = nil
+ end
+ end
+ p = node_next(p)
+ end
+ return found_visible_node
+end
+
+function check_box_high(Nx, box_ptr, box_end)
+ first_char = nil; last_char = nil; find_first_char = true
+ if check_box(box_ptr, box_end) then
+ if first_char then
+ if first_char.font == has_attr(first_char, attr_curjfnt) then
+ set_np_xspc_jachar(Nx, first_char)
+ else
+ set_np_xspc_alchar(Nx, first_char.char,first_char, ligature_head)
+ end
+ end
+ end
+ return last_char
+end
+
+-------------------- Np の計算と情報取得
+
+luatexbase.create_callback("luatexja.jfmglue.whatsit_getinfo", "data",
+ function (Np, lp, Nq, box_stack_level)
+ if Np.nuc then return Np
+ else
+ return Np -- your code
+ end
+ end)
+luatexbase.create_callback("luatexja.jfmglue.whatsit_after", "data",
+ function (stat, Nq, Np, box_stack_level) return false end)
+
+-- calc next Np
+local function set_attr_icflag_processed(p)
+ local a = has_attr(p, attr_icflag) or 0
+ if a<= ITALIC then
+ set_attr(p, attr_uniqid, uniq_id)
+ set_attr(p, attr_icflag, PROCESSED)
+ end
+end
+
+local function check_next_ickern()
+ if lp.id == id_kern and has_attr(lp, attr_icflag)==ITALIC then
+ set_attr(lp, attr_icflag, IC_PROCESSED)
+ set_attr(lp, attr_uniqid, uniq_id)
+ Np.last = lp; lp = node_next(lp)
+ else Np.last = Np.nuc end
+end
+
+local function calc_np_pbox()
+ local uid = has_attr(lp, attr_uniqid)
+ Np.first = lp; Np.id = id_pbox
+ lpa = KINSOKU -- dummy=
+ while lp~=last and lpa>=PACKED and lpa~=BOXBDD
+ and has_attr(lp, attr_uniqid) == uid do
+ Np.nuc = lp; set_attr(lp, attr_uniqid, uniq_id)
+ lp = node_next(lp); lpa = has_attr(lp, attr_icflag) or 0
+ end
+ check_next_ickern()
+end
+
+local calc_np_auxtable = {
+ [id_glyph] = function()
+ Np.first = lp
+ if lp.font == has_attr(lp, attr_curjfnt) then
+ Np.id = id_jglyph
+ else
+ Np.id = id_glyph
+ end
+ Np.first = lp; Np.nuc = lp; set_attr_icflag_processed(lp)
+ lp = node_next(lp); check_next_ickern(); return true
+ end,
+ [id_hlist] = function()
+ Np.first = lp; Np.last = lp; Np.nuc = lp;
+ set_attr_icflag_processed(lp)
+ if lp.shift~=0 then
+ Np.id = id_box_like
+ else
+ Np.id = id_hlist
+ end
+ lp = node_next(lp); return true
+ end,
+ [id_vlist] = function()
+ Np.first = lp; Np.nuc = lp; Np.last = lp;
+ Np.id = id_box_like; set_attr_icflag_processed(lp);
+ lp = node_next(lp); return true
+ end,
+ [id_rule] = function()
+ Np.first = lp; Np.nuc = lp; Np.last = lp;
+ Np.id = id_box_like; set_attr_icflag_processed(lp);
+ lp = node_next(lp); return true
+ end,
+ [id_ins] = function()
+ set_attr_icflag_processed(lp); lp = node_next(lp)
+ return false
+ end,
+ [id_mark] = function()
+ set_attr_icflag_processed(lp); lp = node_next(lp)
+ return false
+ end,
+ [id_adjust] = function()
+ set_attr_icflag_processed(lp); lp = node_next(lp)
+ return false
+ end,
+ [id_disc] = function()
+ Np.first = lp; Np.nuc = lp; set_attr_icflag_processed(lp);
+ Np.last = lp; Np.id = id_disc; lp = node_next(lp); return true
+ end,
+ [id_whatsit] = function()
+ if lp.subtype==sid_user then
+ if lp.user_id==30111 then
+ local lq = node_next(lp)
+ head = node_remove(head, lp); node_free(lp); lp = lq; ihb_flag = true
+ else
+ set_attr_icflag_processed(lp)
+ luatexbase.call_callback("luatexja.jfmglue.whatsit_getinfo"
+ , Np, lp, Nq, box_stack_level)
+ lp = node_next(lp)
+ if Np.nuc then
+ Np.id = id_pbox_w; Np.first = Np.nuc; Np.last = Np.nuc; return true
+ end
+ end
+ else
+ set_attr_icflag_processed(lp)
+ end
+ return false
+ end,
+ [id_math] = function()
+ Np.first = lp; Np.nuc = lp;
+ set_attr_icflag_processed(lp); lp = node_next(lp)
+ while lp.id~=id_math do
+ set_attr_icflag_processed(lp); lp = node_next(lp)
+ end
+ set_attr_icflag_processed(lp);
+ Np.last = lp; Np.id = id_math; lp = node_next(lp);
+ return true
+ end,
+ [id_glue] = function()
+ Np.first = lp; Np.nuc = lp; set_attr_icflag_processed(lp);
+ Np.last = lp; Np.id = id_glue; lp = node_next(lp); return true
+ end,
+ [id_kern] = function()
+ Np.first = lp
+ if lp.subtype==2 then
+ set_attr_icflag_processed(lp); lp = node_next(lp)
+ set_attr_icflag_processed(lp); lp = node_next(lp)
+ set_attr_icflag_processed(lp); lp = node_next(lp)
+ set_attr_icflag_processed(lp); Np.nuc = lp
+ if lp.font == has_attr(lp, attr_curjfnt) then
+ Np.id = id_jglyph
+ else
+ Np.id = id_glyph
+ end
+ lp = node_next(lp); check_next_ickern();
+ else
+ Np.id = id_kern; set_attr_icflag_processed(lp);
+ Np.last = lp; lp = node_next(lp)
+ end
+ return true
+ end,
+ [id_penalty] = function()
+ Bp[#Bp+1] = lp; set_attr_icflag_processed(lp);
+ lp = node_next(lp); return false
+ end,
+ [13] = function()
+ Np.first = lp; Np.nuc = lp; Np.last = lp;
+ Np.id = id_box_like; set_attr_icflag_processed(lp);
+ lp = node_next(lp); return true
+ end,
+}
+
+local function calc_np()
+ -- We assume lp = node_next(Np.last)
+ 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 lp ~= last do
+ lpa = has_attr(lp, attr_icflag) or 0
+ if lpa>=PACKED then
+ if lpa == BOXBDD then
+ local lq = node_next(lp)
+ head = node_remove(head, lp); node_free(lp); lp = lq
+ else calc_np_pbox(); return
+ end -- id_pbox
+ elseif calc_np_auxtable[lp.id]() then return end
+ end
+ Np = nil; return
+end
+
+-- extract informations from Np
+-- We think that "Np is a Japanese character" if Np.met~=nil,
+-- "Np is an alphabetic character" if Np.pre~=nil,
+-- "Np is not a character" otherwise.
+
+-- 和文文字のデータを取得
+function set_np_xspc_jachar(Nx, x)
+ local z = ltjf_font_metric_table[x.font]
+ local c = x.char
+ local cls = ltjf_find_char_class(c, z)
+ local m = ltjf_metrics[z.jfm]
+ set_attr(x, attr_jchar_class, cls)
+ Nx.class = cls
+ Nx.char = c
+ Nx.size= z.size
+ Nx.met = m
+ Nx.var = z.var
+ Nx.pre = ltjs_get_penalty_table('pre', c, 0, box_stack_level)
+ Nx.post = ltjs_get_penalty_table('post', c, 0, box_stack_level)
+ z = fast_find_char_class('lineend', m)
+ local y = m.size_cache[Nx.size].char_type[Nx.class]
+ if y.kern and y.kern[z] then
+ Nx.lend = y.kern[z]
+ else
+ Nx.lend = 0
+ end
+ y = ltjs_get_penalty_table('xsp', c, 3, box_stack_level)
+ Nx.xspc_before = (y%2==1)
+ Nx.xspc_after = (y>=2)
+ Nx.auto_kspc = (has_attr(x, attr_autospc)==1)
+ Nx.auto_xspc = (has_attr(x, attr_autoxspc)==1)
+end
+
+-- 欧文文字のデータを取得
+local ligature_head = 1
+local ligature_tail = 2
+function set_np_xspc_alchar(Nx, c,x, lig)
+ if c~=-1 then
+ if lig == ligature_head then
+ while x.components and x.subtype and math.floor(x.subtype/2)%2==1 do
+ x = x.components; c = x.char
+ end
+ else
+ while x.components and x.subtype and math.floor(x.subtype/2)%2==1 do
+ x = node_tail(x.components); c = x.char
+ end
+ end
+ Nx.pre = ltjs_get_penalty_table('pre', c, 0, box_stack_level)
+ Nx.post = ltjs_get_penalty_table('post', c, 0, box_stack_level)
+ else
+ Nx.pre = 0; Nx.post = 0
+ end
+ Nx.met = nil
+ local y = ltjs_get_penalty_table('xsp', c, 3, box_stack_level)
+ Nx.xspc_before = (y%2==1)
+ Nx.xspc_after = (y>=2)
+ Nx.auto_xspc = (has_attr(x, attr_autoxspc)==1)
+end
+
+-- Np の情報取得メインルーチン
+local function extract_np()
+ local x = Np.nuc;
+ if Np.id == id_jglyph then set_np_xspc_jachar(Np, x)
+ elseif Np.id == id_glyph then set_np_xspc_alchar(Np, x.char, x, ligature_head)
+ elseif Np.id == id_hlist then Np.last_char = check_box_high(Np, x.head, nil)
+ elseif Np.id == id_pbox then Np.last_char = check_box_high(Np, Np.first, node.next(Np.last))
+ elseif Np.id == id_disc then Np.last_char = check_box_high(Np, x.replace, nil)
+ elseif Np.id == id_math then set_np_xspc_alchar(Np, -1, x)
+ end
+end
+
+-- change the information for the next loop
+-- (will be done if Nx is an alphabetic character or a hlist)
+function after_hlist(Nx)
+ if Nx.last_char then
+ if Nx.last_char.font == has_attr(Nx.last_char, attr_curjfnt) then
+ set_np_xspc_jachar(Nx, Nx.last_char);
+ else
+ set_np_xspc_alchar(Nx, Nx.last_char.char,Nx.last_char, ligature_tail)
+ end
+ else
+ Nx.pre = nil; Nx.met = nil
+ end
+end
+
+local function after_alchar(Nx)
+ local x = Nx.nuc
+ set_np_xspc_alchar(Nx, x.char,x, ligature_tail)
+end
+
+
+-------------------- 最下層の処理
+
+local function lineend_fix(g)
+ if g and g.id==id_kern then
+ Nq.lend = 0
+ 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)
+ set_attr(g, attr_uniqid, uniq_id)
+ elseif g.id==id_kern then
+ g.kern = g.kern - Nq.lend
+ else
+ g.spec.width = g.spec.width - Nq.lend
+ end
+ end
+ return g
+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 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)
+ table_insert(Bp, p);
+ set_attr(p, attr_icflag, KINSOKU)
+ set_attr(p, attr_uniqid, uniq_id)
+ 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 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)
+ table_insert(Bp, p)
+ set_attr(p, attr_icflag, KINSOKU)
+ set_attr(p, attr_uniqid, uniq_id)
+ 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 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)
+ table_insert(Bp, p);
+ set_attr(p, attr_icflag, KINSOKU)
+ set_attr(p, attr_uniqid, uniq_id)
+ end
+ else for i, v in pairs(Bp) do add_penalty(v,a) end
+ end
+end
+
+-- 和文文字間の JFM glue を node 化
+local function new_jfm_glue(Nn, bc, ac)
+-- bc, ac: char classes
+ local g = nil
+ local z = Nn.met.size_cache[Nn.size].char_type[bc]
+ if z.glue and z.glue[ac] then
+ local h = node_new(id_glue_spec)
+ h.width = z.glue[ac][1]
+ h.stretch = z.glue[ac][2]
+ h.shrink = z.glue[ac][3]
+ h.stretch_order=0; h.shrink_order=0
+ g = node_new(id_glue)
+ g.subtype = 0; g.spec = h
+ elseif z.kern and z.kern[ac] then
+ g = node_new(id_kern)
+ g.subtype = 1; g.kern = z.kern[ac]
+ end
+ if g then
+ set_attr(g, attr_icflag, FROM_JFM); set_attr(g, attr_uniqid, uniq_id)
+ end
+ return g
+end
+
+-- Nq.last (kern w) .... (glue/kern g) Np.first
+local function real_insert(w, g)
+ if w~=0 then
+ local h = node_new(id_kern)
+ set_attr(h, attr_icflag, LINE_END)
+ set_attr(h, attr_uniqid, uniq_id)
+ h.kern = w; h.subtype = 1
+ head = node_insert_after(head, Nq.last, h)
+ end
+ if g then
+ head = node_insert_before(head, Np.first, g)
+ Np.first = g
+ end
+end
+
+-------------------- 和文文字間空白量の決定
+
+-- get kanjiskip
+local function get_kanji_skip_from_jfm(Nn)
+ local i = Nn.met.size_cache[Nn.size].kanjiskip
+ if i then
+ return { i[1], i[2], i[3] }
+ else return nil
+ end
+end
+local function get_kanjiskip()
+ local g = node_new(id_glue)
+ if Np.auto_kspc or Nq.auto_kspc then
+ if kanji_skip.width == max_dimen then
+ local gx = node_new(id_glue_spec);
+ gx.stretch_order = 0; gx.shrink_order = 0
+ local bk = get_kanji_skip_from_jfm(Nq)
+ local ak
+ if (Np.met==Nq.met) and (Nq.size==Np.size) and (Nq.var==Np.var) then
+ ak = nil
+ else
+ ak = get_kanji_skip_from_jfm(Np)
+ end
+ if bk then
+ if ak then
+ gx.width = round(diffmet_rule(bk[1], ak[1]))
+ gx.stretch = round(diffmet_rule(bk[2], ak[2]))
+ gx.shrink = -round(diffmet_rule(-bk[3], -ak[3]))
+ else
+ gx.width = bk[1]; gx.stretch = bk[2]; gx.shrink = bk[3]
+ end
+ elseif ak then
+ gx.width = ak[1]; gx.stretch = ak[2]; gx.shrink = ak[3]
+ else node_free(gx); gx = get_zero_spec() -- fallback
+ end
+ g.spec = gx
+ else g.spec=node_copy(kanji_skip); node_free(gx) end
+ else
+ g.spec = get_zero_spec(); node_free(gx)
+ end
+ set_attr(g, attr_icflag, KANJI_SKIP)
+ set_attr(g, attr_uniqid, uniq_id)
+ return g
+end
+
+local function calc_ja_ja_aux(gb,ga)
+ if not gb then
+ return ga
+ else
+ if not ga then return gb end
+ local k = node.type(gb.id) .. node.type(ga.id)
+ if k == 'glueglue' then
+ -- 両方とも glue.
+ gb.spec.width = round(diffmet_rule(gb.spec.width, ga.spec.width))
+ gb.spec.stretch = round(diffmet_rule(gb.spec.stretch,ga.spec.shrink))
+ gb.spec.shrink = -round(diffmet_rule(-gb.spec.shrink, -ga.spec.shrink))
+ node_free(ga)
+ return gb
+ elseif k == 'kernkern' then
+ -- 両方とも kern.
+ gb.kern = round(diffmet_rule(gb.kern, ga.kern))
+ node_free(ga)
+ return gb
+ elseif k == 'kernglue' then
+ -- gb: kern, ga: glue
+ ga.spec.width = round(diffmet_rule(gb.kern,ga.spec.width))
+ ga.spec.stretch = round(diffmet_rule(ga.spec.stretch, 0))
+ ga.spec.shrink = -round(diffmet_rule(-ga.spec.shrink, 0))
+ node_free(gb)
+ return ga
+ else
+ -- gb: glue, ga: kern
+ gb.spec.width = round(diffmet_rule(ga.kern, gb.spec.width))
+ gb.spec.stretch = round(diffmet_rule(gb.spec.stretch, 0))
+ gb.spec.shrink = -round(diffmet_rule(-gb.spec.shrink, 0))
+ node_free(ga)
+ return gb
+ end
+ end
+end
+
+local function calc_ja_ja_glue()
+ if ihb_flag then return nil
+ elseif (Nq.size==Np.size) and (Nq.met==Np.met) and (Nq.var==Np.var) then
+ return new_jfm_glue(Nq, Nq.class, Np.class)
+ else
+ local g = new_jfm_glue(Nq, Nq.class,
+ 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
+end
+
+-------------------- 和欧文間空白量の決定
+
+-- get xkanjiskip
+local function get_xkanji_skip_from_jfm(Nn)
+ local i = Nn.met.size_cache[Nn.size].xkanjiskip
+ if i then
+ return { i[1], i[2], i[3] }
+ else return nil
+ end
+end
+local function get_xkanjiskip(Nn)
+ local g = node_new(id_glue)
+ if Nq.xspc_after and Np.xspc_before and (Nq.auto_xspc or Np.auto_xspc) then
+ if xkanji_skip.width == max_dimen then
+ local gx = node_new(id_glue_spec);
+ gx.stretch_order = 0; gx.shrink_order = 0
+ local bk = get_xkanji_skip_from_jfm(Nn)
+ if bk then
+ gx.width = bk[1]; gx.stretch = bk[2]; gx.shrink = bk[3]
+ else node_free(gx); gx = get_zero_spec() -- fallback
+ end
+ g.spec = gx
+ else g.spec=node_copy(xkanji_skip) end
+ else
+ g.spec = get_zero_spec()
+ end
+ set_attr(g, attr_icflag, XKANJI_SKIP)
+ set_attr(g, attr_uniqid, uniq_id)
+ return g
+end
+
+
+-------------------- 隣接した「塊」間の処理
+
+local function get_OA_skip()
+ if not ihb_flag then
+ 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
+ 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
+ if Nq.id==id_jglyph or ((Nq.id==id_pbox or Nq.id==id_pbox_w) and Nq.met) then
+ g = calc_ja_ja_glue() or get_kanjiskip() -- M->K
+ g = lineend_fix(g)
+ handle_penalty_normal(Nq.post, Np.pre, g); real_insert(Nq.lend, g)
+ elseif Nq.met then -- Nq.id==id_hlist
+ g = get_OA_skip() or get_kanjiskip() -- O_A->K
+ handle_penalty_normal(0, Np.pre, g); real_insert(0, g)
+ elseif Nq.pre then
+ g = get_OA_skip() or get_xkanjiskip(Np) -- O_A->X
+ handle_penalty_normal(Nq.post, Np.pre, g); real_insert(0, g)
+ else
+ g = get_OA_skip() -- O_A
+ if Nq.id==id_glue then handle_penalty_normal(0, Np.pre, g)
+ elseif Nq.id==id_kern then handle_penalty_suppress(0, Np.pre, g)
+ else handle_penalty_always(0, Np.pre, g)
+ end
+ real_insert(0, g)
+ end
+ -- \jcharwidowpenalty 挿入予定箇所更新
+ 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
+ if Np.pre then
+ g = get_OB_skip() or get_xkanjiskip(Nq) -- O_B->X
+ g = lineend_fix(g)
+ handle_penalty_normal(Nq.post, Np.pre, g); real_insert(Nq.lend, g)
+ else
+ g = get_OB_skip(); g = lineend_fix(g) -- O_B
+ if Np.id==id_glue then handle_penalty_normal(Nq.post, 0, g)
+ elseif Np.id==id_kern then handle_penalty_suppress(Nq.post, 0, g)
+ else handle_penalty_always(Nq.post, 0, g)
+ end
+ real_insert(Nq.lend, g)
+ end
+end
+
+-- (anything) .. (和文文字で終わる hlist)
+local function handle_np_ja_hlist()
+ local g
+ if Nq.id==id_jglyph or ((Nq.id==id_pbox or Nq.id == id_pbox_w) and Nq.met) then
+ g = get_OB_skip() or get_kanjiskip() -- O_B->K
+ g = lineend_fix(g)
+ handle_penalty_normal(Nq.post, 0, g); real_insert(Nq.lend, g)
+ elseif Nq.met then -- Nq.id==id_hlist
+ g = get_kanjiskip() -- K
+ handle_penalty_suppress(0, 0, g); real_insert(0, g)
+ elseif Nq.pre then
+ g = get_xkanjiskip(Np) -- X
+ handle_penalty_suppress(0, 0, g); real_insert(0, g)
+ end
+end
+
+-- (和文文字で終わる hlist) .. (anything)
+local function handle_nq_ja_hlist()
+ local g = nil
+ if Np.pre then
+ g = get_xkanjiskip(Nq) -- X
+ handle_penalty_suppress(0, 0, g); real_insert(0, g)
+ end
+end
+
+-------------------- 開始・終了時の処理
+
+-- リスト末尾の処理
+local function handle_list_tail()
+ Np = Nq
+ if mode then
+ -- the current list is to be line-breaked:
+ if Np.id == id_jglyph or (Np.id==id_pbox and Np.met) then
+ if Np.lend~=0 then
+ g = node_new(id_kern); g.subtype = 0; g.kern = Np.lend
+ set_attr(g, attr_icflag, BOXBDD)
+ node_insert_after(head, Np.last, g)
+ end
+ end
+ -- Insert \jcharwidowpenalty
+ Bp = widow_Bp; Np = widow_Np; Nq.lend = 0
+ if Np.first then
+ handle_penalty_normal(0,
+ 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, 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); 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
+ 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
+ end
+ end
+end
+
+-- initialize
+local function init_var()
+ uniq_id = uniq_id +1
+ if uniq_id == 0x7FFFFFF then uniq_id = 0 end
+ 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 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
+ -- the current list is the contents of a hbox:
+ -- insert a sentinelEG
+ last=node.tail(head); local g = node_new(id_kern)
+ node_insert_after(head, last, g); last = g
+ end
+end
+
+-- Nq が前側のクラスタとなることによる修正
+local function adjust_nq()
+ if Nq.id==id_glyph then after_alchar(Nq)
+ elseif Nq.id==id_hlist or Nq.id==id_pbox or Nq.id==id_disc then after_hlist(Nq)
+ elseif Nq.id == id_pbox_w then
+ luatexbase.call_callback("luatexja.jfmglue.whatsit_after",
+ false, Nq, Np, box_stack_level)
+ end
+end
+-------------------- 外部から呼ばれる関数
+
+-- main interface
+function main(ahead, amode)
+ if not ahead then return ahead end
+ head = ahead; mode = amode; init_var(); calc_np()
+ if Np then
+ extract_np(); handle_list_head()
+ else
+ if not mode then head = node_remove(head, last); node_free(last) end
+ return head
+ end
+ calc_np()
+ while Np do
+ extract_np(); adjust_nq()
+ -- 挿入部
+ if Np.id == id_jglyph then
+ handle_np_jachar()
+ elseif Np.met then
+ if Np.id==id_hlist then handle_np_ja_hlist()
+ else handle_np_jachar() end
+ elseif Nq.met then
+ if Nq.id==id_hlist then handle_nq_ja_hlist()
+ else handle_nq_jachar() end
+ end
+ calc_np()
+ end
+ handle_list_tail()
+ -- adjust attr_icflag
+ tex.attribute[attr_icflag] = -(0x7FFFFFFF)
+ return head
+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()
+ node.write(node.copy(inhibitglue_node))
+end