font-col.lua /size: 15 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
font-col
'
]
=
{
2
version
=
1
.
001
,
3
comment
=
"
companion to font-ini.mkiv
"
,
4
author
=
"
Hans Hagen, PRAGMA-ADE, Hasselt NL
"
,
5
copyright
=
"
PRAGMA ADE / ConTeXt Development Team
"
,
6
license
=
"
see context related readme files
"
7
}
8 9
-- possible optimization: delayed initialization of vectors
10
-- we should also share equal vectors (math)
11 12
local
context
,
commands
,
trackers
,
logs
=
context
,
commands
,
trackers
,
logs
13
local
node
,
nodes
,
fonts
,
characters
=
node
,
nodes
,
fonts
,
characters
14
local
file
,
lpeg
,
table
,
string
=
file
,
lpeg
,
table
,
string
15 16
local
type
,
next
,
tonumber
,
toboolean
=
type
,
next
,
tonumber
,
toboolean
17
local
gmatch
=
string
.
gmatch
18
local
fastcopy
=
table
.
fastcopy
19
local
formatters
=
string
.
formatters
20 21
local
nuts
=
nodes
.
nuts
22 23
local
setfont
=
nuts
.
setfont
24 25
----- traverse_char = nuts.traverse_char
26
local
nextchar
=
nuts
.
traversers
.
char
27 28
local
settings_to_hash
=
utilities
.
parsers
.
settings_to_hash
29 30
local
trace_collecting
=
false
trackers
.
register
(
"
fonts.collecting
"
,
function
(
v
)
trace_collecting
=
v
end
)
31 32
local
report_fonts
=
logs
.
reporter
(
"
fonts
"
,
"
collections
"
)
33 34
local
enableaction
=
nodes
.
tasks
.
enableaction
35
local
disableaction
=
nodes
.
tasks
.
disableaction
36 37
local
collections
=
fonts
.
collections
or
{
}
38
fonts
.
collections
=
collections
39 40
local
definitions
=
collections
.
definitions
or
{
}
41
collections
.
definitions
=
definitions
42 43
local
vectors
=
collections
.
vectors
or
{
}
44
collections
.
vectors
=
vectors
45 46
local
helpers
=
fonts
.
helpers
47
local
charcommand
=
helpers
.
commands
.
char
48
local
rightcommand
=
helpers
.
commands
.
right
49
local
addprivate
=
helpers
.
addprivate
50
local
hasprivate
=
helpers
.
hasprivate
51
local
fontpatternhassize
=
helpers
.
fontpatternhassize
52 53
local
hashes
=
fonts
.
hashes
54
local
fontdata
=
hashes
.
identifiers
55
local
fontquads
=
hashes
.
quads
56
local
chardata
=
hashes
.
characters
57
local
propdata
=
hashes
.
properties
58
local
mathparameters
=
hashes
.
mathparameters
59 60
local
currentfont
=
font
.
current
61
local
addcharacters
=
font
.
addcharacters
62 63
local
implement
=
interfaces
.
implement
64 65
local
list
=
{
}
66
local
current
=
0
67
local
enabled
=
false
68 69
local
validvectors
=
table
.
setmetatableindex
(
function
(
t
,
k
)
70
local
v
=
false
71
if
not
mathparameters
[
k
]
then
72
v
=
vectors
[
k
]
73
end
74
t
[
k
]
=
v
75
return
v
76
end
)
77 78
local
function
checkenabled
(
)
79
-- a bit ugly but nicer than a fuzzy state while defining math
80
if
next
(
vectors
)
then
81
if
not
enabled
then
82
enableaction
(
"
processors
"
,
"
fonts.collections.process
"
)
83
enabled
=
true
84
end
85
else
86
if
enabled
then
87
disableaction
(
"
processors
"
,
"
fonts.collections.process
"
)
88
enabled
=
false
89
end
90
end
91
end
92 93
collections
.
checkenabled
=
checkenabled
94 95
function
collections
.
reset
(
name
,
font
)
96
if
font
and
font
~
=
"
"
then
97
local
d
=
definitions
[
name
]
98
if
d
then
99
d
[
font
]
=
nil
100
if
not
next
(
d
)
then
101
definitions
[
name
]
=
nil
102
end
103
end
104
else
105
definitions
[
name
]
=
nil
106
end
107
end
108 109
function
collections
.
define
(
name
,
font
,
ranges
,
details
)
110
-- todo: details -> method=force|conditional rscale=
111
-- todo: remap=name
112
local
d
=
definitions
[
name
]
113
if
not
d
then
114
d
=
{
}
115
definitions
[
name
]
=
d
116
end
117
if
name
and
trace_collecting
then
118
report_fonts
(
"
extending collection %a using %a
"
,
name
,
font
)
119
end
120
details
=
settings_to_hash
(
details
)
121
-- todo, combine per font start/stop as arrays
122
local
offset
=
details
.
offset
123
if
type
(
offset
)
=
=
"
string
"
then
124
offset
=
characters
.
getrange
(
offset
,
true
)
or
false
125
else
126
offset
=
tonumber
(
offset
)
or
false
127
end
128
local
target
=
details
.
target
129
if
type
(
target
)
=
=
"
string
"
then
130
target
=
characters
.
getrange
(
target
,
true
)
or
false
131
else
132
target
=
tonumber
(
target
)
or
false
133
end
134
local
rscale
=
tonumber
(
details
.
rscale
)
or
1
135
local
force
=
toboolean
(
details
.
force
,
true
)
136
local
check
=
toboolean
(
details
.
check
,
true
)
137
local
factor
=
tonumber
(
details
.
factor
)
138
local
features
=
details
.
features
139
for
s
in
gmatch
(
ranges
,
"
[^, ]+
"
)
do
140
local
start
,
stop
,
description
,
gaps
=
characters
.
getrange
(
s
,
true
)
141
if
start
and
stop
then
142
if
trace_collecting
then
143
if
description
then
144
report_fonts
(
"
using range %a, slots %U - %U, description %a)
"
,
s
,
start
,
stop
,
description
)
145
end
146
for
i
=
1
,
#
d
do
147
local
di
=
d
[
i
]
148
if
(
start
>
=
di
.
start
and
start
<
=
di
.
stop
)
or
(
stop
>
=
di
.
start
and
stop
<
=
di
.
stop
)
then
149
report_fonts
(
"
overlapping ranges %U - %U and %U - %U
"
,
start
,
stop
,
di
.
start
,
di
.
stop
)
150
end
151
end
152
end
153
d
[
#
d
+
1
]
=
{
154
font
=
font
,
155
start
=
start
,
156
stop
=
stop
,
157
gaps
=
gaps
,
158
offset
=
offset
,
159
target
=
target
,
160
rscale
=
rscale
,
161
force
=
force
,
162
check
=
check
,
163
method
=
details
.
method
,
164
factor
=
factor
,
165
features
=
features
,
166
}
167
end
168
end
169
end
170 171
-- todo: provide a lua variant (like with definefont)
172 173
function
collections
.
registermain
(
name
)
174
local
last
=
currentfont
(
)
175
if
trace_collecting
then
176
report_fonts
(
"
registering font %a with name %a
"
,
last
,
name
)
177
end
178
list
[
#
list
+
1
]
=
last
179
end
180 181
-- check: when true, only set when present in font
182
-- force: when false, then not set when already set
183 184
local
uccodes
=
characters
.
uccodes
185
local
lccodes
=
characters
.
lccodes
186 187
local
methods
=
{
188
lowercase
=
function
(
oldchars
,
newchars
,
vector
,
start
,
stop
,
cloneid
)
189
for
k
,
v
in
next
,
oldchars
do
190
if
k
>
=
start
and
k
<
=
stop
then
191
local
lccode
=
lccodes
[
k
]
192
if
k
~
=
lccode
and
newchars
[
lccode
]
then
193
vector
[
k
]
=
{
cloneid
,
lccode
}
194
end
195
end
196
end
197
end
,
198
uppercase
=
function
(
oldchars
,
newchars
,
vector
,
start
,
stop
,
cloneid
)
199
for
k
,
v
in
next
,
oldchars
do
200
if
k
>
=
start
and
k
<
=
stop
then
201
local
uccode
=
uccodes
[
k
]
202
if
k
~
=
uccode
and
newchars
[
uccode
]
then
203
vector
[
k
]
=
{
cloneid
,
uccode
}
204
end
205
end
206
end
207
end
,
208
}
209 210
function
collections
.
clonevector
(
name
)
211
statistics
.
starttiming
(
fonts
)
212
if
trace_collecting
then
213
report_fonts
(
"
processing collection %a
"
,
name
)
214
end
215
local
definitions
=
definitions
[
name
]
216
local
vector
=
{
}
217
vectors
[
current
]
=
vector
218
for
i
=
1
,
#
definitions
do
219
local
definition
=
definitions
[
i
]
220
local
name
=
definition
.
font
221
local
start
=
definition
.
start
222
local
stop
=
definition
.
stop
223
local
check
=
definition
.
check
224
local
force
=
definition
.
force
225
local
offset
=
definition
.
offset
or
start
226
local
remap
=
definition
.
remap
-- not used
227
local
target
=
definition
.
target
228
local
method
=
definition
.
method
229
local
cloneid
=
list
[
i
]
230
local
oldchars
=
fontdata
[
current
]
.
characters
231
local
newchars
=
fontdata
[
cloneid
]
.
characters
232
local
factor
=
definition
.
factor
233
if
factor
then
234
vector
.
factor
=
factor
235
end
236
if
trace_collecting
then
237
if
target
then
238
report_fonts
(
"
remapping font %a to %a for range %U - %U, offset %X, target %U
"
,
current
,
cloneid
,
start
,
stop
,
offset
,
target
)
239
else
240
report_fonts
(
"
remapping font %a to %a for range %U - %U, offset %X
"
,
current
,
cloneid
,
start
,
stop
,
offset
)
241
end
242
end
243
if
method
then
244
method
=
methods
[
method
]
245
end
246
if
method
then
247
method
(
oldchars
,
newchars
,
vector
,
start
,
stop
,
cloneid
)
248
elseif
check
then
249
if
target
then
250
for
unicode
=
start
,
stop
do
251
local
unic
=
unicode
+
offset
-
start
252
if
not
newchars
[
target
]
then
253
-- not in font
254
elseif
force
or
(
not
vector
[
unic
]
and
not
oldchars
[
unic
]
)
then
255
vector
[
unic
]
=
{
cloneid
,
target
}
256
end
257
target
=
target
+
1
258
end
259
elseif
remap
then
260
-- not used
261
else
262
for
unicode
=
start
,
stop
do
263
local
unic
=
unicode
+
offset
-
start
264
if
not
newchars
[
unicode
]
then
265
-- not in font
266
elseif
force
or
(
not
vector
[
unic
]
and
not
oldchars
[
unic
]
)
then
267
vector
[
unic
]
=
cloneid
268
end
269
end
270
end
271
else
272
if
target
then
273
for
unicode
=
start
,
stop
do
274
local
unic
=
unicode
+
offset
-
start
275
if
force
or
(
not
vector
[
unic
]
and
not
oldchars
[
unic
]
)
then
276
vector
[
unic
]
=
{
cloneid
,
target
}
277
end
278
target
=
target
+
1
279
end
280
elseif
remap
then
281
for
unicode
=
start
,
stop
do
282
local
unic
=
unicode
+
offset
-
start
283
if
force
or
(
not
vector
[
unic
]
and
not
oldchars
[
unic
]
)
then
284
vector
[
unic
]
=
{
cloneid
,
remap
[
unicode
]
}
285
end
286
end
287
else
288
for
unicode
=
start
,
stop
do
289
local
unic
=
unicode
+
offset
-
start
290
if
force
or
(
not
vector
[
unic
]
and
not
oldchars
[
unic
]
)
then
291
vector
[
unic
]
=
cloneid
292
end
293
end
294
end
295
end
296
end
297
if
trace_collecting
then
298
report_fonts
(
"
activating collection %a for font %a
"
,
name
,
current
)
299
end
300
statistics
.
stoptiming
(
fonts
)
301
-- for WS: needs checking
302
if
validvectors
[
current
]
then
303
checkenabled
(
)
304
end
305
end
306 307
-- we already have this parser
308
--
309
-- local spec = (P("sa") + P("at") + P("scaled") + P("at") + P("mo")) * P(" ")^1 * (1-P(" "))^1 * P(" ")^0 * -1
310
-- local okay = ((1-spec)^1 * spec * Cc(true)) + Cc(false)
311
--
312
-- if lpegmatch(okay,name) then
313 314
function
collections
.
prepare
(
name
)
-- we can do this in lua now .. todo
315
current
=
currentfont
(
)
316
if
vectors
[
current
]
then
317
return
318
end
319
local
properties
=
propdata
[
current
]
320
local
mathsize
=
properties
.
mathsize
321
if
mathsize
=
=
1
or
mathsize
=
=
2
or
mathsize
=
=
3
then
322
return
323
end
324
local
d
=
definitions
[
name
]
325
if
d
then
326
if
trace_collecting
then
327
local
filename
=
file
.
basename
(
properties
.
filename
or
"
?
"
)
328
report_fonts
(
"
applying collection %a to %a, file %a
"
,
name
,
current
,
filename
)
329
end
330
list
=
{
}
331
context
.
pushcatcodes
(
"
prt
"
)
-- context.unprotect()
332
context
.
font_fallbacks_start_cloning
(
)
333
for
i
=
1
,
#
d
do
334
local
f
=
d
[
i
]
335
local
name
=
f
.
font
336
local
scale
=
f
.
rscale
or
1
337
if
fontpatternhassize
(
name
)
then
338
context
.
font_fallbacks_clone_unique
(
name
,
scale
)
339
else
340
context
.
font_fallbacks_clone_inherited
(
name
,
scale
)
341
end
342
context
.
font_fallbacks_register_main
(
name
)
343
end
344
context
.
font_fallbacks_prepare_clone_vectors
(
name
)
345
context
.
font_fallbacks_stop_cloning
(
)
346
context
.
popcatcodes
(
)
-- context.protect()
347
end
348
end
349 350
function
collections
.
report
(
message
)
351
if
trace_collecting
then
352
report_fonts
(
"
tex: %s
"
,
message
)
353
end
354
end
355 356
local
function
monoslot
(
font
,
char
,
parent
,
factor
)
357
local
tfmdata
=
fontdata
[
font
]
358
local
privatename
=
formatters
[
"
faked mono %s
"
]
(
char
)
359
local
privateslot
=
hasprivate
(
tfmdata
,
privatename
)
360
if
privateslot
then
361
return
privateslot
362
else
363
local
characters
=
tfmdata
.
characters
364
local
properties
=
tfmdata
.
properties
365
local
width
=
factor
*
fontquads
[
parent
]
366
local
character
=
characters
[
char
]
367
if
character
then
368
-- runtime patching of the font (can only be new characters)
369
-- instead of messing with existing dimensions
370
local
data
=
{
371
-- no features so a simple copy
372
width
=
width
,
373
height
=
character
.
height
,
374
depth
=
character
.
depth
,
375
commands
=
{
376
rightcommand
[
(
width
-
character
.
width
or
0
)
/
2
]
,
377
charcommand
[
char
]
,
378
}
379
}
380
local
u
=
addprivate
(
tfmdata
,
privatename
,
data
)
381
addcharacters
(
properties
.
id
,
{
382
type
=
"
real
"
,
383
characters
=
{
384
[
u
]
=
data
385
}
,
386
}
)
387
return
u
388
else
389
return
char
390
end
391
end
392
end
393 394
function
collections
.
process
(
head
)
-- this way we keep feature processing
395
for
n
,
char
,
font
in
nextchar
,
head
do
396
local
vector
=
validvectors
[
font
]
397
if
vector
then
398
local
vect
=
vector
[
char
]
399
if
not
vect
then
400
-- keep it
401
elseif
type
(
vect
)
=
=
"
table
"
then
402
local
newfont
=
vect
[
1
]
403
local
newchar
=
vect
[
2
]
404
if
trace_collecting
then
405
report_fonts
(
"
remapping character %C in font %a to character %C in font %a%s
"
,
406
char
,
font
,
newchar
,
newfont
,
not
chardata
[
newfont
]
[
newchar
]
and
"
(missing)
"
or
"
"
407
)
408
end
409
setfont
(
n
,
newfont
,
newchar
)
410
else
411
local
fakemono
=
vector
.
factor
412
if
trace_collecting
then
413
report_fonts
(
"
remapping font %a to %a for character %C%s
"
,
414
font
,
vect
,
char
,
not
chardata
[
vect
]
[
char
]
and
"
(missing)
"
or
"
"
415
)
416
end
417
if
fakemono
then
418
setfont
(
n
,
vect
,
monoslot
(
vect
,
char
,
font
,
fakemono
)
)
419
else
420
setfont
(
n
,
vect
)
421
end
422
end
423
end
424
end
425
return
head
426
end
427 428
function
collections
.
found
(
font
,
char
)
-- this way we keep feature processing
429
if
not
char
then
430
font
,
char
=
currentfont
(
)
,
font
431
end
432
if
chardata
[
font
]
[
char
]
then
433
return
true
-- in normal font
434
else
435
local
v
=
vectors
[
font
]
436
return
v
and
v
[
char
]
and
true
or
false
437
end
438
end
439 440
-- interface
441 442
implement
{
443
name
=
"
fontcollectiondefine
"
,
444
actions
=
collections
.
define
,
445
arguments
=
"
4 strings
"
,
446
}
447 448
implement
{
449
name
=
"
fontcollectionreset
"
,
450
actions
=
collections
.
reset
,
451
arguments
=
"
2 strings
"
,
452
}
453 454
implement
{
455
name
=
"
fontcollectionprepare
"
,
456
actions
=
collections
.
prepare
,
457
arguments
=
"
string
"
458
}
459 460
implement
{
461
name
=
"
fontcollectionreport
"
,
462
actions
=
collections
.
report
,
463
arguments
=
"
string
"
464
}
465 466
implement
{
467
name
=
"
fontcollectionregister
"
,
468
actions
=
collections
.
registermain
,
469
arguments
=
"
string
"
470
}
471 472
implement
{
473
name
=
"
fontcollectionclone
"
,
474
actions
=
collections
.
clonevector
,
475
arguments
=
"
string
"
476
}
477 478
implement
{
479
name
=
"
doifelsecharinfont
"
,
480
actions
=
{
collections
.
found
,
commands
.
doifelse
}
,
481
arguments
=
"
integer
"
482
}
483