OSDN Git Service

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