OSDN Git Service

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