summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/luatex/luatexja/ltj-jfont.lua
blob: 1b4d04df4e743f344c5e79c018c539d8668ee8b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
--
-- luatexja/jfont.lua
--
luatexbase.provides_module({
  name = 'luatexja.jfont',
  date = '2011/05/11',
  description = 'Loader for Japanese fonts',
})
module('luatexja.jfont', package.seeall)

luatexja.load_module('base');      local ltjb = luatexja.base
luatexja.load_module('charrange'); local ltjc = luatexja.charrange

local node_new = node.new
local has_attr = node.has_attribute
local set_attr = node.set_attribute
local round = tex.round
local getfont = font.getfont

local attr_icflag = luatexbase.attributes['ltj@icflag']
local attr_curjfnt = luatexbase.attributes['ltj@curjfnt']
local id_glyph = node.id('glyph')
local id_kern = node.id('kern')
local id_glue_spec = node.id('glue_spec')
local id_glue = node.id('glue')
local cat_lp = luatexbase.catcodetables['latex-package']
local ITALIC       = luatexja.icflag_table.ITALIC
local FROM_JFM     = luatexja.icflag_table.FROM_JFM

------------------------------------------------------------------------
-- LOADING JFM
------------------------------------------------------------------------

metrics={} -- this table stores all metric informations
font_metric_table={} -- [font number] -> jfm_name, jfm_var, size

luatexbase.create_callback("luatexja.load_jfm", "data", function (ft, jn) return ft end)

local jfm_file_name, jfm_var
local defjfm_res

function define_jfm(t)
   local real_char -- Does current character class have the 'real' character?
   if t.dir~='yoko' then
      defjfm_res= nil; return
   elseif type(t.zw)~='number' or type(t.zh)~='number' then 
      defjfm_res= nil; return
   end
   t.char_type = {}; t.chars = {}
   for i,v in pairs(t) do
      if type(i) == 'number' then -- char_type
	 if not v.chars then
	    if i ~= 0 then defjfm_res= nil; return  end
	    real_char = true
	 else
	    real_char = false
	    for j,w in pairs(v.chars) do
	       if w == 'lineend' then
		  if #v.chars ~= 1 then defjfm_res= nil; return end
	       elseif type(w) == 'number' then
		  real_char = true;
	       elseif type(w) == 'string' and utf.len(w)==1 then
		  real_char = true; w = utf.byte(w)
	       elseif type(w) == 'string' and utf.len(w)==2 and utf.sub(w,2) == '*' then
		  real_char = true; w = utf.byte(utf.sub(w,1,1))
                  if not t.chars[-w] then 
                     t.chars[-w] = i
                  else 
                     defjfm_res= nil; return
                  end
	       end
	       if not t.chars[w] then
		  t.chars[w] = i
	       else 
		  defjfm_res= nil; return
	       end
	    end
            if type(v.align)~='string' then 
               v.align = 'left' -- left
            end
	    if real_char then
	       if not (type(v.width)=='number' or v.width~='prop') then
		  defjfm_res= nil; return
	       else
		  if type(v.height)~='number' then
		     v.height = 0.0
		  end
		  if type(v.depth)~='number' then
		     v.depth = 0.0
		  end
		  if type(v.italic)~='number' then 
		     v.italic = 0.0
		  end
		  if type(v.left)~='number' then 
		     v.left = 0.0
		  end
		  if type(v.down)~='number' then 
		     v.down = 0.0
		  end
	       end
	    end
	    v.chars = nil
	 end
	 if not v.kern then v.kern = {} end
	 if not v.glue then v.glue = {} end
	 for j in pairs(v.glue) do
	    if v.kern[j] then defjfm_res= nil; return end
	 end
	 for j,x in pairs(v.kern) do
	    if type(x)=='number' then 
               v.kern[j] = {x, 0}
            elseif type(x)=='table' then 
               v.kern[j] = {x[1], x[2] or 0}
            end
	 end
	 t.char_type[i] = v
	 t[i] = nil
      end
   end
   t = luatexbase.call_callback("luatexja.load_jfm", t, jfm_file_name)
   t.size_cache = {}
   defjfm_res = t
end

local function mult_table(old,scale) -- modified from table.fastcopy
    if old then
       local new = { }
       for k,v in next, old do
	  if type(v) == "table" then
	     new[k] = mult_table(v,scale)
	  elseif type(v) == "number" then
	     new[k] = round(v*scale)
          else
             new[k] = v
	  end
       end
       return new
    else return nil end
end

local function update_jfm_cache(j,sz)
   if metrics[j].size_cache[sz] then return end
   local t = {}
   metrics[j].size_cache[sz] = t
   t.chars = metrics[j].chars
   t.char_type = mult_table(metrics[j].char_type, sz)
   for i,v in pairs(t.char_type) do
      if type(i) == 'number' then -- char_type
	 for k,w in pairs(v.glue) do
	    local g, h = node.new(id_glue), node_new(id_glue_spec)
            v.glue[k] = {g, (w[5] and w[5]/sz or 0)}
            h.width, h.stretch, h.shrink = w[1], w[2], w[3]
            h.stretch_order, h.shrink_order = 0, 0
            g.subtype = 0; g.spec = h; set_attr(g, attr_icflag, FROM_JFM + 
                                                (w[4] and w[4]/sz or 0)); 
	 end
	 for k,w in pairs(v.kern) do
	    w[2] = w[2]/sz
	 end
      end
   end
   t.kanjiskip = mult_table(metrics[j].kanjiskip, sz)
   t.xkanjiskip = mult_table(metrics[j].xkanjiskip,sz)
   t.zw = round(metrics[j].zw*sz)
   t.zh = round(metrics[j].zh*sz)
end

luatexbase.create_callback("luatexja.find_char_class", "data", 
			   function (arg, fmtable, char)
			      return 0
			   end)

function find_char_class(c,m)
-- c: character code, m: 
   if not m then return 0 end
   return m.chars[c] or 
      luatexbase.call_callback("luatexja.find_char_class", 0, m, c)
end

local function load_jfont_metric()
   if jfm_file_name=='' then 
      ltjb.package_error('luatexja',
			 'no JFM specified',
			 'To load and define a Japanese font, a JFM must be specified.'..
			  "The JFM 'ujis' will be  used for now.")
      jfm_file_name='ujis'
   end
   for j,v in ipairs(metrics) do 
      if v.name==jfm_file_name then return j end
   end
   luatexja.load_lua('jfm-' .. jfm_file_name .. '.lua')
   if defjfm_res then
      defjfm_res.name = jfm_file_name
      table.insert(metrics, defjfm_res)
      return #metrics
   else 
      return nil
   end
end


------------------------------------------------------------------------
-- LOADING JAPANESE FONTS
------------------------------------------------------------------------
local cstemp

-- EXT
function jfontdefX(g)
  local t = token.get_next()
  cstemp=token.csname_name(t)
  if g then luatexja.is_global = '\\global' else luatexja.is_global = '' end
  tex.sprint(cat_lp, '\\expandafter\\font\\csname ' .. cstemp .. '\\endcsname')
end

luatexbase.create_callback("luatexja.define_jfont", "data", function (ft, fn) return ft end)

-- EXT
function jfontdefY() -- for horizontal font
   local j = load_jfont_metric()
   local fn = font.id(cstemp)
   local f = getfont(fn)
   if not j then 
      ltjb.package_error('luatexja',
			 "bad JFM `" .. jfm_file_name .. "'",
			 'The JFM file you specified is not valid JFM file.\n'..
			    'So defining Japanese font is cancelled.')
      tex.sprint(cat_lp, luatexja.is_global .. '\\expandafter\\let\\csname ' ..cstemp 
             .. '\\endcsname=\\relax')
     return 
   end
   update_jfm_cache(j, f.size)
   local sz = metrics[j].size_cache[f.size]
   local fmtable = { jfm = j, size = f.size, var = jfm_var, 
		     zw = sz.zw, zh = sz.zh, 
		     chars = sz.chars, char_type = sz.char_type,
		     kanjiskip = sz.kanjiskip, xkanjiskip = sz.xkanjiskip, 
		    }
      
   fmtable = luatexbase.call_callback("luatexja.define_jfont", fmtable, fn)
   font_metric_table[fn]=fmtable
   tex.sprint(cat_lp, luatexja.is_global .. '\\protected\\expandafter\\def\\csname ' 
          .. cstemp  .. '\\endcsname{\\ltj@curjfnt=' .. fn .. '\\relax}')
end

-- zw, zh
function load_zw()
   local a = font_metric_table[tex.attribute[attr_curjfnt]]
   if a then
      tex.setdimen('ltj@zw', a.zw)
   else 
      tex.setdimen('ltj@zw',0)
   end
end

function load_zh()
   local a = font_metric_table[tex.attribute[attr_curjfnt]]
   if a then
      tex.setdimen('ltj@zh', a.zh)
   else 
      tex.setdimen('ltj@zh',0)
   end
end

-- extract jfm_file_name and jfm_var
local function extract_metric(name)
   local basename=name
   local tmp = utf.sub(basename, 1, 5)
   jfm_file_name = ''; jfm_var = ''
   if tmp == 'file:' or tmp == 'name:' or tmp == 'psft:' then
      basename = utf.sub(basename, 6)
   end
   local p = utf.find(basename, ":")
   if p then 
      basename = utf.sub(basename, p+1)
   else return 
   end
   -- now basename contains 'features' only.
   p=1
   while p do
      local q = utf.find(basename, ";", p+1) or utf.len(basename)+1
      if utf.sub(basename, p, p+3)=='jfm=' and q>p+4 then
	 jfm_file_name = utf.sub(basename, p+4, q-1)
      elseif utf.sub(basename, p, p+6)=='jfmvar=' and q>p+6 then
	 jfm_var = utf.sub(basename, p+7, q-1)
      end
      if utf.len(basename)+1==q then p = nil else p = q + 1 end
   end
   return
end

-- replace fonts.define.read()
function font_callback(name, size, id, fallback)
   extract_metric(name)
   -- In the present imple., we don't remove "jfm=..." from name.
   return fallback(name, size, id)
end

------------------------------------------------------------------------
-- MISC
------------------------------------------------------------------------

local is_ucs_in_japanese_char = ltjc.is_ucs_in_japanese_char
-- EXT: italic correction
function append_italic()
   local p = tex.nest[tex.nest.ptr].tail
   if p and p.id==id_glyph then
      local f = p.font
      local g = node_new(id_kern)
      g.subtype = 1; node.set_attribute(g, attr_icflag, ITALIC)
      if is_ucs_in_japanese_char(p) then
	 f = has_attr(p, attr_curjfnt)
	 local j = font_metric_table[f]
	 g.kern = j.char_type[find_char_class(p.char, j)].italic
      else
	 local h = getfont(f)
	 if h then
	    g.kern = h.characters[p.char].italic
	 else
	    tex.attribute[attr_icflag] = -(0x7FFFFFFF)
	    return node.free(g)
	 end
      end
      node.write(g)
      tex.attribute[attr_icflag] = -(0x7FFFFFFF)
   end
end