OSDN Git Service

'lineend=extended' in luatexja-adjust.sty (wip)
[luatex-ja/luatexja.git] / src / ltj-jfont.lua
1 --
2 -- luatexja/jfont.lua
3 --
4 luatexbase.provides_module({
5   name = 'luatexja.jfont',
6   date = '2016/04/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 local setfield = node.direct.setfield
17 local getid = node.direct.getid
18 local to_direct = node.direct.todirect
19
20 local node_new = node.direct.new
21 local node_free = node.direct.free
22 local has_attr = node.direct.has_attribute
23 local set_attr = node.direct.set_attribute
24 local round = tex.round
25 local font_getfont = font.getfont
26
27 local attr_icflag = luatexbase.attributes['ltj@icflag']
28 local attr_curjfnt = luatexbase.attributes['ltj@curjfnt']
29 local attr_curtfnt = luatexbase.attributes['ltj@curtfnt']
30 local id_glyph = node.id('glyph')
31 local id_kern = node.id('kern')
32 local cat_lp = luatexbase.catcodetables['latex-package']
33 local FROM_JFM     = luatexja.icflag_table.FROM_JFM
34 ------------------------------------------------------------------------
35 -- LOADING JFM
36 ------------------------------------------------------------------------
37
38 metrics={} -- this table stores all metric informations
39 font_metric_table={} -- [font number] -> jfm_name, jfm_var, size
40
41 luatexbase.create_callback("luatexja.load_jfm", "data", function (ft, jn) return ft end)
42
43 local jfm_file_name, jfm_var, jfm_ksp
44 local defjfm_res
45 local jfm_dir, is_def_jfont, is_vert_enabled
46
47 local function norm_val(a)
48    if (not a) or (a==0.) then
49       return nil
50    elseif a==true then
51       return 1
52    else
53       return a
54    end
55 end
56
57 function define_jfm(t)
58    local real_char -- Does current character class have the 'real' character?
59    if t.dir~=jfm_dir then
60       defjfm_res= nil; return
61    elseif type(t.zw)~='number' or type(t.zh)~='number' then
62       defjfm_res= nil; return
63    end
64    t.version = (type(t.version)=='number') and t.version or 1
65    t.char_type = {}; t.chars = {}
66    for i,v in pairs(t) do
67       if type(i) == 'number' then -- char_type
68          if not v.chars then
69             if i ~= 0 then defjfm_res= nil; return  end
70          else
71             for j,w in pairs(v.chars) do
72                if type(w) == 'number' and w~=-1 then
73                elseif type(w) == 'string' and utf.len(w)==1 then
74                   w = utf.byte(w)
75                elseif type(w) == 'string' and utf.len(w)==2 and utf.sub(w,2) == '*' then
76                   w = utf.byte(utf.sub(w,1,1))
77                end
78                if not t.chars[w] then
79                   t.chars[w] = i
80                else
81                   defjfm_res= nil; return
82                end
83             end
84             v.chars = nil
85          end
86          if type(v.align)~='string' then
87             v.align = 'left' -- left
88          end
89          if type(v.width)~='number' then
90             v.width = (jfm_dir=='tate') and  1.0
91          end
92          if type(v.height)~='number' then
93             v.height = (jfm_dir=='tate') and  0.0
94          end
95          if type(v.depth)~='number' then
96             v.depth =  (jfm_dir=='tate') and  0.0
97          end
98          if type(v.italic)~='number' then
99             v.italic = 0.0
100          end
101          if type(v.left)~='number' then
102             v.left = 0.0
103          end
104          if type(v.down)~='number' then
105             v.down = 0.0
106          end
107          if t.version>=2 then
108             if v.end_stretch then defjfm_res= nil; return end
109             if v.end_shrink  then defjfm_res= nil; return end
110             if v.end_adjust then
111                if type(v.end_adjust)~='table' then
112                   v.end_adjust = nil
113                elseif #(v.end_adjust)==0 then
114                   v.end_adjust = nil
115                else 
116                   table.sort(v.end_adjust)
117                end
118             end
119          else
120             v.end_adjust = nil
121             if v.end_stretch and v.end_stretch~=0.0 then 
122                v.end_adjust = (v.end_adjust or {}) 
123                v.end_adjust[#(v.end_adjust)+1] = v.end_stretch
124             end
125             if v.end_shrink and v.end_ahrink~=0.0 then 
126                v.end_adjust = (v.end_adjust or {}) 
127                v.end_adjust[#(v.end_adjust)+1] = -v.end_shrink
128             end
129             if v.end_adjust then v.end_adjust[#(v.end_adjust)+1] = 0.0 end
130          end
131          v.kern = v.kern or {}; v.glue = v.glue or {}
132          for j,x in pairs(v.glue) do
133             if v.kern[j] then defjfm_res= nil; return end
134             x.ratio, x[5] = (x.ratio or (x[5] and 0.5*(1+x[5]) or 0.5)), nil
135             x.priority, x[4] = (x.priority or x[4] or 0), nil
136             x.kanjiskip_natural = norm_val(x.kanjiskip_natural)
137             x.kanjiskip_stretch = norm_val(x.kanjiskip_stretch)
138             x.kanjiskip_shrink = norm_val(x.kanjiskip_shrink)
139          end
140          for j,x in pairs(v.kern) do
141             if type(x)=='number' then
142                v.kern[j] = {x, 0.5}
143             elseif type(x)=='table' then
144                v.kern[j] = { x[1], ratio=x.ratio or (x[2] and 0.5*(1+x[2]) or 0.5) }
145             end
146          end
147          t.char_type[i] = v
148          t[i] = nil
149       end
150    end
151    t = luatexbase.call_callback("luatexja.load_jfm", t, jfm_file_name)
152    t.size_cache = {}
153    defjfm_res = t
154 end
155
156 local update_jfm_cache
157 do
158    local function mult_table(old,scale) -- modified from table.fastcopy
159       if old then
160          local new = { }
161          for k,v in next, old do
162             if type(v) == "table" then
163                new[k] = mult_table(v,scale)
164             elseif type(v) == "number" then
165                new[k] = round(v*scale)
166             else
167                new[k] = v
168             end
169          end
170          return new
171       else return nil end
172    end
173    update_jfm_cache = function (j,sz)
174       if metrics[j].size_cache[sz] then return end
175       local t = {}
176       metrics[j].size_cache[sz] = t
177       t.chars = metrics[j].chars
178       t.char_type = mult_table(metrics[j].char_type, sz)
179       for i,v in pairs(t.char_type) do
180          v.align = (v.align=='left') and 0 or
181             ((v.align=='right') and 1 or 0.5)
182          if type(i) == 'number' then -- char_type
183             for k,w in pairs(v.glue) do
184                v[k] = {
185                   nil,
186                   ratio=w.ratio/sz,
187                   priority=FROM_JFM + w.priority/sz,
188                   width = w[1], stretch = w[2], shrink = w[3],
189                   kanjiskip_natural = w.kanjiskip_natural and w.kanjiskip_natural/sz,
190                   kanjiskip_stretch = w.kanjiskip_stretch and w.kanjiskip_stretch/sz,
191                   kanjiskip_shrink =  w.kanjiskip_shrink  and w.kanjiskip_shrink/sz,
192                }
193             end
194             for k,w in pairs(v.kern) do
195                local g = node_new(id_kern, 1)
196                setfield(g, 'kern', w[1])
197                set_attr(g, attr_icflag, FROM_JFM)
198                v[k] = {g, ratio=w[2]/sz}
199             end
200          end
201          v.glue, v.kern = nil, nil
202       end
203       t.kanjiskip = mult_table(metrics[j].kanjiskip, sz)
204       t.xkanjiskip = mult_table(metrics[j].xkanjiskip,sz)
205       t.zw = round(metrics[j].zw*sz)
206       t.zh = round(metrics[j].zh*sz)
207       t.size = sz
208    end
209 end
210
211 luatexbase.create_callback("luatexja.find_char_class", "data",
212                            function (arg, fmtable, char)
213                               return 0
214                            end)
215 do
216    local start_time_measure = ltjb.start_time_measure
217    local stop_time_measure = ltjb.stop_time_measure
218    local fcc_temp = { chars_cbcache = {} }
219    setmetatable(
220       fcc_temp.chars_cbcache,
221       {
222          __index = function () return 0 end,
223       })
224    function find_char_class(c,m)
225       -- c: character code, m:
226       local r = (m or fcc_temp).chars_cbcache[c]
227       if not r then
228          r = m.chars[c] or
229             luatexbase.call_callback("luatexja.find_char_class", 0, m, c)
230          m.chars_cbcache[c or 0] = r
231       end
232       return r
233    end
234 end
235
236
237 ------------------------------------------------------------------------
238 -- LOADING JAPANESE FONTS
239 ------------------------------------------------------------------------
240
241 do
242    local cstemp
243    local global_flag -- true if \globaljfont, false if \jfont
244    local function load_jfont_metric()
245       if jfm_file_name=='' then
246          ltjb.package_error('luatexja',
247                             'no JFM specified',
248                             'To load and define a Japanese font, a JFM must be specified.'..
249                             "The JFM 'ujis' will be  used for now.")
250          jfm_file_name='ujis'
251       end
252       for j,v in ipairs(metrics) do
253          if v.name==jfm_file_name then return j end
254       end
255       luatexja.load_lua('jfm-' .. jfm_file_name .. '.lua')
256       if defjfm_res then
257          defjfm_res.name = jfm_file_name
258          table.insert(metrics, defjfm_res)
259          return #metrics
260       else
261          return nil
262       end
263    end
264
265 -- EXT
266    local utf8 = unicode.utf8
267    function jfontdefX(g, dir, csname)
268       jfm_dir, is_def_jfont = dir, true
269       cstemp = csname:sub( (utf8.byte(csname,1,1) == tex.escapechar) and 2 or 1, -1)
270       cstemp = cstemp:sub(1, ((cstemp:sub(-1,-1)==' ') and (cstemp:len()>=2)) and -2 or -1)
271       global_flag = g and '\\global' or ''
272       tex.sprint(cat_lp, '\\expandafter\\font\\csname ',
273                  (cstemp==' ') and '\\space' or cstemp, '\\endcsname')
274    end
275
276    luatexbase.create_callback("luatexja.define_jfont", "data", function (ft, fn) return ft end)
277
278 -- EXT
279    local identifiers = fonts.hashes.identifiers
280    function jfontdefY()
281       local j = load_jfont_metric(jfm_dir)
282       local fn = font.id(cstemp)
283       local f = font_getfont(fn)
284       if not j then
285          ltjb.package_error('luatexja',
286                             "bad JFM `" .. jfm_file_name .. "'",
287                             'The JFM file you specified is not valid JFM file.\n'..
288                                'So defining Japanese font is cancelled.')
289          tex.sprint(cat_lp, global_flag, '\\expandafter\\let\\csname ',
290                     (cstemp==' ') and '\\space' or cstemp,
291                        '\\endcsname=\\relax')
292          return
293       end
294       if not f then return end
295       update_jfm_cache(j, f.size)
296       local ad = identifiers[fn].parameters
297       local sz = metrics[j].size_cache[f.size]
298       local fmtable = { jfm = j, size = f.size, var = jfm_var,
299                         with_kanjiskip = jfm_ksp,
300                         zw = sz.zw, zh = sz.zh,
301                         ascent = ad.ascender,
302                         descent = ad.descender,
303                         chars = sz.chars, char_type = sz.char_type,
304                         kanjiskip = sz.kanjiskip, xkanjiskip = sz.xkanjiskip,
305                         chars_cbcache = {},
306                         vert_activated = is_vert_enabled,
307       }
308
309       fmtable = luatexbase.call_callback("luatexja.define_jfont", fmtable, fn)
310       font_metric_table[fn]=fmtable
311       tex.sprint(cat_lp, global_flag, '\\protected\\expandafter\\def\\csname ',
312                     (cstemp==' ') and '\\space' or cstemp, '\\endcsname{\\ltj@cur'..
313                     (jfm_dir == 'yoko' and 'j' or 't') .. 'fnt', fn, '\\relax}')
314    end
315 end
316
317 do
318    local get_dir_count = ltjd.get_dir_count
319    local dir_tate = luatexja.dir_table.dir_tate
320    local tex_get_attr = tex.getattribute
321    -- PUBLIC function
322    function get_zw()
323       local a = font_metric_table[
324          tex_get_attr((get_dir_count()==dir_tate) and attr_curtfnt or attr_curjfnt)]
325       return a and a.zw or 0
326    end
327    function get_zh()
328       local a = font_metric_table[
329          tex_get_attr((get_dir_count()==dir_tate) and attr_curtfnt or attr_curjfnt)]
330       return a and a.zw or 0
331    end
332 end
333
334 do
335    -- extract jfm_file_name and jfm_var
336    -- normalize position of 'jfm=' and 'jfmvar=' keys
337    local function extract_metric(name)
338       local is_braced = name:match('^{(.*)}$')
339        name= is_braced or name
340       jfm_file_name = ''; jfm_var = ''; jfm_ksp = true
341       local tmp, index = name:sub(1, 5), 1
342       if tmp == 'file:' or tmp == 'name:' or tmp == 'psft:' then
343          index = 6
344       end
345       local p = name:find(":", index); index = p and (p+1) or index
346       while index do
347          local l = name:len()+1
348          local q = name:find(";", index+1) or l
349          if name:sub(index, index+3)=='jfm=' and q>index+4 then
350             jfm_file_name = name:sub(index+4, q-1)
351             if l~=q then
352                name = name:sub(1,index-1) .. name:sub(q+1)
353             else
354                name = name:sub(1,index-1)
355                index = nil
356             end
357          elseif name:sub(index, index+6)=='jfmvar=' and q>index+6 then
358             jfm_var = name:sub(index+7, q-1)
359             if l~=q then
360                name = name:sub(1,index-1) .. name:sub(q+1)
361             else
362                name = name:sub(1,index-1)
363                index = nil
364             end
365          else
366             index = (l~=q) and (q+1) or nil
367          end
368       end
369       if jfm_file_name~='' then
370          local l = name:sub(-1)
371          name = name
372             .. ((l==':' or l==';') and '' or ';')
373             .. 'jfm=' .. jfm_file_name
374          if jfm_var~='' then
375             name = name .. 'jfmvar=' .. jfm_var
376          end
377       end
378       for x in string.gmatch (name, "[:;]([+%%-]?)ltjks") do
379          jfm_ksp = not (x=='-')
380       end
381       if jfm_dir == 'tate' then
382          is_vert_enabled = (not name:match('[:;]%-vert')) and (not  name:match('[:;]%-vrt2'))
383          if not name:match('vert') and not name:match('vrt2') then
384             name = name .. ';+vert;+vrt2'
385          end
386       else
387          is_vert_enabled = nil
388       end
389       return is_braced and ('{' .. name .. '}') or name
390    end
391
392    -- define_font callback
393    local otfl_fdr
394    local ltjr_font_callback = ltjr.font_callback
395    function luatexja.font_callback(name, size, id)
396       local new_name = is_def_jfont and extract_metric(name) or name
397       is_def_jfont = false
398       local res =  ltjr_font_callback(new_name, size, id, otfl_fdr)
399       luatexbase.call_callback('luatexja.define_font', res, new_name, size, id)
400       -- this callback processes variation selector, so we execute it always
401       return res
402    end
403    luatexbase.create_callback('luatexja.define_font', 'simple', function (n) return n end)
404    otfl_fdr= luatexbase.remove_from_callback('define_font', 'luaotfload.define_font')
405    luatexbase.add_to_callback('define_font',luatexja.font_callback,"luatexja.font_callback", 1)
406 end
407
408 ------------------------------------------------------------------------
409 -- LATEX INTERFACE
410 ------------------------------------------------------------------------
411 do
412    -- these function are called from ltj-latex.sty
413    local fenc_list, kyenc_list, ktenc_list = {}, {}, {}
414    function add_fenc_list(enc) fenc_list[enc] = 'true ' end
415    function add_kyenc_list(enc) kyenc_list[enc] = 'true ' end
416    function add_ktenc_list(enc) ktenc_list[enc] = 'true ' end
417    function is_kyenc(enc)
418       tex.sprint(cat_lp, '\\let\\ifin@\\if' .. (kyenc_list[enc] or 'false '))
419    end
420    function is_ktenc(enc)
421       tex.sprint(cat_lp, '\\let\\ifin@\\if' .. (ktenc_list[enc] or 'false '))
422    end
423    function is_kenc(enc)
424       tex.sprint(cat_lp, '\\let\\ifin@\\if'
425                  .. (kyenc_list[enc] or ktenc_list[enc] or 'false '))
426    end
427
428    local kfam_list, Nkfam_list = {}, {}
429    function add_kfam(fam)
430       kfam_list[fam]=true
431    end
432    function search_kfam(fam, use_fd)
433       if kfam_list[fam] then
434          tex.sprint(cat_lp, '\\let\\ifin@\\iftrue '); return
435       elseif Nkfam_list[fam] then
436          tex.sprint(cat_lp, '\\let\\ifin@\\iffalse '); return
437       elseif use_fd then
438          for i,_ in pairs(kyenc_list) do
439             if kpse.find_file(string.lower(i)..fam..'.fd') then
440                tex.sprint(cat_lp, '\\let\\ifin@\\iftrue '); return
441             end
442          end
443          for i,_ in pairs(ktenc_list) do
444             if kpse.find_file(string.lower(i)..fam..'.fd') then
445                tex.sprint(cat_lp, '\\let\\ifin@\\iftrue '); return
446             end
447          end
448          Nkfam_list[fam]=true; tex.sprint(cat_lp, '\\let\\ifin@\\iffalse '); return
449       else
450          tex.sprint(cat_lp, '\\let\\ifin@\\iffalse '); return
451       end
452    end
453    local ffam_list, Nffam_list = {}, {}
454    function is_ffam(fam)
455       tex.sprint(cat_lp, '\\let\\ifin@\\if' .. (ffam_list[fam] or 'false '))
456    end
457    function add_ffam(fam)
458       ffam_list[fam]='true '
459    end
460    function search_ffam_declared()
461      local s = ''
462      for i,_ in pairs(fenc_list) do
463         s = s .. '\\cdp@elt{' .. i .. '}'
464      end
465      tex.sprint(cat_lp, s)
466    end
467    function search_ffam_fd(fam)
468       if Nffam_list[fam] then
469          tex.sprint(cat_lp, '\\let\\ifin@\\iffalse '); return
470       else
471          for i,_ in pairs(fenc_list) do
472             if kpse.find_file(string.lower(i)..fam..'.fd') then
473                tex.sprint(cat_lp, '\\let\\ifin@\\iftrue '); return
474             end
475          end
476          Nffam_list[fam]=true; tex.sprint(cat_lp, '\\let\\ifin@\\iffalse '); return
477       end
478    end
479
480 end
481 ------------------------------------------------------------------------
482 -- ALTERNATE FONTS
483 ------------------------------------------------------------------------
484 alt_font_table = {}
485 local alt_font_table = alt_font_table
486 local attr_curaltfnt = {}
487 local ucs_out = 0x110000
488
489 ------ for TeX interface
490 -- EXT
491 function set_alt_font(b,e,ind,bfnt)
492    -- ind: 新フォント, bfnt: 基底フォント
493    if b>e then b, e = e, b end
494    if b*e<=0 then
495       ltjb.package_error('luatexja',
496                         'bad character range ([' .. b .. ',' .. e .. ']). ' ..
497                            'I take the intersection with [0x80, 0x10ffff].')
498       b, e = math.max(0x80,b),math.min(ucs_out-1,e)
499    elseif e<0 then -- b<e<0
500       -- do nothing
501    elseif b<0x80 or e>=ucs_out then
502       ltjb.package_warning('luatexja',
503                            'bad character range ([' .. b .. ',' .. e .. ']). ' ..
504                               'I take the intersection with [0x80, 0x10ffff].')
505       b, e = math.max(0x80,b), math.min(ucs_out-1,e)
506    end
507    if not alt_font_table[bfnt] then alt_font_table[bfnt]={} end
508    local t = alt_font_table[bfnt]
509    local ac = font_getfont(ind).characters
510    if bfnt==ind then ind = nil end -- ind == bfnt の場合はテーブルから削除
511    if e>=0 then -- character range
512       for i=b, e do
513          if ac[i]then  t[i]=ind end
514       end
515    else
516       b, e = -e, -b
517       local tx = font_metric_table[bfnt].chars
518       for i,v in pairs(tx) do
519          if b<=v and v<=e and ac[i] then t[i]=ind end
520       end
521    end
522 end
523
524 -- EXT
525 function clear_alt_font(bfnt)
526    if alt_font_table[bfnt] then
527       local t = alt_font_table[bfnt]
528       for i,_ in pairs(t) do t[i]=nil; end
529    end
530 end
531
532 ------ used in ltjp.suppress_hyphenate_ja callback
533 function replace_altfont(pf, pc)
534    local a = alt_font_table[pf]
535    return a and a[pc] or pf
536 end
537
538 ------ for LaTeX interface
539
540 local alt_font_table_latex = {}
541
542 -- EXT
543 function clear_alt_font_latex(bbase)
544    local t = alt_font_table_latex[bbase]
545    if t then
546       for j,v in pairs(t) do t[j] = nil end
547    end
548 end
549
550 -- EXT
551 function set_alt_font_latex(b,e,ind,bbase)
552    -- ind: Alt font の enc/fam/ser/shape, bbase: 基底フォントの enc/fam/ser/shape
553    if b>e then b, e = e, b end
554    if b*e<=0 then
555       ltjb.package_error('luatexja',
556                         'bad character range ([' .. b .. ',' .. e .. ']). ' ..
557                            'I take the intersection with [0x80, 0x10ffff].')
558       b, e = math.max(0x80,b),math.min(ucs_out-1,e)
559    elseif e<0 then -- b<e<0
560       -- do nothing
561    elseif b<0x80 or e>=ucs_out then
562       ltjb.package_warning('luatexja',
563                            'bad character range ([' .. b .. ',' .. e .. ']). ' ..
564                               'I take the intersection with [0x80, 0x10ffff].')
565       b, e = math.max(0x80,b), math.min(ucs_out-1,e)
566    end
567
568    if not alt_font_table_latex[bbase] then alt_font_table_latex[bbase]={} end
569    local t = alt_font_table_latex[bbase]
570    if not t[ind] then t[ind] = {} end
571    for i=b, e do
572       for j,v in pairs(t) do
573          if v[i] then -- remove old entry
574             if j~=ind then v[i]=nil end; break
575          end
576       end
577       t[ind][i]=true
578    end
579    -- remove the empty tables
580    for j,v in pairs(t) do
581       local flag_clear = true
582       for k,_ in pairs(v) do flag_clear = false; break end
583       if flag_clear then t[j]=nil end
584    end
585    if ind==bbase  then t[bbase] = nil end
586 end
587
588 -- ここから先は 新 \selectfont の内部でしか実行されない
589 do
590    local alt_font_base, alt_font_base_num
591    local aftl_base
592    -- EXT
593    function does_alt_set(bbase)
594       aftl_base = alt_font_table_latex[bbase]
595       tex.sprint(cat_lp, '\\if' .. (aftl_base and 'true' or 'false'))
596    end
597    -- EXT
598    function print_aftl_address()
599       tex.sprint(cat_lp, ';ltjaltfont' .. tostring(aftl_base):sub(8))
600    end
601
602 -- EXT
603    function output_alt_font_cmd(dir, bbase)
604       alt_font_base = bbase
605       if dir == 't' then
606          alt_font_base_num = tex.getattribute(attr_curtfnt)
607       else
608          alt_font_base_num = tex.getattribute(attr_curjfnt)
609       end
610       local t = alt_font_table[alt_font_base_num]
611       if t then
612          for i,_ in pairs(t) do t[i]=nil end
613       end
614       t = alt_font_table_latex[bbase]
615       if t then
616        for i,_ in pairs(t) do
617             tex.sprint(cat_lp, '\\ltj@pickup@altfont@aux' .. dir .. '{' .. i .. '}')
618          end
619       end
620    end
621
622 -- EXT
623    function pickup_alt_font_a(size_str)
624       local t = alt_font_table_latex[alt_font_base]
625       if t then
626          for i,v in pairs(t) do
627             tex.sprint(cat_lp, '\\expandafter\\ltj@pickup@altfont@copy'
628                           .. '\\csname ' .. i .. '/' .. size_str .. '\\endcsname{' .. i .. '}')
629          end
630       end
631    end
632
633    local function pickup_alt_font_class(class, afnt_num, afnt_chars)
634       local t  = alt_font_table[alt_font_base_num]
635       local tx = font_metric_table[alt_font_base_num].chars
636       for i,v in pairs(tx) do
637          if v==class and afnt_chars[i] then t[i]=afnt_num end
638       end
639    end
640
641 -- EXT
642    function pickup_alt_font_b(afnt_num, afnt_base)
643       local t = alt_font_table[alt_font_base_num]
644       local ac = font_getfont(afnt_num).characters
645       if not t then t = {}; alt_font_table[alt_font_base_num] = t end
646       for i,v in pairs(alt_font_table_latex[alt_font_base]) do
647          if i == afnt_base then
648             for j,_ in pairs(v) do
649                if j>=0 then
650                   if ac[j] then t[j]=afnt_num end
651                else  -- -n (n>=1) means that the character class n,
652                      -- which is defined in the JFM
653                   pickup_alt_font_class(-j, afnt_num, ac)
654                end
655             end
656             return
657          end
658       end
659    end
660
661 end
662 ------------------------------------------------------------------------
663 -- 終了時に各種ノードを破棄
664 ------------------------------------------------------------------------
665 do
666    function cleanup_size_cache()
667       --local gs, ke = 0, 0
668       for _,n in pairs(metrics) do
669          for i,t in pairs(n.size_cache) do
670             for _,v in pairs(t.char_type) do
671                for k,w in pairs(v) do
672                   if type(k)=='number' then
673                      --if w[1] then gs = gs + 1 else ke = ke + 1 end
674                      if w[1] then node_free(w[1]) end
675                   end
676                end
677             end
678             n.size_cache[i]=nil
679          end
680       end
681    end
682 end
683
684 ------------------------------------------------------------------------
685 -- VERT VARIANT TABLE
686 ------------------------------------------------------------------------
687 local vert_form_table = {
688    [0x2013]=0xFE32, [0x2014]=0xFE31, [0x2025]=0xFE30,
689    [0xFF08]=0xFE35, [0xFF09]=0xFE36, [0xFF5B]=0xFE37, [0xFF5D]=0xFE38,
690    [0x3014]=0xFE39, [0x3015]=0xFE3A, [0x3010]=0xFE3B, [0x3011]=0xFE3C,
691    [0x300A]=0xFE3D, [0x300B]=0xFE3E, [0x3008]=0xFE3F, [0x3009]=0xFE40,
692    [0x300C]=0xFE41, [0x300D]=0xFE42, [0x300E]=0xFE43, [0x300F]=0xFE44,
693    [0xFF3B]=0xFE47, [0xFF3D]=0xFE48, [0xFF3F]=0xFE33,
694 }
695
696 ------------------------------------------------------------------------
697 -- 追加のフォント情報
698 ------------------------------------------------------------------------
699 font_extra_info = {}
700 local font_extra_info = font_extra_info -- key: fontnumber
701 local font_extra_basename = {} -- key: basename
702
703 -- IVS and vertical metrics
704 local prepare_fl_data
705 local supply_vkern_table
706 do
707    local fields = fontloader.fields
708    local function glyph_vmetric(glyph)
709       local flds = fields(glyph)
710       local vw, tsb, vk = nil, nil, nil
711       for _,i in ipairs(flds) do
712          if i=='vwidth' then vw = glyph.vwidth end
713          if i=='tsidebearing' then tsb = glyph.tsidebearing end
714          if i=='vkerns' then vk = glyph.vkerns end
715       end
716       return vw, tsb, vk
717    end
718
719    local sort = table.sort
720    local function add_fl_table(dest, glyphs, unitable, asc_des, units, id)
721       local glyphmin, glyphmax = glyphs.glyphmin, glyphs.glyphmax
722       if glyphmax < 0 then return dest end
723       local tg = glyphs.glyphs
724       for i = glyphmin, glyphmax do
725          local gv = tg[i]
726          if gv then
727             if gv.altuni then
728                for _,at in pairs(gv.altuni) do
729                   local bu, vsel = at.unicode, at.variant
730                   if vsel then
731                      if vsel>=0xE0100 then vsel = vsel - 0xE0100 end
732                      local uniq_flag = true
733                      if dest and dest[bu] then
734                         for i,_ in pairs(dest[bu]) do
735                            if i==vsel then uniq_flag = false; break end
736                         end
737                      end
738                      if uniq_flag then
739                         dest = dest or {}; dest[bu] = dest[bu] or {}
740                         dest[bu][vsel] = unitable[gv.name]
741                      end
742                   end
743                end
744             end
745             -- vertical form
746             local gi = unitable[gv.name]
747             if gi then
748                if unitable[gv.name .. '.vert'] then
749                   dest = dest or {}; dest[gi] = dest[gi] or {};
750                   dest[gi].vform = unitable[gv.name .. '.vert']
751                elseif id.characters[gi] and vert_form_table[gi] then
752                   dest = dest or {}; dest[gi] = dest[gi] or {};
753                   dest[gi].vform = vert_form_table[gi]
754                end
755             end
756             -- vertical metric
757             local vw, tsb, vk = glyph_vmetric(gv)
758             local gi = unitable[gv.name]
759             if gi and vw and vw~=asc_des then
760                -- We do not use tsidebearing, since (1) fontloader does not read VORG table
761                -- and (2) 'tsidebearing' doea not appear in the returned table by fontloader.fields.
762                -- Hence, we assume that vertical origin == ascender
763                -- (see capsule_glyph_tate in ltj-setwidth.lua)
764                dest = dest or {}; dest[gi] = dest[gi] or {}
765                dest[gi].vwidth = vw/units
766             end
767             -- vertical kern
768             if gi and vk then
769                dest = dest or {};
770                local dest_vk = dest.vkerns or {}; dest.vkerns = dest_vk
771                for _,v in pairs(vk) do
772                   if unitable[v.char] then
773                      local vl = v.lookup
774                      if type(vl)=='table' then
775                         for _,vlt in pairs(vl) do
776                            dest_vk[vlt] = dest_vk[vlt] or {}
777                            dest_vk[vlt][gi] = dest_vk[vlt][gi] or {}
778                            dest_vk[vlt][gi][unitable[v.char]] = v.off
779                         end
780                      else
781                         dest_vk[vl] = dest_vk[vl] or {}
782                         dest_vk[vl][gi] = dest_vk[vl][gi] or {}
783                         dest_vk[vl][gi][unitable[v.char]] = v.off
784                      end
785                   end
786                end
787             end
788          end
789       end
790       return dest
791    end
792    prepare_fl_data = function (dest, id)
793       local fl = fontloader.open(id.filename)
794       local ind_to_uni, unicodes = {}, {}
795       for i,v in pairs(id.characters) do
796           ind_to_uni[v.index] = i
797       end
798       if fl.glyphs then
799          local tg, glyphmin, glyphmax = fl.glyphs, fl.glyphmin, fl.glyphmax
800          if 0 <= glyphmax then
801             for i = glyphmin, glyphmax do
802                if tg[i] and tg[i].name then unicodes[tg[i].name] = ind_to_uni[i] end
803             end
804          end
805          dest = add_fl_table(dest, fl, unicodes,
806                              fl.ascent + fl.descent, fl.units_per_em, id)
807       end
808       if fl.subfonts then
809          for _,v in pairs(fl.subfonts) do
810             local tg, glyphmin, glyphmax = v.glyphs, v.glyphmin, v.glyphmax
811             if 0 <= glyphmax then
812                for i = glyphmin, glyphmax do
813                   if tg[i] and tg[i].name then unicodes[tg[i].name] = ind_to_uni[i] end
814                end
815            end
816        end
817          for _,v in pairs(fl.subfonts) do
818             dest = add_fl_table(dest, v, unicodes,
819                                 fl.ascent + fl.descent, fl.units_per_em, id)
820          end
821      end
822      if dest then dest.unicodes = unicodes end
823      fontloader.close(fl); collectgarbage("collect")
824      return dest
825    end
826    -- supply vkern table
827    supply_vkern_table = function(id, bname)
828       local bx = font_extra_basename[bname].vkerns
829       local lookuphash =  id.resources.lookuphash
830       local desc = id.shared.rawdata.descriptions
831       if bx and lookuphash then
832          for i,v in pairs(bx) do
833             lookuphash[i] = lookuphash[i] or v
834             for j,w in pairs(v) do
835                desc[j].kerns = desc[j].kerns or {}
836                desc[j].kerns[i] = w
837             end
838          end
839       end
840    end
841 end
842
843 --
844 do
845    local cache_ver = 11
846    local checksum = file.checksum
847
848    local function prepare_extra_data_base(id)
849       if (not id) or (not id.filename) then return end
850       local bname = file.nameonly(id.filename)
851       if not font_extra_basename[bname] then
852          -- if the cache is present, read it
853          local newsum = checksum(id.filename) -- MD5 checksum of the fontfile
854          local v = "extra_" .. string.lower(file.nameonly(id.filename))
855          local dat = ltjb.load_cache(
856             v,
857             function (t) return (t.version~=cache_ver) or (t.chksum~=newsum) end
858          )
859          -- if the cache is not found or outdated, save the cache
860          if dat then
861             font_extra_basename[bname] = dat[1] or {}
862          else
863             local dat = nil
864             dat = prepare_fl_data(dat, id)
865             font_extra_basename[bname] = dat or {}
866             ltjb.save_cache( v,
867                              {
868                                 chksum = checksum(id.filename),
869                                 version = cache_ver,
870                                 dat,
871                              })
872          end
873          return bname
874       end
875    end
876    local function prepare_extra_data_font(id, res)
877       if type(res)=='table' and res.shared and res.filename then
878          font_extra_info[id] = font_extra_basename[file.nameonly(res.filename)]
879       end
880    end
881     luatexbase.add_to_callback(
882        'luaotfload.patch_font',
883        function (tfmdata)
884           -- these function is executed one time per one fontfile
885           local bname = prepare_extra_data_base(tfmdata)
886           if bname then supply_vkern_table(tfmdata, bname) end
887           return tfmdata
888        end,
889        'ltj.prepare_extra_data', 1)
890    luatexbase.add_to_callback(
891       'luatexja.define_font',
892       function (res, name, size, id)
893          prepare_extra_data_font(id, res)
894       end,
895       'ltj.prepare_extra_data', 1)
896
897    local nulltable = {} -- dummy
898    ltjr.vert_addfunc = function (n) font_extra_info[n] = nulltable end
899
900    local identifiers = fonts.hashes.identifiers
901    for i=1,font.nextid()-1 do
902       if identifiers[i] then
903          prepare_extra_data_base(identifiers[i])
904          prepare_extra_data_font(i,identifiers[i])
905       end
906    end
907 end
908
909
910 ------------------------------------------------------------------------
911 -- calculate vadvance
912 ------------------------------------------------------------------------
913 do
914    local function acc_feature(table_vadv, table_vorg, subtables, ft,  already_vert)
915       for char_num,v in pairs(ft.shared.rawdata.descriptions) do
916          if v.slookups then
917             for sn, sv in pairs(v.slookups) do
918                if subtables[sn] and type(sv)=='table' then
919                   if sv[4]~=0 then
920                      table_vadv[char_num]
921                         = (table_vadv[char_num] or 0) + sv[4]
922                   end
923                   if sv[2]~=0 and not already_vert then
924                      table_vorg[char_num]
925                         = (table_vorg[char_num] or 0) + sv[2]
926                   end
927                end
928             end
929          end
930       end
931    end
932
933 luatexbase.add_to_callback(
934    "luatexja.define_jfont",
935    function (fmtable, fnum)
936       local vadv = {}; fmtable.v_advance = vadv
937       local vorg = {}; fmtable.v_origin = vorg
938       local ft = font_getfont(fnum)
939       local subtables = {}
940       if ft.specification then
941          for feat_name,v in pairs(ft.specification.features.normal) do
942             if v==true then
943                for _,i in pairs(ft.resources.sequences) do
944                   if i.order[1]== feat_name and i.type == 'gpos_single' then
945                      for _,st in pairs(i.subtables) do
946                         subtables[st] = true
947                      end
948                   end
949                end
950             end
951          end
952          acc_feature(vadv, vorg, subtables, ft,
953                      ft.specification.features.normal.vrt2 or ft.specification.features.normal.vert)
954          for i,v in pairs(vadv) do
955             vadv[i]=vadv[i]/ft.units_per_em*fmtable.size
956          end
957          for i,v in pairs(vorg) do
958             vorg[i]=vorg[i]/ft.units_per_em*fmtable.size
959          end
960       end
961       return fmtable
962    end, 1, 'ltj.v_advance'
963 )
964 end
965
966 ------------------------------------------------------------------------
967 -- supply tounicode entries
968 ------------------------------------------------------------------------
969 do
970   local ltjr_prepare_cid_font = ltjr.prepare_cid_font
971   luatexbase.add_to_callback(
972      'luaotfload.patch_font',
973      function (tfmdata)
974         if tfmdata.cidinfo then
975            local rd = ltjr_prepare_cid_font(tfmdata.cidinfo.registry, tfmdata.cidinfo.ordering)
976            if rd then
977               local ru, rc = rd.resources.unicodes, rd.characters
978               for i,v in pairs(tfmdata.characters) do
979                  local w = ru["Japan1." .. tostring(v.index)]
980                  if w then
981                     v.tounicode = v.tounicode or rc[w]. tounicode
982                  end
983               end
984            end
985         end
986
987         return tfmdata
988      end,
989      'ltj.supply_tounicode', 1)
990 end
991
992
993 ------------------------------------------------------------------------
994 -- MISC
995 ------------------------------------------------------------------------
996 do
997    local getfont = node.direct.getfont
998    local getchar = node.direct.getchar
999    local get_dir_count = ltjd.get_dir_count
1000    local is_ucs_in_japanese_char = ltjc.is_ucs_in_japanese_char_direct
1001    local ensure_tex_attr = ltjb.ensure_tex_attr
1002    local node_write = node.direct.write
1003    local font = font
1004    local new_ic_kern
1005    if status.luatex_version>=89 then
1006        new_ic_kern = function(g)  return node_new(id_kern,3) end
1007    else
1008        local ITALIC       = luatexja.icflag_table.ITALIC
1009        new_ic_kern = function()
1010          local g = node_new(id_kern, 1)
1011          set_attr(g, attr_icflag, ITALIC)
1012          return g
1013        end
1014    end
1015    -- EXT: italic correction
1016    function append_italic()
1017       local p = to_direct(tex.nest[tex.nest.ptr].tail)
1018       local TEMP = node_new(id_kern)
1019       if p and getid(p)==id_glyph then
1020          if is_ucs_in_japanese_char(p) then
1021             local j = font_metric_table[
1022                has_attr(p, (get_dir_count()==dir_tate) and attr_curtfnt or attr_curjfnt)
1023                ]
1024             local g = new_ic_kern()
1025             setfield(g, 'kern', j.char_type[find_char_class(getchar(p), j)].italic)
1026             node_write(g); ensure_tex_attr(attr_icflag, 0)
1027          else
1028             local f = getfont(p)
1029             local h = font_getfont(f) or font.fonts[f]
1030             if h then
1031                local g = new_ic_kern()
1032                if h.characters[getchar(p)] and h.characters[getchar(p)].italic then
1033                   setfield(g, 'kern', h.characters[getchar(p)].italic)
1034                   node_write(g); ensure_tex_attr(attr_icflag, 0)
1035                end
1036             end
1037          end
1038       end
1039       node_free(TEMP)
1040    end
1041 end
1042