1 local has_attr = node.has_attribute
5 jfm={}; jfm.char_type={}; jfm.glue={}; jfm.kern={}
7 function jfm.define_char_type(t,lt)
8 if not jfm.char_type[t] then jfm.char_type[t]={} end
9 jfm.char_type[t].chars=lt
11 function jfm.define_type_dim(t,l,x,w,h,d,i)
12 if not jfm.char_type[t] then jfm.char_type[t]={} end
13 jfm.char_type[t].width=w; jfm.char_type[t].height=h;
14 jfm.char_type[t].depth=d; jfm.char_type[t].italic=i;
15 jfm.char_type[t].left=l; jfm.char_type[t].down=x
17 function jfm.define_glue(b,a,w,st,sh)
19 if not jfm.glue[j] then jfm.glue[j]={} end
20 jfm.glue[j].width=w; jfm.glue[j].stretch=st;
23 function jfm.define_kern(b,a,w)
25 if not jfm.kern[j] then jfm.kern[j]=w end
28 -- procedures for \loadjfontmetric
29 ltj.metrics={} -- this table stores all metric informations
30 ltj.font_metric_table={}
32 local function search_metric(key)
33 for i,v in ipairs(ltj.metrics) do
34 if v.name==key then return i end
39 -- return nil iff ltj.metrics[ind] is a bad metric
40 local function consistency_check(ind)
41 local t = ltj.metrics[ind]
43 if t.dir~='yoko' then -- TODO: tate?
45 elseif type(t.zw)~='number' or type(t.zh)~='number' then
46 r=nil -- .zw, .zh must be present
48 local lbt = ltj.find_char_type('lindend',ind)
49 if lbt~=0 and t.char_type[lbt].chars~={'linebdd'} then
50 r=nil -- 'linebdd' must be isolated char_type
53 if not r then ltj.metrics[ind] = nil end
57 function ltj.load_jfont_metric()
59 ltj.error('no JFM specified',
60 {[1]='To load and define a Japanese font, the name of JFM must be specified.',
61 [2]="The JFM 'ujis' will be used for now."})
64 jfm.name=jfmfname .. ':' .. ltj.jfmvar
65 local i = search_metric(jfm.name)
67 if i then return i end
68 jfm.char_type={}; jfm.glue={}; jfm.kern={}
69 ltj.loadlua('jfm-' .. jfmfname .. '.lua')
71 t.dir=jfm.dir; t.zw=jfm.zw; t.zh=jfm.zh
72 t.char_type=jfm.char_type
73 t.glue=jfm.glue; t.kern=jfm.kern
74 table.insert(ltj.metrics,t)
75 return consistency_check(#ltj.metrics)
78 function ltj.find_char_type(c,m)
79 -- c: character code, m
80 if not ltj.metrics[m] then return 0 end
81 for i, v in pairs(ltj.metrics[m].char_type) do
83 for j,w in pairs(v.chars) do
84 if w==c then return i end
92 --====== \setjfont\CS={...:...;jfm=metric;...}
94 function ltj.jfontdefX(g)
95 local t = token.get_next()
96 ltj.cstemp=token.csname_name(t)
97 if g then ltj.is_global = '\\global' else ltj.is_global = '' end
98 tex.sprint('\\expandafter\\font\\csname ' .. ltj.cstemp .. '\\endcsname')
101 function ltj.jfontdefY() -- for horizontal font
102 local j=ltj.load_jfont_metric()
103 local fn=font.id(ltj.cstemp)
104 local f = font.fonts[fn]
106 ltj.error("bad JFM '" .. jfmfname .. "'",
107 {[1]='The JFM file you specified is not valid JFM file.',
108 [2]='Defining Japanese font is cancelled.'})
109 tex.sprint(ltj.is_global .. '\\expandafter\\let\\csname '
110 .. ltj.cstemp .. '\\endcsname=\\relax')
113 ltj.font_metric_table[fn]={}
114 ltj.font_metric_table[fn].jfm=j; ltj.font_metric_table[fn].size=f.size
115 tex.sprint(ltj.is_global .. '\\protected\\expandafter\\def\\csname '
116 .. ltj.cstemp .. '\\endcsname'
117 .. '{\\csname luatexja@curjfnt\\endcsname=' .. fn
118 .. ' \\zw=' .. tex.round(f.size*ltj.metrics[j].zw) .. 'sp'
119 .. '\\zh=' .. tex.round(f.size*ltj.metrics[j].zh) .. 'sp\\relax}')
122 local dr_orig = fonts.define.read
123 function fonts.define.read(name, size, id)
124 ltj.extract_metric(name)
125 -- In the present imple., we don't remove "jfm=..." from name.
126 local fontdata = dr_orig(name, size, id)
130 -- extract jfmfname and ltj.jfmvar
131 function ltj.extract_metric(name)
133 local tmp = utf.sub(basename, 1, 5)
136 if tmp == 'file:' or tmp == 'name:' or tmp == 'psft:' then
137 basename = utf.sub(basename, 6)
140 local p = utf.find(basename, ":")
142 basename = utf.sub(basename, p+1)
148 local q= utf.find(basename, ";",p+1) or utf.len(basename)+1
149 if utf.sub(basename,p,p+3)=='jfm=' and q>p+4 then
150 jfmfname = utf.sub(basename,p+4,q-1)
151 elseif utf.sub(basename,p,p+6)=='jfmvar=' and q>p+6 then
152 ltj.jfmvar = utf.sub(basename,p+7,q-1)
154 if utf.len(basename)+1==q then p=nil else p=q+1 end
160 --====== Range of Japanese characters.
161 -- jcr_table_main[chr_code] = index
162 -- index : internal 0, 1, 2, ..., 216 0: 'other'
163 -- external 1 2 216, (out of range): 'other'
166 local ucs_out = 0x110000
167 local jcr_table_main = {}
174 for i=0x100,ucs_out-1 do
178 function ltj.def_char_range(b,e,ind) -- ind: external range number
179 if ind<0 or ind>216 then
180 ltj.error('Invalid range number (' .. ind .. '), should be in the range 1..216.',
183 for i=math.max(0x80,b),math.min(ucs_out-1,e) do
184 jcr_table_main[i]=ind
188 local function get_char_jcrcode(p) -- for internal use
191 if c<0x80 then return jcr_noncjk else i=jcr_table_main[c] end
192 return math.floor(has_attr(p,
193 luatexbase.attributes['luatexja@kcat'..math.floor(i/31)])
194 /math.pow(2, i%31))%2
197 function ltj.get_char_jcrnumber(c) -- return the (external) range number
198 if c<0x80 or c>=ucs_out then return -1
200 local i = jcr_table_main[c] or 0
201 if i==0 then return 217 else return i end
205 function ltj.get_jcr_setting(i) -- i: internal range number
206 return math.floor(tex.getattribute(luatexbase.attributes['luatexja@kcat'..math.floor(i/31)])
207 /math.pow(2, i%31))%2
210 -- 和文文字と認識する unicode の範囲
211 function ltj.is_ucs_in_japanese_char(p)
212 return (get_char_jcrcode(p)~=jcr_noncjk)
215 function ltj.set_jchar_range(g, i) -- i: external range number
219 if i>0 then kc=0 else kc=1; i=-i end
220 if i>216 then i=0 end
221 local attr = luatexbase.attributes['luatexja@kcat'..math.floor(i/31)]
222 local a = tex.getattribute(attr)
223 local k = math.pow(2, i%31)
224 tex.setattribute(g,attr,(math.floor(a/k/2)*2+kc)*k+a%k)