--
-- 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
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
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 }
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
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
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)
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
-- 和文文字
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)]
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)
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'))
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
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
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
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
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
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
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
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)
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