1 local node_type = node.type
2 local node_new = node.new
3 local node_prev = node.prev
4 local node_next = node.next
5 local has_attr = node.has_attribute
6 local node_insert_before = node.insert_before
7 local node_insert_after = node.insert_after
8 local node_hpack = node.hpack
9 local round = tex.round
11 local id_penalty = node.id('penalty')
12 local id_glyph = node.id('glyph')
13 local id_glue_spec = node.id('glue_spec')
14 local id_glue = node.id('glue')
15 local id_kern = node.id('kern')
16 local id_hlist = node.id('hlist')
17 local id_vlist = node.id('vlist')
18 local id_rule = node.id('rule')
19 local id_math = node.id('math')
20 local id_whatsit = node.id('whatsit')
22 local attr_jchar_class = luatexbase.attributes['ltj@charclass']
23 local attr_curjfnt = luatexbase.attributes['ltj@curjfnt']
24 local attr_yablshift = luatexbase.attributes['ltj@yablshift']
25 local attr_ykblshift = luatexbase.attributes['ltj@ykblshift']
26 local attr_icflag = luatexbase.attributes['ltj@icflag']
27 -- attr_icflag: 1: kern from \/, 2: 'lineend' kern from JFM
29 local lang_ja_token = token.create('ltj@japanese')
30 local lang_ja = lang_ja_token[2]
33 local rgjc_get_range_setting = ltj.int_get_range_setting
34 local rgjc_char_to_range = ltj.int_char_to_range
35 local rgjc_is_ucs_in_japanese_char = ltj.int_is_ucs_in_japanese_char
36 local ljfm_find_char_class = ltj.int_find_char_class
47 ------------------------------------------------------------------------
49 -- ltj.ext_... : called from \directlua{}
50 -- ltj.int_... : called from other Lua codes, but not from \directlua{}
51 -- (other) : only called from this file
54 function ltj.error(s,t)
55 tex.error('LuaTeX-ja error: ' .. s ,t)
58 -- Three aux. functions, bollowed from tex.web
60 local function print_scaled(s)
66 out=out..tostring(math.floor(s/unity)) .. '.'
69 if delta>unity then s=s+32768-50000 end
70 out=out .. tostring(math.floor(s/unity))
77 local function print_glue(d,order)
78 local out=print_scaled(d)
82 out=out..'l'; order=order-1
90 local function print_spec(p)
91 local out=print_scaled(p.width)..'pt'
93 out=out..' plus '..print_glue(p.stretch,p.stretch_order)
96 out=out..' minus '..print_glue(p.shrink,p.shrink_order)
101 -- return true if and only if p is a Japanese character node
102 local function is_japanese_glyph_node(p)
103 return p and (p.id==id_glyph)
104 and (p.font==has_attr(p,attr_curjfnt))
107 function math.two_add(a,b) return a+b end
108 function math.two_average(a,b) return (a+b)/2 end
110 ------------------------------------------------------------------------
111 -- CODE FOR STACK TABLE FOR CHARACTER PROPERTIES (prefix: cstb)
112 ------------------------------------------------------------------------
114 ---- table: charprop_stack_table [stack_level][chr_code].{pre|post|xsp}
115 local charprop_stack_table={}; charprop_stack_table[0]={}
117 local function cstb_get_stack_level()
118 local i = tex.getcount('ltj@@stack')
119 if tex.currentgrouplevel > tex.getcount('ltj@@group@level') then
120 i = i+1 -- new stack level
121 tex.setcount('ltj@@group@level', tex.currentgrouplevel)
122 for j,v in pairs(charprop_stack_table) do -- clear the stack above i
123 if j>=i then charprop_stack_table[j]=nil end
125 charprop_stack_table[i] = table.fastcopy(charprop_stack_table[i-1])
126 tex.setcount('ltj@@stack', i)
132 function ltj.ext_set_stack_table(g,m,c,p,lb,ub)
133 local i = cstb_get_stack_level()
135 ltj.error('Invalid code (' .. p .. '), should in the range '
136 .. tostring(lb) .. '..' .. tostring(ub) .. '.',
137 {"I'm going to use 0 instead of that illegal code value."})
139 elseif c<-1 or c>0x10FFFF then
140 ltj.error('Invalid character code (' .. c
141 .. '), should in the range -1.."10FFFF.',{})
143 elseif not charprop_stack_table[i][c] then
144 charprop_stack_table[i][c] = {}
146 charprop_stack_table[i][c][m] = p
148 for j,v in pairs(charprop_stack_table) do
149 if not charprop_stack_table[j][c] then charprop_stack_table[j][c] = {} end
150 charprop_stack_table[j][c][m] = p
155 local function cstb_get_penalty_table(m,c)
156 local i = charprop_stack_table[tex.getcount('ltj@@stack')][c]
160 ltj.int_get_penalty_table = cstb_get_penalty_table
162 local function cstb_get_inhibit_xsp_table(c)
163 local i = charprop_stack_table[tex.getcount('ltj@@stack')][c]
164 if i then i=i.xsp end
167 ltj.int_get_inhibit_xsp_table = cstb_get_inhibit_xsp_table
169 ------------------------------------------------------------------------
170 -- CODE FOR GETTING/SETTING PARAMETERS
171 ------------------------------------------------------------------------
173 -- EXT: print parameters that don't need arguments
174 function ltj.ext_get_parameter_unary(k)
175 if k == 'yalbaselineshift' then
176 tex.write(print_scaled(tex.getattribute('ltj@yablshift'))..'pt')
177 elseif k == 'yjabaselineshift' then
178 tex.write(print_scaled(tex.getattribute('ltj@ykblshift'))..'pt')
179 elseif k == 'kanjiskip' then
180 tex.write(print_spec(tex.getskip('kanjiskip')))
181 elseif k == 'xkanjiskip' then
182 tex.write(print_spec(tex.getskip('xkanjiskip')))
183 elseif k == 'jcharwidowpenalty' then
184 tex.write(tex.getcount('jcharwidowpenalty'))
185 elseif k == 'autospacing' then
186 tex.write(tex.getattribute('ltj@autospc'))
187 elseif k == 'autoxspacing' then
188 tex.write(tex.getattribute('ltj@autoxspc'))
189 elseif k == 'differentjfm' then
190 if ltj.ja_diffmet_rule == math.max then
192 elseif ltj.ja_diffmet_rule == math.min then
194 elseif ltj.ja_diffmet_rule == math.two_average then
196 elseif ltj.ja_diffmet_rule == math.two_add then
198 else -- This can't happen.
204 -- EXT: print parameters that need arguments
205 function ltj.ext_get_parameter_binary(k,c)
206 if k == 'jacharrange' then
207 if c<0 or c>216 then c=0 end
208 tex.write(rgjc_get_range_setting(c))
210 if c<0 or c>0x10FFFF then
211 ltj.error('Invalid character code (' .. c
212 .. '), should in the range 0.."10FFFF.',
213 {"I'm going to use 0 instead of that illegal character code."})
216 if k == 'prebreakpenalty' then
217 tex.write(cstb_get_penalty_table('pre',c))
218 elseif k == 'postbreakpenalty' then
219 tex.write(cstb_get_penalty_table('post',c))
220 elseif k == 'kcatcode' then
221 tex.write(cstb_get_penalty_table('kcat',c))
222 elseif k == 'chartorange' then
223 tex.write(rgjc_char_to_range(c))
224 elseif k == 'jaxspmode' or k == 'alxspmode' then
225 tex.write(cstb_get_inhibit_xsp_table(c))
230 -- EXT: print \global if necessary
231 function ltj.ext_print_global()
232 if ltj.isglobal=='global' then tex.sprint('\\global') end
236 ------------------------------------------------------------------------
237 -- MAIN PROCESS STEP 1: replace fonts (prefix: main1)
238 ------------------------------------------------------------------------
240 --- the following function is modified from jafontspec.lua (by K. Maeda).
241 --- Instead of "%", we use U+FFFFF for suppressing spaces.
242 local function main1_process_input_buffer(buffer)
243 local c = utf.byte(buffer, utf.len(buffer))
244 local p = node_new(id_glyph)
246 if utf.len(buffer) > 0
247 and rgjc_is_ucs_in_japanese_char(p) then
248 buffer = buffer .. string.char(0xF3,0xBF,0xBF,0xBF) -- U+FFFFF
253 local function main1_suppress_hyphenate_ja(head)
255 for p in node.traverse(head) do
256 if p.id == id_glyph then
257 if rgjc_is_ucs_in_japanese_char(p) then
258 local v = has_attr(p, attr_curjfnt)
261 node.set_attribute(p,attr_jchar_class,
262 ljfm_find_char_class(p.char, ltj.font_metric_table[v].jfm))
264 v = has_attr(p, attr_ykblshift)
266 node.set_attribute(p, attr_yablshift, v)
268 node.unset_attribute(p, attr_yablshift)
279 luatexbase.add_to_callback('process_input_buffer',
281 return main1_process_input_buffer(buffer)
282 end,'ltj.process_input_buffer')
283 luatexbase.add_to_callback('hpack_filter',
284 function (head,groupcode,size,packtype)
285 return main1_suppress_hyphenate_ja(head)
286 end,'ltj.hpack_filter_pre',0)
287 luatexbase.add_to_callback('hyphenate',
289 return main1_suppress_hyphenate_ja(head)
293 ------------------------------------------------------------------------
294 -- MAIN PROCESS STEP 4: width of japanese chars (prefix: main4)
295 ------------------------------------------------------------------------
298 local function main4_get_hss()
299 local hss = node_new(id_glue)
300 local fil_spec = node_new(id_glue_spec)
302 fil_spec.stretch = 65536
303 fil_spec.stretch_order = 2
304 fil_spec.shrink = 65536
305 fil_spec.shrink_order = 2
310 local function main4_set_ja_width(head)
312 local met_tb, t, s, g, q, a, h
313 local m = false -- is in math mode?
315 local v=has_attr(p,attr_yablshift) or 0
316 if p.id==id_glyph then
317 p.yoffset = p.yoffset-v
318 if is_japanese_glyph_node(p) then
319 met_tb = ltj.font_metric_table[p.font]
320 t = ltj.metrics[met_tb.jfm]
321 s = t.char_type[has_attr(p,attr_jchar_class)]
322 if s.width ~= 'prop' and
323 not(s.left==0.0 and s.down==0.0 and s.align=='left'
324 and round(s.width*met_tb.size)==p.width) then
325 -- must be encapsuled by a \hbox
326 head, q = node.remove(head,p)
328 p.yoffset=round(p.yoffset-met_tb.size*s.down)
329 p.xoffset=round(p.xoffset-met_tb.size*s.left)
330 if s.align=='middle' or s.align=='right' then
331 h = node_insert_before(p, p, main4_get_hss())
333 if s.align=='middle' or s.align=='left' then
334 node_insert_after(h, p, main4_get_hss())
336 g = node_hpack(h, round(met_tb.size*s.width), 'exactly')
337 g.height = round(met_tb.size*s.height)
338 g.depth = round(met_tb.size*s.depth)
339 head, p = node_insert_before(head, q, g)
345 elseif p.id==id_math then
346 m=(p.subtype==0); p=node_next(p)
349 if p.id==id_hlist or p.id==id_vlist then
351 elseif p.id==id_rule then
352 p.height=p.height-v; p.depth=p.depth+v
362 -- mode = true iff main_process is called from pre_linebreak_filter
363 local function main_process(head, mode)
365 p = ltj.int_insert_jfm_glue(p,mode)
366 p = ltj.int_insert_kanji_skip(p)
367 -- off because we write the code of step 2
368 p = main4_set_ja_width(p)
375 function ltj.ext_show_node_list(head,depth,print_fn)
378 debug_show_node_list_X(head, print_fn)
380 print_fn(debug_depth .. ' (null list)')
383 function debug_show_node_list_X(p,print_fn)
384 debug_depth=debug_depth.. '.'
385 local k = debug_depth
388 local pt=node_type(p.id)
389 if pt == 'glyph' then
390 print_fn(debug_depth.. ' GLYPH ', p.subtype, utf.char(p.char), p.font)
391 elseif pt=='hlist' then
392 print_fn(debug_depth.. ' hlist ', p.subtype, '(' .. print_scaled(p.height)
393 .. '+' .. print_scaled(p.depth)
394 .. ')x' .. print_scaled(p.width) )
395 debug_show_node_list_X(p.head,print_fn)
397 elseif pt == 'whatsit' then
398 print_fn(debug_depth.. ' whatsit', p.subtype)
399 elseif pt == 'glue' then
400 s = debug_depth.. ' glue ' .. p.subtype
401 .. ' ' .. print_spec(p.spec)
402 if has_attr(p, attr_icflag)==TEMPORARY then
403 s = s .. ' (might be replaced)'
404 elseif has_attr(p, attr_icflag)==FROM_JFM then
405 s = s .. ' (from JFM)'
406 elseif has_attr(p, attr_icflag)==KANJI_SKIP then
407 s = s .. ' (kanjiskip)'
408 elseif has_attr(p, attr_icflag)==XKANJI_SKIP then
409 s = s .. ' (xkanjiskip)'
412 elseif pt == 'kern' then
413 s = debug_depth.. ' kern ' .. p.subtype
414 .. ' ' .. print_scaled(p.kern) .. 'pt'
415 if has_attr(p, attr_icflag)==ITALIC then
416 s = s .. ' (italic correction)'
417 elseif has_attr(p, attr_icflag)==TEMPORARY then
418 s = s .. ' (might be replaced)'
419 elseif has_attr(p, attr_icflag)==FROM_JFM then
420 s = s .. ' (from JFM)'
421 elseif has_attr(p, attr_icflag)==LINE_END then
422 s = s .. " (from 'lineend' in JFM)"
425 elseif pt == 'penalty' then
426 s = debug_depth.. ' penalty ' .. tostring(p.penalty)
427 if has_attr(p, attr_icflag)==KINSOKU then
428 s = s .. ' (for kinsoku)'
432 print_fn(debug_depth.. ' ' .. node.type(p.id), p.subtype)
440 luatexbase.add_to_callback('pre_linebreak_filter',
441 function (head,groupcode)
442 return main_process(head, true)
443 end,'ltj.pre_linebreak_filter',2)
444 luatexbase.add_to_callback('hpack_filter',
445 function (head,groupcode,size,packtype)
446 return main_process(head, false)
447 end,'ltj.hpack_filter',2)