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
|
local module = {
name = "harf-luaotfload",
description = "Harf luaotfload integration",
version = "0.4.2",
date = "2019-09-07",
license = "GPL v2.0"
}
luatexbase.provides_module(module)
if not pcall(require, "luaharfbuzz") then
luatexbase.module_error("harf", "'luaharfbuzz' module is required.")
end
local harf = require("harf")
local define_font = harf.callbacks.define_font
local readers = {
opentype = fonts.readers.opentype,
otf = fonts.readers.otf,
ttf = fonts.readers.ttf,
ttc = fonts.readers.ttc,
}
local function harf_reader(spec)
local features = spec.features and spec.features.raw
local mode = features and features.mode
if mode and mode ~= "harf" then
return readers[spec.forced](spec)
end
return define_font(spec)
end
-- Register font readers. We override the default ones to always use HarfBuzz
-- if no mode is explicitly set or when `mode=harf` is used, otherwise we
-- fallback to the old readers. Fonts we load will be shaped by the callbacks
-- we register below.
fonts.readers.harf = harf_reader
fonts.readers.opentype = harf_reader
fonts.readers.otf = harf_reader
fonts.readers.ttf = harf_reader
fonts.readers.ttc = harf_reader
local GSUBtag = harf.Tag.new("GSUB")
local GPOStag = harf.Tag.new("GPOS")
local dflttag = harf.Tag.new("dflt")
local aux = luaotfload.aux
local aux_provides_script = aux.provides_script
aux.provides_script = function(fontid, script)
local fontdata = font.getfont(fontid)
local hbdata = fontdata and fontdata.hb
if hbdata then
local hbshared = hbdata.shared
local hbface = hbshared.face
local script = harf.Tag.new(script)
for _, tag in next, { GSUBtag, GPOStag } do
local scripts = hbface:ot_layout_get_script_tags(tag) or {}
for i = 1, #scripts do
if script == scripts[i] then return true end
end
end
return false
end
return aux_provides_script(fontid, script)
end
local aux_provides_language = aux.provides_language
aux.provides_language = function(fontid, script, language)
local fontdata = font.getfont(fontid)
local hbdata = fontdata and fontdata.hb
if hbdata then
local hbshared = hbdata.shared
local hbface = hbshared.face
local script = harf.Tag.new(script)
-- fontspec seems to incorrectly use “DFLT” for language instead of “dflt”.
local language = harf.Tag.new(language == "DFLT" and "dflt" or language)
for _, tag in next, { GSUBtag, GPOStag } do
local scripts = hbface:ot_layout_get_script_tags(tag) or {}
for i = 1, #scripts do
if script == scripts[i] then
if language == dflttag then
-- By definition “dflt” language is always present.
return true
else
local languages = hbface:ot_layout_get_language_tags(tag, i - 1) or {}
for j = 1, #languages do
if language == languages[j] then return true end
end
end
end
end
end
return false
end
return aux_provides_language(fontid, script, language)
end
local aux_provides_feature = aux.provides_feature
aux.provides_feature = function(fontid, script, language, feature)
local fontdata = font.getfont(fontid)
local hbdata = fontdata and fontdata.hb
if hbdata then
local hbshared = hbdata.shared
local hbface = hbshared.face
local script = harf.Tag.new(script)
-- fontspec seems to incorrectly use “DFLT” for language instead of “dflt”.
local language = harf.Tag.new(language == "DFLT" and "dflt" or language)
local feature = harf.Tag.new(feature)
for _, tag in next, { GSUBtag, GPOStag } do
local _, script_idx = hbface:ot_layout_find_script(tag, script)
local _, language_idx = hbface:ot_layout_find_language(tag, script_idx, language)
if hbface:ot_layout_find_feature(tag, script_idx, language_idx, feature) then
return true
end
end
return false
end
return aux_provides_feature(fontid, script, language, feature)
end
local aux_font_has_glyph = aux.font_has_glyph
aux.font_has_glyph = function(fontid, codepoint)
local fontdata = font.getfont(fontid)
local hbdata = fontdata and fontdata.hb
if hbdata then
local hbshared = hbdata.shared
local unicodes = hbshared.unicodes
return unicodes[codepoint] ~= nil
end
return aux_font_has_glyph(fontid, codepoint)
end
local aux_slot_of_name = aux.slot_of_name
aux.slot_of_name = function(fontid, glyphname, unsafe)
local fontdata = font.getfont(fontid)
local hbdata = fontdata and fontdata.hb
if hbdata then
local hbshared = hbdata.shared
local hbfont = hbshared.font
local gid = hbfont:get_glyph_from_name(glyphname)
if gid ~= nil then
return gid + harf.CH_GID_PREFIX
end
return nil
end
return aux_slot_of_name(fontid, glyphname, unsafe)
end
local aux_name_of_slot = aux.name_of_slot
aux.name_of_slot = function(fontid, codepoint)
local fontdata = font.getfont(fontid)
local hbdata = fontdata and fontdata.hb
if hbdata then
local hbshared = hbdata.shared
local hbfont = hbshared.font
local characters = fontdata.characters
local character = characters[codepoint]
if character then
local gid = characters[codepoint].index
return hbfont:get_glyph_name(gid)
end
return nil
end
return aux_name_of_slot(fontid, codepoint)
end
-- luatexbase does not know how to handle `wrapup_run` callback, teach it.
luatexbase.callbacktypes.wrapup_run = 1 -- simple
luatexbase.callbacktypes.get_glyph_string = 1 -- simple
local base_callback_descriptions = luatexbase.callback_descriptions
local base_add_to_callback = luatexbase.add_to_callback
local base_remove_from_callback = luatexbase.remove_from_callback
-- Remove all existing functions from given callback, insert ours, then
-- reinsert the removed ones, so ours takes a priority.
local function add_to_callback(name, func)
local saved_callbacks = {}, ff, dd
for k, v in next, base_callback_descriptions(name) do
saved_callbacks[k] = { base_remove_from_callback(name, v) }
end
base_add_to_callback(name, func, "Harf "..name.." callback")
for _, v in next, saved_callbacks do
base_add_to_callback(name, v[1], v[2])
end
end
-- Register all Harf callbacks, except `define_font` which is handled above.
for name, func in next, harf.callbacks do
if name ~= "define_font" then
add_to_callback(name, func)
end
end
|