summaryrefslogtreecommitdiff
path: root/macros/luatex/generic/luatexja/src/ltj-rmlgbm.lua
blob: 23d20be3f36ac8b6f4597ed56ed0ed738581c7a3 (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
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
--
-- ltj-rmlgbm.lua
--
luatexja.load_module 'base';      local ltjb = luatexja.base

local get_modtime
do
   local find_file = kpse.find_file
   local lfs = require"lfs"
   local file_attributes = lfs.attributes
   get_modtime = function (f)
      f = f and find_file(f, 'cmap files')
      return f and file_attributes(f, 'modification')
   end
end
local cidfont_data = {}
local cache_chars = {}
local cache_ver = 14
local sub = string.sub
local identifiers = fonts.hashes.identifiers

local cid_reg, cid_order, cid_supp, cid_name
local cid_replace = {
   ["Adobe-Japan1"] = {"UniJIS2004-UTF32", 23059, 7,
                       function (i)
                          if (231<=i and i<=632) or (8718<=i and i<=8719)
                             or (12063<=i and i<=12087) then
                             return 327680 -- 655360/2
                          elseif 9758<=i and i<=9778 then
                             return 218453 -- 655360/3
                          elseif 9738<=i and i<=9757 then
                             return 163840 -- 655360/4
                          end
                       end},
                       -- 基本的には JIS X 0213:2004 に沿ったマッピング
   ["Adobe-Korea1"] = {"UniKS-UTF32",  18351, 2,
                       function (i)
                          if 8094<=i and i<=8100 then
                             return 327680 -- 655360/2
                          end
                       end},
   ["Adobe-GB1"]    = {"UniGB-UTF32",  30571, 6,
                       function (i)
                          if (814<=i and i<=939) or (i==7716)
                             or (22355<=i and i<=22357) then
                             return 327680 -- 655360/2
                          end
                       end},
   ["Adobe-CNS1"]   = {"UniCNS-UTF32", 19178, 7,
                       function (i)
                          if (13648<=i and i<=13742) or (i==17603) then
                             return 327680 -- 655360/2
                          end
                       end},
   ["Adobe-KR"] = {"UniAKR-UTF32", 22896, 9,
                       function (i)
                          if i==3057 then
                             return 655360*2
                          elseif i==3058 then
                             return 655360*3
                          elseif i==12235 or i==12236 then
                             return 163840 -- 655360/4
                          end
                       end},
}

-- reading CID maps
local make_cid_font
do
   local line, fh -- line, file handler
   local tt,cidm -- characters, cid->(Unicode)

   local function load_cid_char(cid_dec, mke)
      local cid, ucs, ucsa
      line = fh:read("*l")
      while line do
         if string.find(line, "end...?char") then
            line = fh:read("*l"); return
         else -- WMA l is in the form "<%x+>%s%d+"
            ucs, cid = string.match(line, "<(%x+)>%s+<?(%x+)>?")
            cid = cid_dec(cid); ucs = tonumber(ucs, 16);
            if not tt[ucs]  then
               tt[ucs] = mke(cid); cidm[cid]=ucs
            end
         end
         line = fh:read("*l")
      end
   end

   local function load_cid_range(inc, cid_dec, mke)
      local bucs, eucs, cid
      line = fh:read("*l")
      while line do
        if string.find(line, "end...?range") then
            line = fh:read("*l"); return
         else -- WMA l is in the form "<%x+>%s+<%x+>"
            bucs, eucs, cid = string.match(line, "<(%x+)>%s+<(%x+)>%s+<?(%x+)>?")
            cid = cid_dec(cid);
            bucs = tonumber(bucs, 16); eucs = tonumber(eucs, 16)
            for ucs = bucs, eucs do
               if not tt[ucs]  then
                  tt[ucs] = mke(cid); cidm[cid]=ucs
               end
               cid = inc(cid)
            end
         end
         line = fh:read("*l")
      end
   end

   local function open_cmap_file(name, inc, cid_dec, mke)
      local fn = kpse.find_file(name, 'cmap files')
      if fn then
         fh = io.open(fn, "r")
         line = fh:read("*l")
         while line do
            if string.find(line, "%x+%s+begin...?char") then
               load_cid_char(cid_dec, mke)
            elseif string.find(line, "%x+%s+begin...?range") then
               load_cid_range(inc, cid_dec, mke)
            else
               line = fh:read("*l")
            end
         end
         fh:close();
      end
   end

   local function increment(a) return a+1 end
   local function entry(a)
      return {index = a}
   end
   local feat_dummy_vert = { gsub={vert={dflt={dflt=true}}} }
   local seq_dummy_vert={{
     features={vert={dflt={dflt=true}}},
     --flags={false,false,false,false},
     --index=1, name="s_s_0", skiphash=false, steps={coverage={},index=1},
     ["type"]="gsub_single", order='vert',
   }}
   make_cid_font = function ()
      local kx = cid_replace[cid_name]
      if not kx then return end
      local k = {
         cidinfo = { ordering=cid_order, registry=cid_reg, supplement=kx[3] },
         encodingbytes = 2, extend=1000, format = 'opentype',
         direction = 0, characters = {}, parameters = {
            ascender = 655360*0.88,
            descender = 655360*0.12,
         },
         embedding = "no", cache = "yes", factor = 0, hfactor = 0, vfactor = 0,
         tounicode = 1, filename="",
         properties = { language = "dflt", script = "dflt" },
      }
      cidfont_data[cid_name] = k

      -- CID => Unicode 符号空間
      local tth, cidmo = {}, {}
      tt, cidm = tth, cidmo
      for i = 0,kx[2] do cidm[i] = -1 end
      open_cmap_file(kx[1] .. "-H", increment, tonumber, entry)
      k.characters = tth

      -- Unicode にマップされなかった文字の処理
      -- これらは TrueType フォントを使って表示するときはおかしくなる
      local ttu, pricode = {}, 0xF0000
      for i,v in ipairs(cidmo) do
         if v==-1 then
            tth[pricode], cidmo[i], pricode
               = { index = i }, pricode, pricode+1;
         end
         ttu[i] = cidmo[i]
         ttu[cid_order .. '.' .. i] = cidmo[i]
      end

      -- shared
      k.shared = {
         otfdata = {
            cidinfo= k.cidinfo, verbose = false,
            shared = { featuredata = {}, },
         },
         dynamics = {}, processes = {},
         rawdata = {}, features={},
      }
      k.resources = {
         unicodes = ttu,
         features = feat_dummy_vert,
         sequences = seq_dummy_vert,
      }
      k.descriptions = {}
      cache_chars[cid_name]  = { [655360] = k.characters }

      -- 縦書用字形
      tt, cidm = {}, {}
      local ttv = {}; k.ltj_vert_table = ttv
      for i = 0,kx[2] do cidm[i] = -1 end
      open_cmap_file(kx[1] .. "-V", increment, tonumber, entry)
      for i,v in pairs(tt) do
         ttv[i] =  cidmo[v.index] -- "unicode" of vertical variant
      end

      -- tounicode エントリ
      local cidp = {nil, nil}; tt, ttu, cidm = {}, {}, {}
      open_cmap_file(cid_name .. "-UCS2",
                     function(a)
                        a[2] = a[2] +1 ; return a
                     end,
                     function(a)
                        cidp[1] = string.upper(string.sub(a,1,string.len(a)-4))
                        cidp[2] = tonumber(string.sub(a,-4),16)
                        return cidp
                     end,
                     function(a) return a[1] ..string.format('%04X',a[2])  end)
      -- tt は cid -> tounicode になっているので cidm -> tounicode に変換
      local kxf = kx[4]
      for i,v in ipairs(cidmo) do
         k.characters[v].width = kxf(i)
         if v>=0xF0000 then
            k.characters[v].tounicode = tt[i]
         end
      end

      -- Save
      if k and k.characters and k.characters[46] then
        k.characters[46].width = math.floor(655360/14);
      end
      ltjb.save_cache("ltj-cid-auto-" .. string.lower(cid_name),
        {version = cache_ver,
         cid2ucs_modtime = get_modtime(cid_name..'-UCS2'),
         ucs2cid = kx[1]..'-H',
         ucs2cid_modtime = get_modtime(kx[1]..'-H'),
         k})
      k.shared.rawdata.resources=k.resources
      k.shared.rawdata.descriptions=k.descriptions
   end
end

--
local cidf_vert_processor
do
   local traverse_glyph, is_node = node.direct.traverse_glyph, node.is_node
   local to_direct = node.direct.todirect
   local id_glyph = node.id 'glyph'
   local getfont = node.direct.getfont
   local getchar = node.direct.getchar
   local setchar = node.direct.setchar
   local font_getfont = font.getfont
   cidf_vert_processor = {
      function (head, fnum)
         local fontdata = font_getfont(fnum)
         if head and luatexja.jfont.font_metric_table[fnum] and luatexja.jfont.font_metric_table[fnum].vert_activated then
            local vt = fontdata.ltj_vert_table
            local nh = is_node(head) and to_direct(head) or head
            for n in traverse_glyph(head) do
               local c = getchar(n)
               if getfont(n)==fnum then setchar(n, vt[c] or c) end
            end
            return head, false
         end
      end
   }
end

local dummy_vht, dummy_vorg = {}, {}
setmetatable(dummy_vht, {__index = function () return 1 end } )
setmetatable(dummy_vorg, {__index = function () return 0.88 end } )
local function cid_cache_outdated(t)
  return (t.version~=cache_ver)
    or (t.cid2ucs_modtime ~= get_modtime(cid_name..'-UCS2'))
    or (t.ucs2cid_modtime ~= get_modtime(t.ucs2cid))
end
local function read_cid_font()
   local dat = ltjb.load_cache("ltj-cid-auto-" .. string.lower(cid_name),
                               cid_cache_outdated)
   if dat then
      dat[1].shared.rawdata.resources=dat[1].resources
      dat[1].shared.rawdata.descriptions=dat[1].descriptions
      cidfont_data[cid_name] = dat[1]
      cache_chars[cid_name]  = { [655360] = cidfont_data[cid_name].characters }
   else
      -- Now we must create the virtual metrics from CMap.
      make_cid_font()
   end
   if cidfont_data[cid_name] then
      cidfont_data[cid_name].shared.processes = cidf_vert_processor
      cidfont_data[cid_name].resources.ltj_extra
        = { ind_to_uni = cidfont_data[cid_name].resources.unicodes,
            vheight = dummy_vht, vorigin = dummy_vorg }
      for i,v in pairs(cidfont_data[cid_name].characters) do
         if not v.width then v.width = 655360 end
         v.height, v.depth = 576716.8, 78643.2 -- optimized for jfm-ujis.lua
      end
      return cidfont_data[cid_name]
   else
      return nil
   end
end

-- High-level
local function prepare_cid_font(reg, ord)
   cid_reg, cid_order, cid_name, cid_supp = reg, ord, reg .. '-' .. ord
   return cidfont_data[cid_name] or read_cid_font()
end

local definers = fonts.definers
local function mk_rml(name, size, id)
   local specification = definers.analyze(name,size)
   --specification = definers.resolve(specification) (not needed)
   specification.detail = specification.detail or ''
   do
      local n = specification.name
      if sub(n,1,1)=="{" then n=sub(n,2) end
      if sub(n,-1)=="}" then  n=sub(n,1,-2) end
      specification.name=n
   end
   local fontdata = {}
   local cachedata = {}
   local s = cidfont_data[cid_name]
   for k, v in pairs(s) do
      fontdata[k] = v
      cachedata[k] = v
   end
   fontdata.characters = nil
   cachedata.characters = nil
   fontdata.shared = nil
   cachedata.shared = nil
   if s.shared then
      cachedata.shared = {}
      local shared = cachedata.shared
      for k, v in pairs(s.shared) do
         shared[k] = v
      end
   end

   -- characters & scaling
   if size < 0 then size = -size * 655.36 end
   local scale = size / 655360

   do
      local def_height =  0.88 * size
      -- character's default height (optimized for jfm-ujis.lua)
      local def_depth =  0.12 * size  -- and depth.
      if not cache_chars[cid_name][size] then
         cache_chars[cid_name][size]  = {}
         for k, v in pairs(cache_chars[cid_name][655360]) do
            cache_chars[cid_name][size][k] = {
               index = v.index, width = v.width * scale,
               height = def_height, depth = def_depth, tounicode = v.tounicode,
            }
         end
      end
      fontdata.characters = cache_chars[cid_name][size]
      cachedata.characters = cache_chars[cid_name][size]
   end
   luatexja.rmlgbm.vert_addfunc(id, fontdata)

   -- other parameters
   do
      local parameters = {}
      for k, v in pairs(s.parameters) do  parameters[k] = v * scale end
      fontdata.parameters  = parameters; fontdata.size  = size; fontdata.resources  = s.resources
      cachedata.parameters = parameters; cachedata.size = size; cachedata.resources = s.resources
   end

   -- no embedding
   local var = ''
   local s = string.match(specification.detail, 'slant=([+-]*%d*%.?%d)')
   if s and e~=0  then
      s = s * 1000
      var, fontdata.slant  = var .. 's' .. tostring(s), s
   end
   local e = string.match(specification.detail, 'extend=([+-]*%d*%.?%d)')
   if e and e~=1  then
      e = e * 1000
      var, fontdata.extend  = var .. 'x' .. tostring(e), e
   end
   fontdata.name = specification.name .. size .. var; cachedata.name = fontdata.name
   fontdata.fullname = specification.name .. var; cachedata.fullname = fontdata.fullname
   fontdata.psname = specification.name; cachedata.psname = fontdata.psname
   identifiers[id] = cachedata

   return fontdata
end

local function font_callback(name, size, id, fallback)
   if sub(name,1,1)=="{" and sub(name,-1)=="}" then name = sub(name,2,-2) end
   local p = name:find(":") or 0
   if sub(name,1, p-1) == 'psft' then
      local s = "Adobe-Japan1-7"
      local basename = sub(name,p+1)
      local p = basename:find(":")
      local q = basename:find("/[BI][BI]?")
      if q and p and q<=p then
         basename = basename:gsub('/[BI][BI]?', '', 1)
         p = basename:find(":")
      end
      if p then
         local xname = sub(basename,p+1)
         p = 1
         while p do
            local q = xname:find(";", p+1) or xname:len()+1
            if sub(xname,p, p+3)=='cid=' and q>p+4 then
               s = sub(xname,p+4, q-1)
            end
            if xname:len()+1==q then p = nil else p = q + 1 end
         end
      end
      cid_reg, cid_order = string.match(s, "^(.-)%-(.-)%-(%d-)$")
      if not cid_reg then
         cid_reg, cid_order = string.match(s, "^(.-)%-(.-)$")
      end
      if not prepare_cid_font(cid_reg, cid_order) then
         ltjb.package_error('luatexja',
                            "bad cid key `" .. s .. "'",
                            "I couldn't find any non-embedded font information for the CID\n" ..
                            '`' .. s .. "'. For now, I'll use `Adobe-Japan1-6'.\n"..
                            'Please contact the LuaTeX-ja project team.')
         cid_name = "Adobe-Japan1"
      end
      return mk_rml(basename, size, id)
   else
      local fontdata=fallback(name, size, id)
      if type (fontdata) == "table" and fontdata.encodingbytes == 2 then
        luatexbase.call_callback ("luaotfload.patch_font", fontdata, name, id)
      else
        luatexbase.call_callback ("luaotfload.patch_font_unsafe", fontdata, name, id)
      end
      return fontdata
   end
end

luatexja.rmlgbm = {
   prepare_cid_font = prepare_cid_font,
   cidfont_data = cidfont_data,
   font_callback = font_callback,
   vert_addfunc = function () end, -- dummy, set in ltj-direction.lua
}

prepare_cid_font('Adobe', 'Japan1')