summaryrefslogtreecommitdiff
path: root/macros/luatex/generic/luaotfload/luaotfload-multiscript.lua
blob: c2aa664fabcfa41ee6b3b594dc0ae32b210e2c67 (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
-----------------------------------------------------------------------
--         FILE:  luaotfload-multiscript.lua
--  DESCRIPTION:  part of luaotfload / multiscript
-----------------------------------------------------------------------

local ProvidesLuaModule = {
    name          = "luaotfload-multiscript",
    version       = "3.11",     --TAGVERSION
    date          = "2019-11-10", --TAGDATE
    description   = "luaotfload submodule / multiscript",
    license       = "GPL v2.0",
    author        = "Marcel Krüger"
}

if luatexbase and luatexbase.provides_module then
  luatexbase.provides_module (ProvidesLuaModule)
end

local nodenew            = node.direct.new
local getfont            = font.getfont
local setfont            = node.direct.setfont
local getwhd             = node.direct.getwhd
local insert_after       = node.direct.insert_after
local traverse_char      = node.direct.traverse_char
local protect_glyph      = node.direct.protect_glyph
local otffeatures        = fonts.constructors.newfeatures "otf"

local sep = lpeg.P' '^0 * ';' * lpeg.P' '^0
local codepoint = lpeg.S'0123456789ABCDEF'^4/function(c)return tonumber(c, 16)end
local codepoint_range = codepoint * ('..' * codepoint + lpeg.Cc(false))
local function multirawset(table, key1, key2, value)
  for key = key1,(key2 or key1) do
    rawset(table, key, value)
  end
  return table
end
local script_extensions do
  local entry = lpeg.Cg(codepoint_range * sep * lpeg.Ct((lpeg.C(lpeg.R'AZ' * lpeg.R'az'^1))^1 * ' ') * '#')^-1 * (1-lpeg.P'\n')^0 * '\n'
  local file = lpeg.Cf(
      lpeg.Ct''
    * entry^0
  , multirawset)

  local f = io.open(kpse.find_file"ScriptExtensions.txt")
  script_extensions = file:match(f:read'*a')
  f:close()
  for cp,t in next, script_extensions do
    for i=1,#t do
      t[t[i]] = true
    end
  end
end
local script_mapping do
  -- We could extract these from PropertyValueAliases.txt...
  local script_aliases = {
    Adlam = "Adlm", Caucasian_Albanian = "Aghb", Ahom = "Ahom", Arabic = "Arab",
    Imperial_Aramaic = "Armi", Armenian = "Armn", Avestan = "Avst",
    Balinese = "Bali", Bamum = "Bamu", Bassa_Vah = "Bass", Batak = "Batk",
    Bengali = "Beng", Bhaiksuki = "Bhks", Bopomofo = "Bopo", Brahmi = "Brah",
    Braille = "Brai", Buginese = "Bugi", Buhid = "Buhd", Chakma = "Cakm",
    Canadian_Aboriginal = "Cans", Carian = "Cari", Cham = "Cham",
    Cherokee = "Cher", Coptic = "Copt", Cypriot = "Cprt", Cyrillic = "Cyrl",
    Devanagari = "Deva", Dogra = "Dogr", Deseret = "Dsrt", Duployan = "Dupl",
    Egyptian_Hieroglyphs = "Egyp", Elbasan = "Elba", Elymaic = "Elym",
    Ethiopic = "Ethi", Georgian = "Geor", Glagolitic = "Glag",
    Gunjala_Gondi = "Gong", Masaram_Gondi = "Gonm", Gothic = "Goth",
    Grantha = "Gran", Greek = "Grek", Gujarati = "Gujr", Gurmukhi = "Guru",
    Hangul = "Hang", Han = "Hani", Hanunoo = "Hano", Hatran = "Hatr",
    Hebrew = "Hebr", Hiragana = "Hira", Anatolian_Hieroglyphs = "Hluw",
    Pahawh_Hmong = "Hmng", Nyiakeng_Puachue_Hmong = "Hmnp",
    Katakana_Or_Hiragana = "Hrkt", Old_Hungarian = "Hung", Old_Italic = "Ital",
    Javanese = "Java", Kayah_Li = "Kali", Katakana = "Kana",
    Kharoshthi = "Khar", Khmer = "Khmr", Khojki = "Khoj", Kannada = "Knda",
    Kaithi = "Kthi", Tai_Tham = "Lana", Lao = "Laoo", Latin = "Latn",
    Lepcha = "Lepc", Limbu = "Limb", Linear_A = "Lina", Linear_B = "Linb",
    Lisu = "Lisu", Lycian = "Lyci", Lydian = "Lydi", Mahajani = "Mahj",
    Makasar = "Maka", Mandaic = "Mand", Manichaean = "Mani", Marchen = "Marc",
    Medefaidrin = "Medf", Mende_Kikakui = "Mend", Meroitic_Cursive = "Merc",
    Meroitic_Hieroglyphs = "Mero", Malayalam = "Mlym", Modi = "Modi",
    Mongolian = "Mong", Mro = "Mroo", Meetei_Mayek = "Mtei", Multani = "Mult",
    Myanmar = "Mymr", Nandinagari = "Nand", Old_North_Arabian = "Narb",
    Nabataean = "Nbat", Newa = "Newa", Nko = "Nkoo", Nushu = "Nshu",
    Ogham = "Ogam", Ol_Chiki = "Olck", Old_Turkic = "Orkh", Oriya = "Orya",
    Osage = "Osge", Osmanya = "Osma", Palmyrene = "Palm", Pau_Cin_Hau = "Pauc",
    Old_Permic = "Perm", Phags_Pa = "Phag", Inscriptional_Pahlavi = "Phli",
    Psalter_Pahlavi = "Phlp", Phoenician = "Phnx", Miao = "Plrd",
    Inscriptional_Parthian = "Prti", Rejang = "Rjng", Hanifi_Rohingya = "Rohg",
    Runic = "Runr", Samaritan = "Samr", Old_South_Arabian = "Sarb",
    Saurashtra = "Saur", SignWriting = "Sgnw", Shavian = "Shaw",
    Sharada = "Shrd", Siddham = "Sidd", Khudawadi = "Sind", Sinhala = "Sinh",
    Sogdian = "Sogd", Old_Sogdian = "Sogo", Sora_Sompeng = "Sora",
    Soyombo = "Soyo", Sundanese = "Sund", Syloti_Nagri = "Sylo",
    Syriac = "Syrc", Tagbanwa = "Tagb", Takri = "Takr", Tai_Le = "Tale",
    New_Tai_Lue = "Talu", Tamil = "Taml", Tangut = "Tang", Tai_Viet = "Tavt",
    Telugu = "Telu", Tifinagh = "Tfng", Tagalog = "Tglg", Thaana = "Thaa",
    Thai = "Thai", Tibetan = "Tibt", Tirhuta = "Tirh", Ugaritic = "Ugar",
    Vai = "Vaii", Warang_Citi = "Wara", Wancho = "Wcho", Old_Persian = "Xpeo",
    Cuneiform = "Xsux", Yi = "Yiii", Zanabazar_Square = "Zanb",
    Inherited = "Zinh", Common = "Zyyy", Unknown = "Zzzz",
  }
  local entry = lpeg.Cg(codepoint_range * sep * ((lpeg.R'AZ' + lpeg.R'az' + '_')^1/script_aliases))^-1 * (1-lpeg.P'\n')^0 * '\n'
  -- local entry = lpeg.Cg(codepoint_range * sep * lpeg.Cc(true))^-1 * (1-lpeg.P'\n')^0 * '\n'
  local file = lpeg.Cf(
      lpeg.Ct''
    * entry^0
  , multirawset)

  local f = io.open(kpse.find_file"Scripts.txt")
  script_mapping = file:match(f:read'*a')
  f:close()
end

local additional_scripts_tables = { }

local additional_scripts_fonts = setmetatable({}, {
  __index = function(t, fid)
    local f = font.getfont(fid)
    -- table.tofile('myfont2', f)
    local res = f and f.additional_scripts or false
    t[fid] = res
    return res
  end,
})

local function makecombifont(tfmdata, _, additional_scripts)
  local basescript = tfmdata.properties.script
  local scripts = {basescript = false}
  additional_scripts = additional_scripts_tables[additional_scripts]
  for script, fontname in pairs(additional_scripts) do
    if script ~= basescript then
      local f = fonts.definers.read(fontname, tfmdata.size)
      local fid
      if type(f) == 'table' then
        fid = font.define(f)
      else
        error[[FIXME???]]
      end
      scripts[script] = {
        fid = fid,
        font = f,
        characters = f.characters,
      }
    end
  end
  tfmdata.additional_scripts = scripts
end

local glyph_id = node.id'glyph'
-- TODO: unset last_script, matching parentheses etc
function domultiscript(head, _, _, _, direction)
  head = node.direct.todirect(head)
  local last_fid, last_fonts, last_script
  for cur, cid, fid in traverse_char(head) do
    if fid ~= last_fid then
      last_fid, last_fonts, last_script = fid, additional_scripts_fonts[fid]
    end
    if last_fonts then
      local mapped_scr = script_mapping[cid]
      if mapped_scr == "Zinh" then
        mapped_scr = last_script
      else
        local additional_scr = script_extensions[cid]
        if additional_scripts then
          if additional_scripts[last_script] then
            mapped_scr = last_script
          elseif not last_fonts[mapped_scr] then
            for i = 1, #additional_scripts do
              if last_fonts[additional_scripts[i]] then
                mapped_scr = additional_scripts[i]
                break
              end
            end
          end
        elseif mapped_scr == "Zyyy" then
          mapped_scr = last_script
        end
      end
      last_script = mapped_scr
      local mapped_font = last_fonts[mapped_scr]
      if mapped_font then
        setfont(cur, mapped_font.fid)
      end
    end
  end
end

function luaotfload.add_multiscript(name, fonts)
  if fonts == nil then
    fonts = name
    name = #additional_scripts_fonts + 1
  end
  additional_scripts_tables[name] = fonts
  return name
end

otffeatures.register {
  name        = "multiscript",
  description = "Combine fonts for multiple scripts",
  manipulators = {
    node = makecombifont,
  },
  -- processors = { -- processors would be nice, but they are applied
  --                -- too late for our purposes
  --   node = donotdef,
  -- }
}

--- vim:sw=2:ts=2:expandtab:tw=71