OSDN Git Service

0c9abe3516880565d12cf3cdff7e1d4b0ccb26c4
[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
40 local kanji_skip
41 local xkanji_skip
42
43 -- (glyph_node nr) ... (node nq) <GLUE> ,,, (node np)
44 local np, nq, nrc
45 local no_skip = 0 
46 local after_schar = 1
47 local after_wchar = 2 -- nr is a Japanese glyph_node
48 local insert_skip = no_skip
49 local head
50
51 local cstb_get_inhibit_xsp_table = ltj.int_get_inhibit_xsp_table
52
53 local function is_japanese_glyph_node(p)
54    return p and (p.id==id_glyph) 
55    and (p.font==has_attr(p,attr_curjfnt))
56 end
57
58 local function get_zero_glue()
59    local g = node_new(id_glue_spec)
60    g.width = 0; g.stretch_order = 0; g.stretch = 0
61    g.shrink_order = 0; g.shrink = 0
62    return g
63 end
64
65 -- the following 2 functions are the lowest part.
66 -- cx: the Kanji code of np
67 local function insert_ascii_kanji_xkskip(q, cx)
68    if cstb_get_inhibit_xsp_table(cx)<=1 then return end
69    local g = node_new(id_glue)
70    g.subtype = 0; g.spec = node.copy(xkanji_skip)
71    node_insert_after(head, q, g)
72 end
73
74 local function insert_kanji_ascii_xkskip(q, p)
75    local g=true
76    local c = p.char
77    while p.components and p.subtype 
78       and math.floor(p.subtype/2)%2==1 do
79       p = p.components; c = p.char
80    end
81    if cstb_get_inhibit_xsp_table(c)%2 == 1 then
82       if cstb_get_inhibit_xsp_table(nrc)%2 == 0 then g = false end
83    else g = false
84    end
85    if g then
86       g = node_new(id_glue)
87       g.subtype = 0; g.spec = node.copy(xkanji_skip)
88       node_insert_after(head, q, g)
89    end
90 end
91
92
93 local function set_insert_skip_after_achar(p)
94    local c = p.char
95    while p.components and p.subtype 
96       and math.floor(p.subtype/2)%2 == 1 do
97       p=node.tail(p.components); c = p.char
98    end
99   if cstb_get_inhibit_xsp_table(c)>=2 then
100      insert_skip = after_schar
101   else
102      insert_skip = no_skip
103   end
104 end
105
106 local function get_kanji_skip_from_jfm(p)
107    local px = { ltj.font_metric_table[p.font].size, 
108                 ltj.font_metric_table[p.font].jfm }
109    local i = ltj.metrics[px[2]].kanjiskip
110    print(p.font, px[1], px[2], ltj.metrics[px[2]].dir, i)
111    if i then
112       return { round(i[1]*px[1]), round(i[2]*px[1]), round(i[3]*px[1]) }
113    else return nil
114    end
115 end
116
117 local function insert_kanji_skip()
118    print('a', utf.char(np.char))
119    local g = node_new(id_glue); g.subtype=0
120    if kanji_skip.width==max_dimen then -- use kanjiskip from JFM
121       local gx = node_new(id_glue_spec);
122       gx.stretch_order = 0; gx.shrink_order = 0
123       local bk = nil -- incomplete
124       local ak = get_kanji_skip_from_jfm(np)
125       if bk then
126          if ak then
127             gx.width = round(ltj.ja_diffmet_rule(bk[1], ak[1]))
128             gx.stretch = round(ltj.ja_diffmet_rule(bk[2], ak[2]))
129             gx.shrink = -round(ltj.ja_diffmet_rule(-bk[3], -ak[3]))
130          else
131             gx.width = bk[1]; gx.stretch = bk[2]; gx.shrink = bk[3]
132          end
133       elseif ak then
134          gx.width = ak[1]; gx.stretch = ak[2]; gx.shrink = ak[3]
135       else
136          gx = get_zero_glue()-- ???
137       end
138       g.spec = gx
139    else
140       g.spec=node.copy(kanji_skip)
141    end
142    local h = node_prev(np)
143    if h  and has_attr(h, attr_icflag)==TEMPORARY then
144       if h.id==id_kern then
145          g.spec.width = g.spec.width + h.kern
146          head = node.remove(head, h)
147          node_insert_before(head, np, g)
148       else
149          h.spec.width = g.spec.width + h.spec.width
150          h.spec.stretch = g.spec.stretch + h.spec.stretch
151          h.spec.shrink = g.spec.shrink + h.spec.shrink
152       end
153    else
154       node_insert_before(head, np, g)
155    end
156    print('b', utf.char(np.char))
157 end
158
159 -- When p is a glyph_node ...
160 local function insks_around_char()
161    if is_japanese_glyph_node(np) then
162       if insert_skip==after_wchar then
163          insert_kanji_skip()
164       elseif insert_skip==after_schar then
165          insert_ascii_kanji_xkskip(nq, np.char)
166       end
167       insert_skip=after_wchar; nrc = np.char
168    else
169       if insert_skip==after_wchar then
170          insert_kanji_ascii_xkskip(nq, np)
171       end
172       set_insert_skip_after_achar(np)
173    end
174    nq = np
175 end
176
177 -- Return first and last glyph nodes in a hbox
178 local first_char = nil
179 local last_char = nil
180 local find_first_char = nil
181 local function check_box(box_ptr)
182    local p = box_ptr; local found_visible_node = false
183    while p do
184       if p.id==id_glyph then
185          repeat 
186             if find_first_char then
187                first_char = p; find_first_char = false
188             end
189             last_char = p; found_visible_node = true; p=node_next(p)
190             if not p then return found_visible_node end
191          until p.id~=id_glyph
192       end
193       if p.id==id_hlist then
194          found_visible_node = true
195          if p.shift==0 then
196             if check_box(p.head) then found_visible_node = true end
197          else if find_first_char then 
198                find_first_char = false
199             else 
200                last_char = nil
201             end
202          end
203       elseif p.id==id_ins    or p.id==id_mark
204           or p.id==id_adjust or p.id==id_whatsit
205           or p.id==id_penalty then
206          p=p
207       else
208          found_visible_node = true
209          if find_first_char then 
210             find_first_char = false
211          else 
212             last_char = nil
213          end
214       end
215       p = node_next(p)
216    end
217    return found_visible_node
218 end 
219
220 -- When np is a hlist_node ...
221 local function insks_around_hbox()
222    if np.shift==0 then
223       find_first_char = true; first_char = nil; last_char = nil
224       if check_box(np.head) then
225          -- first char
226          if is_japanese_glyph_node(first_char) then
227             nrc = first_char.char
228             if insert_skip==after_schar then 
229                insert_ascii_kanji_xkskip(nq, first_char.char)
230             elseif insert_skip==after_wchar then
231                insert_kanji_skip()
232             end
233             insert_skip = after_wchar
234          elseif first_char then
235             if insert_skip==after_wchar then
236                insert_kanji_ascii_xkskip(nq, first_char)
237             end
238             set_insert_skip_after_achar(first_char)
239          end
240          -- last char
241          if is_japanese_glyph_node(last_char) then
242             if is_japanese_glyph_node(node_next(np)) then
243                local g = node_new(id_glue)
244                g.subtype = 0; g.spec = node.copy(kanji_skip)
245                node_insert_after(head, np, g)
246             end
247             insert_skip = after_wchar; nrc = last_char.char
248          elseif last_char then
249             set_insert_skip_after_achar(last_char)
250          else insert_skip = no_skip
251          end
252       else insert_skip = no_skip
253       end
254    else insert_skip = no_skip
255    end
256    nq = np
257 end
258
259 -- When np is a penalty ...
260 local function insks_around_penalty()
261    nq = np
262 end
263
264 -- When np is a kern ...
265 -- 
266 local function insks_around_kern()
267    if np.subtype==1 then -- \kern or \/
268       local i = has_attr(np, attr_icflag)
269       if not i or i==FROM_JFM then -- \kern
270          insert_skip = no_skip
271       elseif i==ITALIC or i==LINE_END or i==TEMPORARY then
272          nq = np
273       end
274    elseif np.subtype==2 then 
275       -- (np = kern from \accent) .. (accent char) .. (kern from \accent) .. (glyph)
276       np = node_next(node_next(np))
277    end
278 end
279
280 -- When np is a math_node ...
281 local function insks_around_math()
282    local g = { char = -1 }
283    if (np.subtype==0) and (insert_skip==after_wchar) then
284       insert_kanji_ascii_xkskip(nq, g)
285       insert_skip = no_skip
286    else
287       nq = np; set_insert_skip_after_achar(g)
288    end
289 end
290
291 function ltj.int_insert_kanji_skip(ahead)
292    kanji_skip=tex.skip['kanjiskip']
293    xkanji_skip=tex.skip['xkanjiskip']
294    head = ahead
295    np = head; nq = nil; insert_skip = no_skip
296    while np do
297       if np.id==id_glyph then
298          repeat 
299             insks_around_char(); np=node_next(np)
300          until (not np) or np.id~=id_glyph
301       else
302          if np.id==id_hlist then
303             insks_around_hbox()
304          elseif np.id==id_penalty then
305             insks_around_penalty()
306          elseif np.id==id_kern then
307             insks_around_kern()
308          elseif np.id==id_math then
309             insks_around_math()
310          elseif np.id==id_ins    or np.id==id_mark
311              or np.id==id_adjust or np.id==id_whatsit then
312             -- do nothing
313             np = np
314          else
315             -- rule, disc, glue, margin_kern
316             insert_skip = no_skip
317          end
318          np = node_next(np)
319       end
320    end
321    return head
322 end