OSDN Git Service

65e349bdbbca92dd0e36f08a853b1c6f9dbe060b
[luatex-ja/luatexja.git] / src / luatexja-xkanji.lua
1 ------------------------------------------------------------------------
2 -- MAIN PROCESS STEP 3: insert \xkanjiskip (prefix: none)
3 ------------------------------------------------------------------------
4
5 local node_type = node.type
6 local node_new = node.new
7 local node_prev = node.prev
8 local node_next = node.next
9 local node_copy = node.copy
10 local has_attr = node.has_attribute
11 local set_attr = node.set_attribute
12 local node_insert_before = node.insert_before
13 local node_insert_after = node.insert_after
14 local node_hpack = node.hpack
15 local round = tex.round
16
17 local id_penalty = node.id('penalty')
18 local id_glyph = node.id('glyph')
19 local id_glue = node.id('glue')
20 local id_glue_spec = node.id('glue_spec')
21 local id_kern = node.id('kern')
22 local id_hlist = node.id('hlist')
23 local id_ins = node.id('ins')
24 local id_mark = node.id('mark')
25 local id_adjust = node.id('adjust')
26 local id_math = node.id('math')
27 local id_whatsit = node.id('whatsit')
28
29 local attr_icflag = luatexbase.attributes['ltj@icflag']
30 local attr_autospc = luatexbase.attributes['ltj@autospc']
31 local attr_autoxspc = luatexbase.attributes['ltj@autoxspc']
32 local attr_curjfnt = luatexbase.attributes['ltj@curjfnt']
33 local max_dimen = 1073741823
34
35 local ITALIC = 1
36 local TEMPORARY = 2
37 local FROM_JFM = 3
38 local KINSOKU = 4
39 local LINE_END = 5
40 local KANJI_SKIP = 6
41 local XKANJI_SKIP = 7
42 local PACKED = 8
43
44 local kanji_skip
45 local xkanji_skip
46
47 -- (glyph_node nr) ... (node nq) <GLUE> ,,, (node np)
48 local np, nq, nrc, nrf
49 local np_spc, nr_spc
50 local no_skip = 0 
51 local after_schar = 1
52 local after_wchar = 2 -- nr is a Japanese glyph_node
53 local insert_skip = no_skip
54 local head
55
56 local cstb_get_penalty_table = ltj.int_get_penalty_table
57 local cstb_get_skip_table = ltj.int_get_skip_table
58
59 local function is_japanese_glyph_node(p)
60    return p and (p.id==id_glyph) 
61    and (p.font==has_attr(p,attr_curjfnt))
62 end
63
64 local function get_zero_glue()
65    local g = node_new(id_glue_spec)
66    g.width = 0; g.stretch_order = 0; g.stretch = 0
67    g.shrink_order = 0; g.shrink = 0
68    return g
69 end
70
71 local function skip_table_to_spec(n)
72    local g = node_new(id_glue_spec)
73    local st = cstb_get_skip_table(n, ltj.box_stack_level)
74    g.width = st.width; g.stretch = st.stretch; g.shrink = st.shrink
75    g.stretch_order = st.stretch_order; g.shrink_order = st.shrink_order
76    return g
77 end
78
79 local function add_glue_spec(g,h)
80    -- g := g + h
81    g.width = g.width + h.width
82    if g.stretch_order<h.stretch_order then
83       g.stretch_order = h.stretch_order
84       g.stretch = h.stretch
85    elseif g.stretch_order==h.stretch_order then
86       g.stretch = g.stretch + h.stretch
87    end
88    if g.shrink_order<h.shrink_order then
89       g.shrink_order = h.shrink_order
90       g.shrink = h.shrink
91    elseif g.shrink_order==h.shrink_order then
92       g.shrink = g.shrink + h.shrink
93    end
94 end
95
96 -- lowest part of \xkanjiskip 
97 local function get_xkanji_skip_from_jfm(pf)
98    if pf then
99       local px = { ltj.font_metric_table[pf].size, 
100                    ltj.font_metric_table[pf].jfm }
101       local i = ltj.metrics[px[2]].xkanjiskip
102       if i then
103          return { round(i[1]*px[1]), round(i[2]*px[1]), round(i[3]*px[1]) }
104       else return nil
105       end
106    else return nil
107    end
108 end
109
110 local function insert_xkanjiskip_node(q, f, p)
111    if nr_spc[2] or np_spc[2] then
112       local g = node_new(id_glue); g.subtype = 0
113       if xkanji_skip.width==max_dimen then -- use xkanjiskip from JFM
114          local gx = node_new(id_glue_spec)
115          gx.stretch_order = 0; gx.shrink_order = 0
116          local ak = get_xkanji_skip_from_jfm(f)
117          if ak then
118             gx.width = ak[1]; gx.stretch = ak[2]; gx.shrink = ak[3]
119          else gx = get_zero_glue() -- fallback
120          end
121          g.spec = gx
122       else g.spec=node_copy(xkanji_skip)
123       end
124       local h = node_prev(p)
125       if h  and has_attr(h, attr_icflag)==TEMPORARY then
126          if h.id==id_kern then
127             g.spec.width = g.spec.width + h.kern
128             set_attr(g,attr_icflag,XKANJI_SKIP)
129             node_insert_after(head, q, g)
130             head = node.remove(head, h)
131          else
132             add_glue_spec(h.spec, g.spec)
133          end
134       else
135          set_attr(g,attr_icflag,XKANJI_SKIP)
136          node_insert_after(head, q, g)
137       end
138    end
139 end
140
141 local function insert_ascii_kanji_xkskip(q, p)
142    if cstb_get_penalty_table('xsp', p.char, 3, ltj.box_stack_level)<=1 then return end
143    insert_xkanjiskip_node(q, p.font, p)
144 end
145
146 local function insert_kanji_ascii_xkskip(q, p)
147    local g = true
148    local c = p.char
149    while p.components and p.subtype 
150       and math.floor(p.subtype/2)%2==1 do
151       p = p.components; c = p.char
152    end
153    if cstb_get_penalty_table('xsp', c, 3, ltj.box_stack_level)%2 == 1 then
154       if cstb_get_penalty_table('xsp', nrc, 3, ltj.box_stack_level)%2 == 0 then g = false end
155    else g = false
156    end
157    if g then insert_xkanjiskip_node(q, nrf, p) end
158 end
159
160 local function set_insert_skip_after_achar(p)
161    local c = p.char
162    while p.components and p.subtype 
163       and math.floor(p.subtype/2)%2 == 1 do
164       p=node.tail(p.components); c = p.char
165    end
166   if cstb_get_penalty_table('xsp', c, 3, ltj.box_stack_level)>=2 then
167      insert_skip = after_schar
168   else
169      insert_skip = no_skip
170   end
171 end
172
173 -- lowest part of \kanjiskip 
174 local function get_kanji_skip_from_jfm(pf)
175    if pf then
176       local px = { ltj.font_metric_table[pf].size, 
177                    ltj.font_metric_table[pf].jfm }
178       local i = ltj.metrics[px[2]].kanjiskip
179       if i then
180          return { round(i[1]*px[1]), round(i[2]*px[1]), round(i[3]*px[1]) }
181       else return nil
182       end
183    else return nil
184    end
185 end
186
187 local function insert_kanji_skip(ope, p)
188    local g = node_new(id_glue); g.subtype=0
189    if nr_spc[1] or np_spc[1] then
190       if kanji_skip.width==max_dimen then -- use kanjiskip from JFM
191          local gx = node_new(id_glue_spec);
192          gx.stretch_order = 0; gx.shrink_order = 0
193          local bk = get_kanji_skip_from_jfm(nrf)
194          local ak = get_kanji_skip_from_jfm(p.font)
195          if bk then
196             if ak then
197                gx.width = round(ltj.ja_diffmet_rule(bk[1], ak[1]))
198                gx.stretch = round(ltj.ja_diffmet_rule(bk[2], ak[2]))
199                gx.shrink = -round(ltj.ja_diffmet_rule(-bk[3], -ak[3]))
200             else
201                gx.width = bk[1]; gx.stretch = bk[2]; gx.shrink = bk[3]
202             end
203          elseif ak then
204             gx.width = ak[1]; gx.stretch = ak[2]; gx.shrink = ak[3]
205          else gx = get_zero_glue() -- fallback
206          end
207          g.spec = gx
208       else g.spec=node_copy(kanji_skip)
209       end
210    else g.spec = get_zero_glue()
211    end
212    local h = node_prev(p)
213    if h  and has_attr(h, attr_icflag)==TEMPORARY then
214       if h.id==id_kern then
215          g.spec.width = g.spec.width + h.kern
216          head = node.remove(head, h)
217          set_attr(g,attr_icflag,KANJI_SKIP)
218          ope(head, p, g)
219       else
220          add_glue_spec(h.spec, g.spec)
221       end
222    else
223       set_attr(g,attr_icflag,KANJI_SKIP)
224       ope(head, p, g)
225    end
226 end
227
228 -- When p is a glyph_node ...
229 local function insks_around_char()
230    if is_japanese_glyph_node(np) then
231       if insert_skip==after_wchar then
232          insert_kanji_skip(node_insert_before, np)
233       elseif insert_skip==after_schar then
234          insert_ascii_kanji_xkskip(nq, np)
235       end
236       insert_skip=after_wchar
237       nrc = np.char; nrf = np.font; nr_spc = np_spc
238    else
239       if insert_skip==after_wchar then
240          insert_kanji_ascii_xkskip(nq, np)
241       end
242       set_insert_skip_after_achar(np); nr_spc = np_spc
243    end
244    nq = np
245 end
246
247 -- Return first and last glyph nodes in a hbox
248 local first_char = nil
249 local last_char = nil
250 local find_first_char = nil
251 local function check_box(box_ptr)
252    local p = box_ptr; local found_visible_node = false
253    if not p then 
254       find_first_char = false; first_char = nil; last_char = nil
255       return true
256    end
257    while p do
258       if p.id==id_glyph then
259          repeat 
260             if find_first_char then
261                first_char = p; find_first_char = false
262             end
263             last_char = p; found_visible_node = true; p=node_next(p)
264             if not p then return found_visible_node end
265          until p.id~=id_glyph
266       end
267       if p.id==id_hlist then
268          if has_attr(p, attr_icflag)==PACKED then
269             for q in node.traverse_id(id_glyph, p.head) do
270                if find_first_char then
271                   first_char = q; find_first_char = false
272                end
273                last_char = q; found_visible_node = true
274             end
275          else
276             if p.shift==0 then
277                if check_box(p.head) then found_visible_node = true end
278             else if find_first_char then 
279                   find_first_char = false
280                else 
281                   last_char = nil
282                end
283             end
284          end
285       elseif p.id==id_ins    or p.id==id_mark
286           or p.id==id_adjust or p.id==id_whatsit
287           or p.id==id_penalty then
288          p=p
289       else
290          found_visible_node = true
291          if find_first_char then 
292             find_first_char = false
293          else 
294             last_char = nil
295          end
296       end
297       p = node_next(p)
298    end
299    return found_visible_node
300 end 
301
302 -- When np is a hlist_node ...
303 local function insks_around_hbox()
304    if np.shift==0 then
305       find_first_char = true; first_char = nil; last_char = nil
306       if check_box(np.head) then
307          -- first char
308          if is_japanese_glyph_node(first_char) then
309             nrc = first_char.char; nrf = first_char.font
310             if insert_skip==after_schar then 
311                insert_ascii_kanji_xkskip(nq, first_char)
312             elseif insert_skip==after_wchar then
313                np_spc = { has_attr(first_char, attr_autospc)==1, 
314                           has_attr(first_char, attr_autoxspc)==1 }
315                insert_kanji_skip(node_insert_before, np)
316             end
317             insert_skip = after_wchar
318          elseif first_char then
319             if insert_skip==after_wchar then
320                insert_kanji_ascii_xkskip(nq, first_char)
321             end
322             set_insert_skip_after_achar(first_char)
323          end
324          -- last char
325          if is_japanese_glyph_node(last_char) then
326             insert_skip = after_wchar
327             nrc = last_char.char; nrf = last_char.font
328             nr_spc = { has_attr(last_char, attr_autospc)==1, 
329                        has_attr(last_char, attr_autoxspc)==1 }
330             if is_japanese_glyph_node(node_next(np)) then
331                insert_kanji_skip(node_insert_after, np)
332             end
333          elseif last_char then
334             set_insert_skip_after_achar(last_char)
335             nr_spc = { has_attr(last_char, attr_autospc)==1, 
336                        has_attr(last_char, attr_autoxspc)==1 }
337          else insert_skip = no_skip
338          end
339       else insert_skip = no_skip
340       end
341    else insert_skip = no_skip
342    end
343    nq = np
344 end
345
346 -- When np is a penalty ...
347 local function insks_around_penalty()
348    nq = np
349 end
350
351 -- When np is a kern ...
352 -- 
353 local function insks_around_kern()
354    if np.subtype==1 then -- \kern or \/
355       local i = has_attr(np, attr_icflag)
356       if not i or i==FROM_JFM then -- \kern
357          insert_skip = no_skip
358       elseif i==ITALIC or i==LINE_END or i==TEMPORARY then
359          nq = np
360       end
361    elseif np.subtype==2 then 
362       -- (np = kern from \accent) .. (accent char) .. (kern from \accent) .. (glyph)
363       np = node_next(node_next(np))
364    else  -- kern from TFM
365       nq = np
366    end
367 end
368
369 -- When np is a math_node ...
370 local function insks_around_math()
371    local g = { char = -1 }
372    if (np.subtype==0) and (insert_skip==after_wchar) then
373       insert_kanji_ascii_xkskip(nq, g)
374       insert_skip = no_skip
375    else
376       nq = np; set_insert_skip_after_achar(g); nr_spc = np_spc
377    end
378 end
379
380 function ltj.int_insert_kanji_skip(ahead)
381    kanji_skip=skip_table_to_spec('kanjiskip')
382    xkanji_skip=skip_table_to_spec('xkanjiskip')
383    head = ahead
384    np = head; nq = nil; insert_skip = no_skip
385    while np do
386       np_spc = { (has_attr(np, attr_autospc)==1), 
387                  (has_attr(np, attr_autoxspc)==1) }
388       if np.id==id_glyph then
389          repeat 
390             np_spc = { has_attr(np, attr_autospc)==1, 
391                        has_attr(np, attr_autoxspc)==1 }
392             insks_around_char(); np=node_next(np)
393          until (not np) or np.id~=id_glyph
394       else
395          if np.id==id_hlist then
396             insks_around_hbox()
397          elseif np.id==id_penalty then
398             insks_around_penalty()
399          elseif np.id==id_kern then
400             insks_around_kern()
401          elseif np.id==id_math then
402             insks_around_math()
403          elseif np.id==id_ins    or np.id==id_mark
404              or np.id==id_adjust or np.id==id_whatsit then
405             -- do nothing
406             np = np
407          else
408             -- rule, disc, glue, margin_kern
409             insert_skip = no_skip
410          end
411          np = node_next(np)
412       end
413    end
414    return head
415 end