OSDN Git Service

renamed cache file: ivs_*.lua -> extra_*.lua
[luatex-ja/luatexja.git] / src / ltj-jfont.lua
1 --
2 -- luatexja/jfont.lua
3 --
4 luatexbase.provides_module({
5   name = 'luatexja.jfont',
6   date = '2014/10/03',
7   description = 'Loader for Japanese fonts',
8 })
9 module('luatexja.jfont', package.seeall)
10
11 luatexja.load_module('base');      local ltjb = luatexja.base
12 luatexja.load_module('charrange'); local ltjc = luatexja.charrange
13 luatexja.load_module('rmlgbm');    local ltjr = luatexja.rmlgbm
14 luatexja.load_module('direction'); local ltjd = luatexja.direction
15
16
17 local Dnode = node.direct or node
18
19 local setfield = (Dnode ~= node) and Dnode.setfield or function(n, i, c) n[i] = c end
20 local getid = (Dnode ~= node) and Dnode.getid or function(n) return n.id end
21 local getfont = (Dnode ~= node) and Dnode.getfont or function(n) return n.font end
22 local getchar = (Dnode ~= node) and Dnode.getchar or function(n) return n.char end
23
24 local nullfunc = function(n) return n end
25 local to_direct = (Dnode ~= node) and Dnode.todirect or nullfunc
26
27 local node_new = Dnode.new
28 local node_free = Dnode.free
29 local has_attr = Dnode.has_attribute
30 local set_attr = Dnode.set_attribute
31 local node_write = Dnode.write
32 local round = tex.round
33 local font_getfont = font.getfont
34
35 local attr_icflag = luatexbase.attributes['ltj@icflag']
36 local attr_curjfnt = luatexbase.attributes['ltj@curjfnt']
37 local attr_curtfnt = luatexbase.attributes['ltj@curtfnt']
38 local id_glyph = node.id('glyph')
39 local id_kern = node.id('kern')
40 local id_glue_spec = node.id('glue_spec')
41 local id_glue = node.id('glue')
42 local cat_lp = luatexbase.catcodetables['latex-package']
43 local ITALIC       = luatexja.icflag_table.ITALIC
44 local FROM_JFM     = luatexja.icflag_table.FROM_JFM
45
46 ------------------------------------------------------------------------
47 -- LOADING JFM
48 ------------------------------------------------------------------------
49
50 metrics={} -- this table stores all metric informations
51 font_metric_table={} -- [font number] -> jfm_name, jfm_var, size
52
53 luatexbase.create_callback("luatexja.load_jfm", "data", function (ft, jn) return ft end)
54
55 local jfm_file_name, jfm_var
56 local defjfm_res
57 local jfm_dir
58
59 function define_jfm(t)
60    local real_char -- Does current character class have the 'real' character?
61    if t.dir~=jfm_dir then
62       defjfm_res= nil; return
63    elseif type(t.zw)~='number' or type(t.zh)~='number' then
64       defjfm_res= nil; return
65    end
66    t.char_type = {}; t.chars = {}
67    for i,v in pairs(t) do
68       if type(i) == 'number' then -- char_type
69          if not v.chars then
70             if i ~= 0 then defjfm_res= nil; return  end
71             real_char = true
72          else
73             real_char = false
74             for j,w in pairs(v.chars) do
75                if type(w) == 'number' and w~=-1 then
76                   real_char = true;
77                elseif type(w) == 'string' and utf.len(w)==1 then
78                   real_char = true; w = utf.byte(w)
79                elseif type(w) == 'string' and utf.len(w)==2 and utf.sub(w,2) == '*' then
80                   real_char = true; w = utf.byte(utf.sub(w,1,1))
81                   if not t.chars[-w] then
82                      t.chars[-w] = i
83                   else
84                      defjfm_res= nil; return
85                   end
86                end
87                if not t.chars[w] then
88                   t.chars[w] = i
89                else
90                   defjfm_res= nil; return
91                end
92             end
93             if type(v.align)~='string' then
94                v.align = 'left' -- left
95             end
96             if real_char then
97                if type(v.width)~='number' and v.width~='prop' then
98                   defjfm_res= nil; return
99                else
100                   if v.width=='prop' and jfm_dir=='tate' then
101                      v.width = 1.0
102                   end
103                   if type(v.height)~='number' then
104                      v.height = 0.0
105                   end
106                   if type(v.depth)~='number' then
107                      v.depth = 0.0
108                   end
109                   if type(v.italic)~='number' then
110                      v.italic = 0.0
111                   end
112                   if type(v.left)~='number' then
113                      v.left = 0.0
114                   end
115                   if type(v.down)~='number' then
116                      v.down = 0.0
117                   end
118                end
119             end
120             v.chars = nil
121          end
122          v.kern = v.kern or {}; v.glue = v.glue or {}
123          for j in pairs(v.glue) do
124             if v.kern[j] then defjfm_res= nil; return end
125          end
126          for j,x in pairs(v.kern) do
127             if type(x)=='number' then
128                v.kern[j] = {x, 0}
129             elseif type(x)=='table' then
130                v.kern[j] = {x[1], x[2] or 0}
131             end
132          end
133          t.char_type[i] = v
134          t[i] = nil
135       end
136    end
137    t = luatexbase.call_callback("luatexja.load_jfm", t, jfm_file_name)
138    t.size_cache = {}
139    defjfm_res = t
140 end
141
142 local update_jfm_cache
143 do
144    local function mult_table(old,scale) -- modified from table.fastcopy
145       if old then
146          local new = { }
147          for k,v in next, old do
148             if type(v) == "table" then
149                new[k] = mult_table(v,scale)
150             elseif type(v) == "number" then
151                new[k] = round(v*scale)
152             else
153                new[k] = v
154             end
155          end
156          return new
157       else return nil end
158    end
159
160    update_jfm_cache = function (j,sz)
161       if metrics[j].size_cache[sz] then return end
162       local t = {}
163       metrics[j].size_cache[sz] = t
164       t.chars = metrics[j].chars
165       t.char_type = mult_table(metrics[j].char_type, sz)
166       for i,v in pairs(t.char_type) do
167          v.align = (v.align=='left') and 0 or
168             ((v.align=='right') and 1 or 0.5)
169          if type(i) == 'number' then -- char_type
170             for k,w in pairs(v.glue) do
171                local h = node_new(id_glue_spec)
172                v[k] = {true, h, (w[5] and w[5]/sz or 0), FROM_JFM + (w[4] and w[4]/sz or 0)}
173                setfield(h, 'width', w[1])
174                setfield(h, 'stretch', w[2])
175                setfield(h, 'shrink', w[3])
176                setfield(h, 'stretch_order', 0)
177                setfield(h, 'shrink_order', 0)
178             end
179             for k,w in pairs(v.kern) do
180                local g = node_new(id_kern)
181                setfield(g, 'kern', w[1])
182                setfield(g, 'subtype', 1)
183                set_attr(g, attr_icflag, FROM_JFM)
184                v[k] = {false, g, w[2]/sz}
185             end
186          end
187          v.glue, v.kern = nil, nil
188       end
189       t.kanjiskip = mult_table(metrics[j].kanjiskip, sz)
190       t.xkanjiskip = mult_table(metrics[j].xkanjiskip,sz)
191       t.zw = round(metrics[j].zw*sz)
192       t.zh = round(metrics[j].zh*sz)
193    end
194 end
195
196 luatexbase.create_callback("luatexja.find_char_class", "data",
197                            function (arg, fmtable, char)
198                               return 0
199                            end)
200 do
201    local start_time_measure = ltjb.start_time_measure
202    local stop_time_measure = ltjb.stop_time_measure
203    local fcc_temp = { chars_cbcache = {} }
204    setmetatable(
205       fcc_temp.chars_cbcache,
206       {
207          __index = function () return 0 end,
208       })
209    function find_char_class(c,m)
210       -- c: character code, m:
211       local r = (m or fcc_temp).chars_cbcache[c]
212       if not r then
213          r = m.chars[c] or
214             luatexbase.call_callback("luatexja.find_char_class", 0, m, c)
215          m.chars_cbcache[c or 0] = r
216       end
217       return r
218    end
219 end
220
221
222 ------------------------------------------------------------------------
223 -- LOADING JAPANESE FONTS
224 ------------------------------------------------------------------------
225
226 do
227    local cstemp
228    local global_flag -- true if \globaljfont, false if \jfont
229    local function load_jfont_metric()
230       if jfm_file_name=='' then
231          ltjb.package_error('luatexja',
232                             'no JFM specified',
233                             'To load and define a Japanese font, a JFM must be specified.'..
234                             "The JFM 'ujis' will be  used for now.")
235          jfm_file_name='ujis'
236       end
237       for j,v in ipairs(metrics) do
238          if v.name==jfm_file_name then return j end
239       end
240       luatexja.load_lua('jfm-' .. jfm_file_name .. '.lua')
241       if defjfm_res then
242          defjfm_res.name = jfm_file_name
243          table.insert(metrics, defjfm_res)
244          return #metrics
245       else
246          return nil
247       end
248    end
249
250 -- EXT
251    function jfontdefX(g)
252       local t = token.get_next()
253       cstemp=token.csname_name(t)
254       global_flag = g and '\\global' or ''
255       tex.sprint(cat_lp, '\\expandafter\\font\\csname ', cstemp, '\\endcsname')
256    end
257
258    luatexbase.create_callback("luatexja.define_jfont", "data", function (ft, fn) return ft end)
259
260 -- EXT
261    local identifiers = fonts.hashes.identifiers
262    function jfontdefY(dir)
263       jfm_dir = dir
264       local j = load_jfont_metric(dir)
265       local fn = font.id(cstemp)
266       local f = font_getfont(fn)
267       if not j then
268          ltjb.package_error('luatexja',
269                             "bad JFM `" .. jfm_file_name .. "'",
270                             'The JFM file you specified is not valid JFM file.\n'..
271                                'So defining Japanese font is cancelled.')
272          tex.sprint(cat_lp, global_flag, '\\expandafter\\let\\csname ', cstemp,
273                        '\\endcsname=\\relax')
274          return
275       end
276       update_jfm_cache(j, f.size)
277       local ad = identifiers[fn].parameters
278       local sz = metrics[j].size_cache[f.size]
279       local fmtable = { jfm = j, size = f.size, var = jfm_var,
280                         zw = sz.zw, zh = sz.zh,
281                         ascent = ad.ascender,
282                         descent = ad.descender,
283                         chars = sz.chars, char_type = sz.char_type,
284                         kanjiskip = sz.kanjiskip, xkanjiskip = sz.xkanjiskip,
285                         chars_cbcache = {},
286       }
287
288       fmtable = luatexbase.call_callback("luatexja.define_jfont", fmtable, fn)
289       font_metric_table[fn]=fmtable
290       tex.sprint(cat_lp, global_flag, '\\protected\\expandafter\\def\\csname ',
291                     cstemp , '\\endcsname{\\ltj@cur'..
292                     (dir == 'yoko' and 'j' or 't') .. 'fnt', fn, '\\relax}')
293    end
294 end
295
296 do
297    local get_dir_count = ltjd.get_dir_count
298    local dir_tate = luatexja.dir_table.dir_tate
299    local tex_get_attr = tex.getattribute
300    -- PUBLIC function
301    function get_zw()
302       local a = font_metric_table[
303          tex_get_attr((get_dir_count()==dir_tate) and attr_curtfnt or attr_curjfnt)]
304       return a and a.zw or 0
305    end
306    function get_zh()
307       local a = font_metric_table[
308          tex_get_attr((get_dir_count()==dir_tate) and attr_curtfnt or attr_curjfnt)]
309       return a and a.zw or 0
310    end
311 end
312
313 do
314    -- extract jfm_file_name and jfm_var
315    -- normalize position of 'jfm=' and 'jfmvar=' keys
316    local function extract_metric(name)
317       jfm_file_name = ''; jfm_var = ''
318       local tmp, index = name:sub(1, 5), 1
319       if tmp == 'file:' or tmp == 'name:' or tmp == 'psft:' then
320          index = 6
321       end
322       local p = name:find(":", index); index = p and (p+1) or index
323       while index do
324          local l = name:len()+1
325          local q = name:find(";", index+1) or l
326          if name:sub(index, index+3)=='jfm=' and q>index+4 then
327             jfm_file_name = name:sub(index+4, q-1)
328             if l~=q then
329                name = name:sub(1,index-1) .. name:sub(q+1)
330             else
331                name = name:sub(1,index-1)
332                index = nil
333             end
334          elseif name:sub(index, index+6)=='jfmvar=' and q>index+6 then
335             jfm_var = name:sub(index+7, q-1)
336             if l~=q then
337                name = name:sub(1,index-1) .. name:sub(q+1)
338             else
339                name = name:sub(1,index-1)
340                index = nil
341             end
342          else
343             index = (l~=q) and (q+1) or nil
344          end
345       end
346       if jfm_file_name~='' then
347          local l = name:sub(-1)
348          name = name
349             .. ((l==':' or l==';') and '' or ';')
350             .. 'jfm=' .. jfm_file_name
351          if jfm_var~='' then
352             name = name .. 'jfmvar=' .. jfm_var
353          end
354       end
355       return name
356    end
357
358    -- define_font callback
359    local otfl_fdr = fonts.definers.read
360    local ltjr_font_callback = ltjr.font_callback
361    function luatexja.font_callback(name, size, id)
362       local new_name = extract_metric(name)
363       local res =  ltjr_font_callback(new_name, size, id, otfl_fdr)
364       luatexbase.call_callback('luatexja.define_font', res, new_name, size, id)
365       return res
366    end
367    luatexbase.create_callback('luatexja.define_font', 'simple', function (n) return n end)
368    luatexbase.add_to_callback('define_font',luatexja.font_callback,"luatexja.font_callback", 1)
369 end
370
371 ------------------------------------------------------------------------
372 -- LATEX INTERFACE
373 ------------------------------------------------------------------------
374 do
375    -- these function are called from ltj-latex.sty
376    local kyenc_list, ktenc_list = {}, {}
377    function add_kyenc_list(enc) kyenc_list[enc] = 'true ' end
378    function add_ktenc_list(enc) ktenc_list[enc] = 'true ' end
379    function is_kyenc(enc)
380       tex.sprint(cat_lp, '\\let\\ifin@\\if' .. (kyenc_list[enc] or 'false '))
381    end
382    function is_ktenc(enc)
383       tex.sprint(cat_lp, '\\let\\ifin@\\if' .. (ktenc_list[enc] or 'false '))
384    end
385    function is_kenc(enc)
386       tex.sprint(cat_lp, '\\let\\ifin@\\if'
387                  .. (kyenc_list[enc] or ktenc_list[enc] or 'false '))
388    end
389
390    local kfam_list, Nkfam_list = {}, {}
391    function add_kfam_list(enc, fam)
392       if not kfam_list[enc] then kfam_list[enc] = {} end
393       kfam_list[enc][fam] = 'true '
394    end
395    function add_Nkfam_list(enc, fam)
396       if not Nkfam_list[enc] then Nkfam_list[enc] = {} end
397       Nkfam_list[enc][fam] = 'true '
398    end
399    function is_kfam(enc, fam)
400       tex.sprint(cat_lp, '\\let\\ifin@\\if'
401                  .. (kfam_list[enc] and kfam_list[enc][fam] or 'false ')) end
402    function is_Nkfam(enc, fam)
403       tex.sprint(cat_lp, '\\let\\ifin@\\if'
404                  .. (Nkfam_list[enc] and Nkfam_list[enc][fam] or 'false ')) end
405
406    local ffam_list, Nffam_list = {}, {}
407    function add_ffam_list(enc, fam)
408       if not ffam_list[enc] then ffam_list[enc] = {} end
409       ffam_list[enc][fam] = 'true '
410    end
411    function add_Nffam_list(enc, fam)
412       if not Nffam_list[enc] then Nffam_list[enc] = {} end
413       Nffam_list[enc][fam] = 'true '
414    end
415    function is_ffam(enc, fam)
416       tex.sprint(cat_lp, '\\let\\ifin@\\if'
417                  .. (ffam_list[enc] and ffam_list[enc][fam] or 'false ')) end
418    function is_Nffam(enc, fam)
419       tex.sprint(cat_lp, '\\let\\ifin@\\if'
420                  .. (Nffam_list[enc] and Nffam_list[enc][fam] or 'false ')) end
421 end
422 ------------------------------------------------------------------------
423 -- ALTERNATE FONTS
424 ------------------------------------------------------------------------
425 alt_font_table = {}
426 local alt_font_table = alt_font_table
427 local attr_curaltfnt = {}
428 local ucs_out = 0x110000
429
430 ------ for TeX interface
431 -- EXT
432 function set_alt_font(b,e,ind,bfnt)
433    -- ind: 新フォント, bfnt: 基底フォント
434    if b>e then b, e = e, b end
435    if b*e<=0 then
436       ltjb.package_error('luatexja',
437                         'bad character range ([' .. b .. ',' .. e .. ']). ' ..
438                            'I take the intersection with [0x80, 0x10ffff].')
439       b, e = math.max(0x80,b),math.min(ucs_out-1,e)
440    elseif e<0 then -- b<e<0
441       -- do nothing
442    elseif b<0x80 or e>=ucs_out then
443       ltjb.package_warning('luatexja',
444                            'bad character range ([' .. b .. ',' .. e .. ']). ' ..
445                               'I take the intersection with [0x80, 0x10ffff].')
446       b, e = math.max(0x80,b), math.min(ucs_out-1,e)
447    end
448    if not alt_font_table[bfnt] then alt_font_table[bfnt]={} end
449    local t = alt_font_table[bfnt]
450    local ac = font_getfont(ind).characters
451    if bfnt==ind then ind = nil end -- ind == bfnt の場合はテーブルから削除
452    if e>=0 then -- character range
453       for i=b, e do
454          if ac[i]then  t[i]=ind end
455       end
456    else
457       b, e = -e, -b
458       local tx = font_metric_table[bfnt].chars
459       for i,v in pairs(tx) do
460          if b<=v and v<=e and ac[i] then t[i]=ind end
461       end
462    end
463 end
464
465 -- EXT
466 function clear_alt_font(bfnt)
467    if alt_font_table[bfnt] then
468       local t = alt_font_table[bfnt]
469       for i,_ in pairs(t) do t[i]=nil; end
470    end
471 end
472
473 ------ used in ltjp.suppress_hyphenate_ja callback
474 function replace_altfont(pf, pc)
475    local a = alt_font_table[pf]
476    return a and a[pc] or pf
477 end
478
479 ------ for LaTeX interface
480
481 local alt_font_table_latex = {}
482
483 -- EXT
484 function clear_alt_font_latex(bbase)
485    local t = alt_font_table_latex[bbase]
486    if t then
487       for j,v in pairs(t) do t[j] = nil end
488    end
489 end
490
491 -- EXT
492 function set_alt_font_latex(b,e,ind,bbase)
493    -- ind: Alt font の enc/fam/ser/shape, bbase: 基底フォントの enc/fam/ser/shape
494    if b>e then b, e = e, b end
495    if b*e<=0 then
496       ltjb.package_error('luatexja',
497                         'bad character range ([' .. b .. ',' .. e .. ']). ' ..
498                            'I take the intersection with [0x80, 0x10ffff].')
499       b, e = math.max(0x80,b),math.min(ucs_out-1,e)
500    elseif e<0 then -- b<e<0
501       -- do nothing
502    elseif b<0x80 or e>=ucs_out then
503       ltjb.package_warning('luatexja',
504                            'bad character range ([' .. b .. ',' .. e .. ']). ' ..
505                               'I take the intersection with [0x80, 0x10ffff].')
506       b, e = math.max(0x80,b), math.min(ucs_out-1,e)
507    end
508
509    if not alt_font_table_latex[bbase] then alt_font_table_latex[bbase]={} end
510    local t = alt_font_table_latex[bbase]
511    if not t[ind] then t[ind] = {} end
512    for i=b, e do
513       for j,v in pairs(t) do
514          if v[i] then -- remove old entry
515             if j~=ind then v[i]=nil end; break
516          end
517       end
518       t[ind][i]=true
519    end
520    -- remove the empty tables
521    for j,v in pairs(t) do
522       local flag_clear = true
523       for k,_ in pairs(v) do flag_clear = false; break end
524       if flag_clear then t[j]=nil end
525    end
526    if ind==bbase  then t[bbase] = nil end
527 end
528
529 -- ここから先は 新 \selectfont の内部でしか実行されない
530 do
531    local alt_font_base, alt_font_base_num
532    local aftl_base
533    -- EXT
534    function does_alt_set(bbase)
535       aftl_base = alt_font_table_latex[bbase]
536       tex.sprint(cat_lp, '\\if' .. (aftl_base and 'true' or 'false'))
537    end
538    -- EXT
539    function print_aftl_address()
540       tex.sprint(cat_lp, ';ltjaltfont' .. tostring(aftl_base):sub(8))
541    end
542
543 -- EXT
544    function output_alt_font_cmd(dir, bbase)
545       alt_font_base = bbase
546       if dir == 't' then
547          alt_font_base_num = tex.getattribute(attr_curtfnt)
548       else
549          alt_font_base_num = tex.getattribute(attr_curjfnt)
550       end
551       local t = alt_font_table[alt_font_base_num]
552       if t then
553          for i,_ in pairs(t) do t[i]=nil end
554       end
555       t = alt_font_table_latex[bbase]
556       if t then
557        for i,_ in pairs(t) do
558             tex.sprint(cat_lp, '\\ltj@pickup@altfont@aux' .. dir .. '{' .. i .. '}')
559          end
560       end
561    end
562
563 -- EXT
564    function pickup_alt_font_a(size_str)
565       local t = alt_font_table_latex[alt_font_base]
566       if t then
567          for i,v in pairs(t) do
568             tex.sprint(cat_lp, '\\expandafter\\ltj@pickup@altfont@copy'
569                           .. '\\csname ' .. i .. '/' .. size_str .. '\\endcsname{' .. i .. '}')
570          end
571       end
572    end
573
574    local function pickup_alt_font_class(class, afnt_num, afnt_chars)
575       local t  = alt_font_table[alt_font_base_num]
576       local tx = font_metric_table[alt_font_base_num].chars
577       for i,v in pairs(tx) do
578          if v==class and afnt_chars[i] then t[i]=afnt_num end
579       end
580    end
581
582 -- EXT
583    function pickup_alt_font_b(afnt_num, afnt_base)
584       local t = alt_font_table[alt_font_base_num]
585       local ac = font_getfont(afnt_num).characters
586       if not t then t = {}; alt_font_table[alt_font_base_num] = t end
587       for i,v in pairs(alt_font_table_latex[alt_font_base]) do
588          if i == afnt_base then
589             for j,_ in pairs(v) do
590                if j>=0 then
591                   if ac[j] then t[j]=afnt_num end
592                else  -- -n (n>=1) means that the character class n,
593                      -- which is defined in the JFM
594                   pickup_alt_font_class(-j, afnt_num, ac)
595                end
596             end
597             return
598          end
599       end
600    end
601
602 end
603
604 ------------------------------------------------------------------------
605 -- 追加のフォント情報
606 ------------------------------------------------------------------------
607 font_extra_info = {}
608 local font_extra_info = font_extra_info -- key: fontnumber
609
610 -- IVS and vertical metrics
611 local prepare_fl_data
612 do
613    local ivs -- temp table
614    local sort = table.sort
615    local function add_fl_table(dest, tg, unitable, glyphmax)
616       for i = 0, glyphmax-1 do
617          if tg[i] then
618             local gv = tg[i]
619             if gv.altuni then
620                for _,at in pairs(gv.altuni) do
621                   local bu, vsel = at.unicode, at.variant
622                   if vsel then
623                      if vsel>=0xE0100 then vsel = vsel - 0xE0100 end
624                      dest = dest or {}; dest[bu] = dest[bu] or {}
625                      local uniq_flag = true
626                      for i,_ in pairs(dest[bu]) do
627                         if i==vs then uniq_flag = false; break end
628                      end
629                      if uniq_flag then
630                         dest[bu][vsel] = unitable[gv.name]
631                      end
632                   end
633                end
634             end
635          end
636       end
637       return dest
638    end
639    prepare_fl_data = function (dest, id, fname)
640       local fl = fontloader.open(fname)
641       local unicodes = id.resources.unicodes
642       if fl.glyphs then
643          dest = add_fl_table(dest, fl.glyphs, id.resources.unicodes, fl.glyphmax)
644       end
645       if fl.subfonts then
646          for _,v in pairs(fl.subfonts) do
647             dest = add_fl_table(dest, v.glyphs, id.resources.unicodes, v.glyphmax)
648          end
649       end
650       fontloader.close(fl)
651       return dest
652    end
653 end
654
655 -- 縦書き用字形への変換テーブル
656 local prepare_vert_data
657 do
658    local function add_feature_table(tname, src, dest)
659       for i,v in pairs(src) do
660          if type(v.slookups)=='table' then
661             local s = v.slookups[tname]
662             if s  then
663                if not dest then dest = {} end
664                if not dest[i] then dest[i] = {} end
665                dest[i].vert = dest[i].vert or s
666             end
667          end
668       end
669       return dest
670    end
671    prepare_vert_data = function (dest, id) 
672       local a = id.resources.sequences
673       if a then
674          local s = id.shared.rawdata.descriptions
675          for i,v in pairs(a) do
676             if v.features.vert or v.features.vrt2 then
677                dest= add_feature_table(v.subtables[1], s, dest)
678             end
679          end
680       end
681       return dest
682    end
683    -- 縦書き用字形への変換
684    function get_vert_glyph(n, chr)
685       local fn = font_extra_info[n]
686       return (fn and fn[chr] and fn[chr].vert) or chr
687    end
688 end
689
690 -- 
691 do
692    local font_extra_basename = {} -- key: basename
693    local cache_ver = 2
694    local checksum = file.checksum
695
696    local function prepare_extra_data(n, id)
697       -- test if already loaded
698       if type(id)=='number' then -- sometimes id is an integer
699          return
700       elseif (not id) or font_extra_info[n]  then return
701       end
702       local fname = id.filename
703       local bname = file.basename(fname)
704       if not fname then
705          font_extra_info[n] = {}; return
706       elseif font_extra_basename[bname] then
707          font_extra_info[n] = font_extra_basename[bname]; return
708       end
709       -- if the cache is present, read it
710       local newsum = checksum(fname) -- MD5 checksum of the fontfile
711       local v = "extra_" .. string.lower(file.nameonly(fname))
712       local dat = ltjb.load_cache(v,
713          function (t) return (t.version~=cache_ver) or (t.chksum~=newsum) end
714       )
715       -- if the cache is not found or outdated, save the cache
716       if dat then
717          font_extra_basename[bname] = dat[1] or {}
718       else
719          local dat = nil
720          dat = prepare_vert_data(dat, id)
721          dat = prepare_fl_data(dat, id, fname)
722          font_extra_basename[bname] = dat or {}
723          ltjb.save_cache( v,
724                           {
725                              chksum = checksum(fname),
726                              version = cache_ver,
727                              dat,
728                           })
729       end
730       font_extra_info[n] = font_extra_basename[bname]
731    end
732    luatexbase.add_to_callback('luatexja.define_font',
733                               function (res, name, size, id)
734                                  prepare_extra_data(id, res)
735                               end,
736                               'ltj.prepare_extra_data', 1)
737
738    local function a (n, dat) font_extra_info[n] = dat end
739    ltjr.vert_addfunc = a
740
741    local identifiers = fonts.hashes.identifiers
742    for i=1,font.nextid()-1 do
743       if identifiers[i] then prepare_extra_data(i, identifiers[i]) end
744    end
745 end
746
747 ------------------------------------------------------------------------
748 -- MISC
749 ------------------------------------------------------------------------
750 do
751    local get_dir_count = ltjd.get_dir_count
752    local is_ucs_in_japanese_char = ltjc.is_ucs_in_japanese_char_direct
753    local tex_set_attr = tex.setattribute
754    local font = font
755    -- EXT: italic correction
756    function append_italic()
757       local p = to_direct(tex.nest[tex.nest.ptr].tail)
758       if p and getid(p)==id_glyph then
759          local f = getfont(p)
760          local g = node_new(id_kern)
761          setfield(g, 'subtype', 1)
762          set_attr(g, attr_icflag, ITALIC)
763          if is_ucs_in_japanese_char(p) then
764             f = has_attr(p, (get_dir_count()==dir_tate) and attr_curtfnt or attr_curjfnt)
765             local j = font_metric_table[f]
766             setfield(g, 'kern', j.char_type[find_char_class(getchar(p), j)].italic)
767          else
768             local h = font_getfont(f) or font.fonts[f]
769             if h then
770                setfield(g, 'kern', h.characters[getchar(p)].italic)
771             else
772                tex_set_attr(attr_icflag, 0)
773                return node_free(g)
774             end
775          end
776          node_write(g)
777          tex_set_attr(attr_icflag, 0)
778       end
779    end
780 end
781