OSDN Git Service

Initial Commit.
[luatex-ja/luatexja.git] / src / luatexja-core.lua
1 -- error messages
2 function ltj.error(s)
3   tex.error("LuaTeX-ja error: " .. s ); 
4 end
5
6 -- procedures for loading Japanese font metric
7 jfm={}; jfm.char_type={}; jfm.glue={}; jfm.kern={}
8
9 function jfm.define_char_type(t,lt) 
10    if not jfm.char_type[t] then jfm.char_type[t]={} end
11    jfm.char_type[t].chars=lt 
12 end
13 function jfm.define_type_dim(t,l,w,h,d,i)
14    if not jfm.char_type[t] then jfm.char_type[t]={} end
15    jfm.char_type[t].width=w; jfm.char_type[t].height=h;
16    jfm.char_type[t].depth=d; jfm.char_type[t].italic=i; jfm.char_type[t].left=l
17 end
18 function jfm.define_glue(b,a,w,st,sh)
19    local j=b*0x800+a
20    if not jfm.glue[j] then jfm.glue[j]={} end
21    jfm.glue[j].width=w; jfm.glue[j].stretch=st; 
22    jfm.glue[j].shrink=sh
23 end
24 function jfm.define_kern(b,a,w)
25    local j=b*0x800+a
26    if not jfm.kern[j] then jfm.kern[j]=w end
27 end
28
29 -- procedures for \loadjfontmetric
30 ltj.metrics={} -- this table stores all metric information
31
32 function ltj.loadjfontmetric()
33    if string.len(jfm.name)==0 then
34       ltj.error("the key of font metric is null"); return nil
35    elseif ltj.metrics[jfm.name] then
36       ltj.error("the metric '" .. jfm.name .. "' is already loaded"); return nil
37    end
38    ltj.metrics[jfm.name]={}
39    if jfm.dir~='yoko' then
40       ltj.error("jfm.dir must be 'yoko'"); return nil
41    end
42    ltj.metrics[jfm.name].dir=jfm.dir
43    ltj.metrics[jfm.name].zw=jfm.zw
44    ltj.metrics[jfm.name].zh=jfm.zh
45    ltj.metrics[jfm.name].char_type=jfm.char_type
46    ltj.metrics[jfm.name].glue=jfm.glue
47    ltj.metrics[jfm.name].kern=jfm.kern
48 end
49
50 function ltj.find_char_type(c,metrickey)
51 -- c: character code, metrickey: key
52    for i, v in pairs(ltj.metrics[metrickey].char_type) do
53       if i~=0 then
54         for j,w in pairs(v.chars) do
55            if w==c then return i; end
56         end
57       end
58    end
59    return 0
60 end
61
62 -- procedures for \jfont command.
63 function ltj.jfontdefA(b)
64   ltj.fntbki=font.current()
65   ltj.cstemp=token.csname_name(token.get_next())
66   tex.sprint('\\csname ' .. ltj.cstemp .. '\\endcsname\\csname @jfont\\endcsname')
67   -- A trick to get font id associated of the argument of \jfont.
68   -- font.id() does not seem to work in my environment...
69 end
70 function ltj.jfontdefB(s) -- for horizontal font
71    if not ltj.metrics[s] then
72       ltj.error("metric named '" .. s .. "' didn't loaded")
73       return
74    end
75    local i = ltj.transform_jfont(font.current(), s)
76    tex.sprint('\\expandafter\\def\\csname ' .. ltj.cstemp .. '\\endcsname' 
77               .. '{\\csname luatexja@curjfnt\\endcsname=' .. i
78               .. '\\zw=' .. font.fonts[i].parameters.quad .. 'sp' 
79               .. '\\zh=' .. font.fonts[i].parameters.x_height .. 'sp\\relax}')
80    font.current(ltj.fntbki); ltj.fntbk = {}; ltj.cstemp = {}
81 end
82
83 -- "transform" font according to a font metric
84 function ltj.transform_jfont(index,metrickey)
85    local f = {}
86    local of=font.fonts[index]
87    f.name = of.name
88    f.fullname = metrickey
89    f.size = of.size
90    f.parameters = {}
91    f.parameters.quad=tex.round(f.size*ltj.metrics[metrickey].zw)
92    f.parameters.x_height=tex.round(f.size*ltj.metrics[metrickey].zh)
93    f.designsize = of.designsize
94    f.type = 'virtual'
95    f.characters = {}
96    f.fonts = { { id = index } }
97    for i,v in pairs(of.characters) do
98       f.characters[i]={}
99       local ci = ltj.metrics[metrickey].char_type[ltj.find_char_type(i,metrickey)]
100       if ci.left~=0.0 then
101          f.characters[i].commands = { 
102             {'right', tex.round(-ci.left*f.size)}, 
103             {'char',i} 
104          }
105       else
106          f.characters[i].commands = { {'char',i} }
107       end
108       f.characters[i].italic = tex.round(ci.italic*f.size)
109       f.characters[i].width  = tex.round(ci.width*f.size)
110       f.characters[i].height = tex.round(ci.height*f.size)
111       f.characters[i].depth  = tex.round(ci.depth*f.size)
112    end
113    if not f.characters[0x3000] then
114       f.characters[0x3000]={}
115       local ci = ltj.metrics[metrickey].char_type[ltj.find_char_type(0x3000,metrickey)]
116       f.characters[0x3000].commands = {{'right', tex.round(ci.width*f.size)} }
117       f.characters[0x3000].italic = tex.round(ci.italic*f.size)
118       f.characters[0x3000].width  = tex.round(ci.width*f.size)
119       f.characters[0x3000].height = tex.round(ci.height*f.size)
120       f.characters[0x3000].depth  = tex.round(ci.depth*f.size)
121    end
122    return font.define(f)
123 end
124
125
126 -- Replace font in nodes which character code is in the range of
127 -- Japanese characters, using two attributes
128 function ltj.replace_ja_font(head)
129    local p=head
130    while p do 
131       if node.type(p.id)=='glyph' then
132          if ltj.is_ucs_in_japanese_char(p.char) then
133             local v = node.has_attribute(p,luatexbase.attributes['luatexja@curjfnt'])
134             if v then p.font=v end
135             v=node.has_attribute(p,luatexbase.attributes['luatexja@ykblshift'])
136             if v then 
137                node.set_attribute(p,luatexbase.attributes['luatexja@yablshift'],v)
138             else
139                node.unset_attribute(p,luatexbase.attributes['luatexja@yablshift'])
140             end
141          end
142       end
143       p=node.next(p)
144    end
145    return head
146 end
147
148
149 -- return true if and only if p is a Japanese character node
150 function ltj.is_japanese_glyph_node(p)
151    if not p then return false
152    elseif node.type(p.id)~='glyph' then return false
153    elseif p.font==node.has_attribute(p,luatexbase.attributes['luatexja@curjfnt']) then
154       return true
155    else return false
156    end
157 end
158
159 ---------- Kinsoku 
160 ----------
161 ltj.penalty_table = {}
162 function ltj.set_penalty_table(m,c,p)
163    if not ltj.penalty_table[c] then ltj.penalty_table[c]={} end
164    if m=='pre' then
165       ltj.penalty_table[c].pre=p
166    elseif m=='post' then
167       ltj.penalty_table[c].post=p
168    end
169 end
170
171 ltj.inhibit_xsp_table = {}
172 function ltj.set_inhibit_xsp_table(c,p)
173    ltj.inhibit_xsp_table[c]=p
174 end
175
176 ----------
177 ----------
178 function ltj.create_ihb_node()
179    local g=node.new(node.id('whatsit'), node.subtype('user_defined'))
180    g.user_id=30111; g.type=number; g.value=1
181    node.write(g)
182 end
183
184 -- The fullname field of virtual font expresses its metric
185 function ltj.find_size_metrickey(p)
186    if ltj.is_japanese_glyph_node(p) then
187       return font.fonts[p.font].size, font.fonts[p.font].fullname
188    else 
189       return nil, nil
190    end
191 end
192
193 function ltj.new_jfm_glue(size,mt,bc,ac)
194 -- mt: metric key, bc, ac: char classes
195    local g=nil
196    local h
197    local w=bc*0x800+ac
198    if ltj.metrics[mt].glue[w] then
199       h=node.new(node.id('glue_spec'))
200       h.width  =tex.round(size*ltj.metrics[mt].glue[w].width)
201       h.stretch=tex.round(size*ltj.metrics[mt].glue[w].stretch)
202       h.shrink =tex.round(size*ltj.metrics[mt].glue[w].shrink)
203       h.stretch_order=0; h.shrink_order=0
204       g=node.new(node.id('glue'))
205       g.subtype=0; g.spec=h; return g
206    elseif ltj.metrics[mt].kern[w] then
207       g=node.new(node.id('kern'))
208       g.subtype=0; g.kern=tex.round(size*ltj.metrics[mt].kern[w]); return g
209    else
210       return nil
211    end
212 end
213
214 -- The fullname field of virtual font expresses its metric
215 function ltj.calc_between_two_jchar(q,p)
216    -- q, p: node (possibly null)
217    local ps,pm,qs,qm,g,h
218    if not p then -- q is the last node
219       qs, qm = ltj.find_size_metrickey(q)
220       if not qm then 
221          return nil
222       else
223          g=ltj.new_jfm_glue(qs,qm,
224                                 ltj.find_char_type(q.char,qm),
225                                 ltj.find_char_type('boxbdd',qm))
226       end
227    elseif not q then
228       -- p is the first node etc.
229       if q then print(node.type(q.id)) end
230       ps, pm = ltj.find_size_metrickey(p)
231       if not pm then
232          return nil
233       else
234          g=ltj.new_jfm_glue(ps,pm,
235                                 ltj.find_char_type('boxbdd',pm),
236                                 ltj.find_char_type(p.char,pm))
237       end
238    else -- p and q are not nil
239       qs, qm = ltj.find_size_metrickey(q)
240       ps, pm = ltj.find_size_metrickey(p)
241       if (not pm) and (not qm) then 
242          -- Both p and q are NOT Japanese glyph node
243          return nil
244       elseif (qs==ps) and (qm==pm) then 
245          -- Both p and q are Japanese glyph node, and same metric and size
246          g=ltj.new_jfm_glue(ps,pm,
247                                 ltj.find_char_type(q.char,qm),
248                                 ltj.find_char_type(p.char,pm))
249       elseif not qm then
250          -- q is not Japanese glyph node
251          g=ltj.new_jfm_glue(ps,pm,
252                                 ltj.find_char_type('jcharbdd',pm),
253                                 ltj.find_char_type(p.char,pm))
254       elseif not pm then
255          -- p is not Japanese glyph node
256          g=ltj.new_jfm_glue(qs,qm,
257                                 ltj.find_char_type(q.char,qm),
258                                 ltj.find_char_type('jcharbdd',qm))
259       else
260          g=ltj.new_jfm_glue(qs,qm,
261                                 ltj.find_char_type(q.char,qm),
262                                 ltj.find_char_type('diffmet',qm))
263          h=ltj.new_jfm_glue(ps,pm,
264                                 ltj.find_char_type('diffmet',pm),
265                                 ltj.find_char_type(p.char,pm))
266          g=ltj.calc_between_two_jchar_aux(g,h)
267       end
268    end
269    return g
270 end
271
272
273 -- In the beginning of a hbox created by line breaking, there are the followings:
274 --   o a hbox by \parindent
275 --   o a whatsit node which contains local paragraph materials.
276 -- When we insert jfm glues, we ignore these nodes.
277 function ltj.is_parindent_box(p)
278    if node.type(p.id)=='hlist' then 
279       return (p.subtype==3)
280       -- hlist (subtype=3) is a box by \parindent
281    elseif node.type(p.id)=='whatsit' then 
282       return (p.subtype==node.subtype('local_par'))
283    end
284 end
285
286 function ltj.add_kinsoku_penalty(head,p)
287    local c = p.char
288    if not ltj.penalty_table[c] then return false; end
289    if ltj.penalty_table[c].pre then
290       local q = node.prev(p)
291       if (not q) and node.type(q.id)=='penalty' then
292          q.penalty=q.penalty+ltj.penalty_table[c].pre
293       else 
294          q=node.new(node.id('penalty'))
295          q.penalty=ltj.penalty_table[c].pre
296          node.insert_before(head,p,q)
297       end
298    end
299    if ltj.penalty_table[c].post then
300       local q = node.next(p)
301       if (not q) and node.type(q.id)=='penalty' then
302          q.penalty=q.penalty+ltj.penalty_table[c].post
303          return false
304       else 
305          q=node.new(node.id('penalty'))
306          q.penalty=ltj.penalty_table[c].post
307          node.insert_after(head,p,q)
308          return true
309       end
310    end
311 end
312
313 -- Insert jfm glue: main routine
314
315 function ltj.insert_jfm_glue(head)
316    local p = head
317    local q = nil  -- the previous node of p
318    local g
319    local ihb_flag = false
320    if not p then 
321       return head 
322    end
323    while p and  ltj.is_parindent_box(p) do p=node.next(p) end
324    while p do
325       if node.type(p.id)=='whatsit' and p.subtype==44
326          and p.user_id==30111 then
327          g=p; p=node.next(p); 
328          ihb_flag=true; head,p=node.remove(head, g)
329       else
330          g=ltj.calc_between_two_jchar(q,p)
331          if g and (not ihb_flag) then
332             h = node.insert_before(head,p,g)
333             if not q then head=h end 
334             -- If p is the first node (=head), the skip is inserted
335             -- before head. So we must change head.
336          end
337          q=p; ihb_flag=false
338          if ltj.is_japanese_glyph_node(p) 
339             and ltj.add_kinsoku_penalty(head,p) then
340             p=node.next(p)
341          end
342          p=node.next(p)
343       end
344    end
345    -- Insert skip after the last node
346    g=ltj.calc_between_two_jchar(q,nil)
347    if g then
348       h = node.insert_after(head,q,g)
349    end
350    return head
351 end
352
353
354
355 -- Insert \xkanjiskip at the boundaries between Japanese characters 
356 -- and non-Japanese characters. 
357 -- We also insert \kanjiskip between Kanji in this function.
358 ltj.kanji_skip={}
359 ltj.xkanji_skip={}
360 ltj.insert_skip=0 
361 ltj.cx = nil
362     -- 0: ``no_skip'', 1: ``after_schar'', 2: ``after_wchar''
363 -- These variables are ``global'', because we want to avoid to write one large function.
364 function ltj.insert_kanji_skip(head)
365    if tex.count['luatexja@autospc']==0 then
366       ltj.kanji_skip=tex.skip['kanjiskip']
367    else
368       ltj.kanji_skip=node.new(node.id('glue_spec'))
369       ltj.kanji_skip.width=0;  ltj.kanji_skip.stretch=0; ltj.kanji_skip.shrink=0
370    end
371    if tex.count['luatexja@autoxspc']==0 then
372       ltj.xkanji_skip=tex.skip['xkanjiskip']
373    else
374       ltj.xkanji_skip=node.new(node.id('glue_spec'))
375       ltj.xkanji_skip.width=0;  ltj.xkanji_skip.stretch=0; ltj.xkanji_skip.shrink=0
376    end
377    local p=head -- 「現在のnode」
378    local q=nil  -- pの一つ前 
379    ltj.insert_skip=0
380    while p do
381       if node.type(p.id)=='glyph' then
382          repeat 
383             ltj.insks_around_char(head,q,p)
384             q=p; p=node.next(p)
385          until (not p) or node.type(p.id)~='glyph'
386       else
387          if node.type(p.id) == 'hlist' then
388             ltj.insks_around_hbox(head,q,p)
389          elseif node.type(p.id) == 'penalty' then
390             ltj.insks_around_penalty(head,q,p)
391          elseif node.type(p.id) == 'kern' then
392             ltj.insks_around_kern(head,q,p)
393          elseif node.type(p.id) == 'math' then
394             ltj.insks_around_math(head,q,p)
395          elseif node.type(p.id) == 'ins' or node.type(p.id) == 'mark'
396             or node.type(p.id) == 'adjust'
397             or node.type(p.id) == 'whatsit' then
398             -- do nothing
399             p=p
400          else
401             -- rule, disc, glue, margin_kern
402             ltj.insert_skip=0
403          end
404          q=p; p=node.next(p)
405       end
406    end
407    return head
408 end
409
410 -- Insert \xkanjiskip before p, a glyph node
411 -- TODO; ligature
412 function ltj.insks_around_char(head,q,p)
413    local a=ltj.inhibit_xsp_table[p.char]
414    if ltj.is_japanese_glyph_node(p) then
415       ltj.cx=p.char
416       if ltj.is_japanese_glyph_node(q)  then
417          local g = node.new(node.id('glue'))
418          g.subtype=0; g.spec=node.copy(ltj.kanji_skip)
419          node.insert_before(head,p,g)
420       elseif ltj.insert_skip==1 then
421          ltj.insert_akxsp(head,q)
422       end
423       ltj.insert_skip=2
424    else
425       if not a then a=3 end
426       if ltj.insert_skip==2 then
427          ltj.insert_kaxsp(head,q,a)
428       end
429       if  a>=2 then
430          ltj.insert_skip=1
431       else
432          ltj.insert_skip=0
433       end
434    end
435 end
436
437 function ltj.insert_akxsp(head,q)
438    local f = ltj.inhibit_xsp_table[ltj.cx]
439    local g
440    if f then 
441       if f<=1 then return end
442    end
443    g = node.new(node.id('glue'))
444    g.subtype=0; g.spec=node.copy(ltj.xkanji_skip)
445    node.insert_after(head,q,g)
446 end
447
448 function ltj.insert_kaxsp(head,q,a)
449    local g=true
450    local f=ltj.inhibit_xsp_table[ltj.cx]
451    if a%2 == 1 then
452       if f then 
453          if f%2==0 then g=false end
454       end
455    else 
456       g=false
457    end
458    if g then
459       g = node.new(node.id('glue'))
460       g.subtype=0; g.spec=node.copy(ltj.xkanji_skip)
461       node.insert_after(head,q,g)
462    end
463 end
464
465 -- Return first and last glyph nodes in a hbox
466 ltj.first_char = nil
467 ltj.last_char = nil
468 ltj.find_first_char = nil
469 function ltj.check_box(bp)
470    local p, flag
471    p=bp; flag=false
472    while p do
473       if node.type(p.id)=='glyph' then
474          repeat 
475             if ltj.find_first_char then
476                ltj.first_char=p; ltj.find_first_char=false
477             end
478             ltj.last_char=p; flag=true; p=node.next(p)
479             if not p then return flag end
480          until node.type(p.id)~='glyph'
481       end
482       if node.type(p.id)=='hlist' then
483          flag=true
484          if p.shift==0 then
485             if ltj.check_box(p.head) then flag=true end
486          else if ltj.find_first_char then 
487                ltj.find_first_char=false
488             else 
489                ltj.last_char=nil
490             end
491          end
492       elseif node.type(p.id) == 'ins' or node.type(p.id) == 'mark'
493          or node.type(p.id) == 'adjust' 
494          or node.type(p.id) == 'whatsit' or node.type(p.id) == 'penalty' then
495          p=p
496       else
497          flag=true
498          if ltj.find_first_char then 
499             ltj.find_first_char=false
500          else 
501             ltj.last_char=nil
502          end
503       end
504       p=node.next(p)
505    end
506    return flag
507 end 
508
509 -- Insert \xkanjiskip around p, an hbox
510 function ltj.insks_around_hbox(head,q,p)
511    if p.shift==0 then
512       ltj.find_first_char=true
513       if ltj.check_box(p.head) then
514          -- first char
515          if ltj.is_japanese_glyph_node(ltj.first_char) then
516             ltj.cx=ltj.first_char.char
517             if ltj.insert_skip==1 then 
518                ltj.insert_akxsp(head,q)
519             elseif ltj.insert_skip==2 then
520                local g = node.new(node.id('glue'))
521                g.subtype=0; g.spec=node.copy(ltj.kanji_skip)
522                node.insert_before(head,p,g)
523             end
524             ltj.insert_skip=2
525          elseif ltj.first_char then
526             local a=ltj.inhibit_xsp_table[ltj.first_char.char]
527             if not a then a=3 end
528             if ltj.insert_skip==2 then
529                local g = node.new(node.id('glue'))
530                g.subtype=0; g.spec=node.copy(ltj.kanji_skip)
531                node.insert_after(head,q,g)
532             end
533             if  a>=2 then
534                ltj.insert_skip=1
535             else
536                ltj.insert_skip=0
537             end
538          end
539          -- last char
540          if ltj.is_japanese_glyph_node(ltj.last_char) then
541             if ltj.is_japanese_glyph_node(node.next(p)) then
542                local g = node.new(node.id('glue'))
543                g.subtype=0; g.spec=node.copy(ltj.kanji_skip)
544                node.insert_after(head,p,g)
545             end
546             ltj.insert_skip=2
547          elseif ltj.last_char then
548             local a=ltj.inhibit_xsp_table[ltj.last_char.char]
549             if not a then a=3 end
550             if a>=2 then
551                ltj.insert_skip=1
552             else
553                ltj.insert_skip=0
554             end
555          else ltj.insert_skip=0
556          end
557       else ltj.insert_skip=0
558       end
559    else ltj.insert_skip=0
560    end
561 end
562
563 -- Insert \xkanjiskip around p, a penalty
564 function ltj.insks_around_penalty(head,q,p)
565    local r=node.next(p)
566    if (not r) and node.type(r.id)=='glyph' then
567       local a=ltj.inhibit_xsp_table[r.char]
568       if ltj.is_japanese_glyph_node(r) then
569          ltj.cx=r.char
570          if ltj.is_japanese_glyph_node(p)  then
571             local g = node.new(node.id('glue'))
572             g.subtype=0; g.spec=node.copy(ltj.kanji_skip)
573             node.insert_before(head,r,g)
574          elseif ltj.insert_skip==1 then
575             ltj.insert_akxsp(head,p)
576          end
577          q=p; p=node.next(p)
578          ltj.insert_skip=2
579       else
580          if not a then a=3 end
581          if ltj.insert_skip==2 then
582             ltj.insert_kaxsp(head,p,a)
583          end
584          if  a>=2 then
585             ltj.insert_skip=1
586          else
587             ltj.insert_skip=0
588          end
589       end
590    end
591 end
592
593 -- Insert \xkanjiskip around p, a kern
594 function ltj.insks_around_kern(head,q,p)
595    if p.subtype==1 then -- \kern or \/
596       if node.has_attribute(p,luatexbase.attributes['luatexja@icflag']) then
597          p=p -- p is a kern from \/: do nothing
598       else
599          ltj.insert_skip=0
600       end
601    elseif p.subtype==2 then -- \accent: We ignore the accent character.
602       local v = node.next(node.next(node.next(p)))
603       if v and node.type(v.id)=='glyph' then
604          ltj.insks_around_char(head,q,v)
605       end
606    end
607 end
608
609 -- Insert \xkanjiskip around p, a math_node
610 function ltj.insks_around_math(head,q,p)
611    local a=ltj.inhibit_xsp_table['math']
612    if not a then a=3 end
613    if (p.subtype==0) and (ltj.insert_skip==2) then
614       ltj.insert_kaxsp(head,q,a)
615       ltj.insert_skip=0
616    else
617       ltj.insert_skip=1
618    end
619 end
620
621 -- Shift baseline
622 function ltj.baselineshift(head)
623    local p=head
624    local m=false -- is in math mode?
625    while p do 
626       local v=node.has_attribute(p,luatexbase.attributes['luatexja@yablshift'])
627       if v then
628          if node.type(p.id)=='glyph' then
629             p.yoffset=p.yoffset-v
630          elseif node.type(p.id)=='math' then
631             m=(p.subtype==0)
632          end
633          if m then -- boxes and rules are shifted only in math mode
634             if node.type(p.id)=='hlist' or node.type(p.id)=='vlist' then
635                p.shift=p.shift+v
636             elseif node.type(p.id)=='rule' then
637                p.height=p.height-v; p.depth=p.depth+v 
638             end
639          end
640       end
641       p=node.next(p)
642    end
643    return head
644 end
645
646
647 -- main process
648 function ltj.main_process(head)
649    local p = head
650    p = ltj.insert_jfm_glue(p)
651    p = ltj.insert_kanji_skip(p)
652    p = ltj.baselineshift(p)
653    return p
654 end
655
656 --- the following function is modified from jafontspec.lua (by K. Maeda).
657 --- Instead of "%", we use U+FFFFF for suppressing spaces.
658 utf = unicode.utf8
659 function ltj.process_input_buffer(buffer)
660 --      print(utf.byte(buffer, utf.len(buffer)))
661    if utf.len(buffer) > 0 
662         and ltj.is_ucs_in_japanese_char(utf.byte(buffer, utf.len(buffer))) then
663         buffer = buffer .. string.char(0xF3,0xBF,0xBF,0xBF) -- U+FFFFF
664    end
665    return buffer
666 end
667
668
669 ---------- Hyphenate
670
671 -- 
672 function ltj.suppress_hyphenate_ja(head)
673    local p=head
674    while p do 
675       if node.type(p.id)=='glyph' and  ltj.is_ucs_in_japanese_char(p.char) then
676          local v = node.has_attribute(p,luatexbase.attributes['luatexja@curjfnt'])
677          if v then p.font=v end
678          v=node.has_attribute(p,luatexbase.attributes['luatexja@ykblshift'])
679          if v then 
680             node.set_attribute(p,luatexbase.attributes['luatexja@yablshift'],v)
681          else
682             node.unset_attribute(p,luatexbase.attributes['luatexja@yablshift'])
683          end
684          p.lang=ltj.ja_lang_number
685       end
686       p=node.next(p)
687    end
688    lang.hyphenate(head) 
689 end
690
691 -- callback
692 luatexbase.add_to_callback('process_input_buffer', 
693    function (buffer)
694      return ltj.process_input_buffer(buffer)
695    end,'ltj.process_input_buffer')
696 luatexbase.add_to_callback('pre_linebreak_filter', 
697    function (head,groupcode)
698      return ltj.main_process(head)
699    end,'ltj.pre_linebreak_filter')
700 luatexbase.add_to_callback('hpack_filter', 
701   function (head,groupcode,size,packtype)
702      return ltj.main_process(ltj.replace_ja_font(head))
703   end,'ltj.hpack_filter')
704 luatexbase.add_to_callback('hyphenate', 
705  function (head,tail)
706     return ltj.suppress_hyphenate_ja(head)
707  end,'ltj.hyphenate')