typo-cap.lua /size: 19 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
typo-cap
'
]
=
{
2
version
=
1
.
001
,
3
comment
=
"
companion to typo-cap.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
local
next
,
type
,
tonumber
=
next
,
type
,
tonumber
10
local
format
,
insert
=
string
.
format
,
table
.
insert
11
local
div
,
getrandom
=
math
.
div
,
utilities
.
randomizer
.
get
12 13
local
trace_casing
=
false
trackers
.
register
(
"
typesetters.casing
"
,
function
(
v
)
trace_casing
=
v
end
)
14
local
check_kerns
=
true
directives
.
register
(
"
typesetters.casing.checkkerns
"
,
function
(
v
)
check_kerns
=
v
end
)
15 16
local
report_casing
=
logs
.
reporter
(
"
typesetting
"
,
"
casing
"
)
17 18
local
nodes
,
node
=
nodes
,
node
19 20
local
nuts
=
nodes
.
nuts
21 22
local
getnext
=
nuts
.
getnext
23
local
getprev
=
nuts
.
getprev
24
local
getid
=
nuts
.
getid
25
----- getattr = nuts.getattr
26
local
takeattr
=
nuts
.
takeattr
27
local
getfont
=
nuts
.
getfont
28
local
getsubtype
=
nuts
.
getsubtype
29
local
getchar
=
nuts
.
getchar
30
local
isglyph
=
nuts
.
isglyph
31
local
getdisc
=
nuts
.
getdisc
32 33
local
setattr
=
nuts
.
setattr
34
local
setchar
=
nuts
.
setchar
35
local
setfont
=
nuts
.
setfont
36 37
local
copy_node
=
nuts
.
copy
38
local
end_of_math
=
nuts
.
end_of_math
39
local
insert_after
=
nuts
.
insert_after
40
local
find_attribute
=
nuts
.
find_attribute
41 42
local
nextglyph
=
nuts
.
traversers
.
glyph
43 44
local
nodecodes
=
nodes
.
nodecodes
45
local
kerncodes
=
nodes
.
kerncodes
46 47
local
glyph_code
=
nodecodes
.
glyph
48
local
kern_code
=
nodecodes
.
kern
49
local
disc_code
=
nodecodes
.
disc
50
local
math_code
=
nodecodes
.
math
51 52
local
fontkern_code
=
kerncodes
.
fontkern
53 54
local
enableaction
=
nodes
.
tasks
.
enableaction
55 56
local
newkern
=
nuts
.
pool
.
kern
57 58
local
fonthashes
=
fonts
.
hashes
59
local
fontdata
=
fonthashes
.
identifiers
60
local
fontchar
=
fonthashes
.
characters
61 62
local
variables
=
interfaces
.
variables
63
local
v_reset
=
variables
.
reset
64 65
local
texsetattribute
=
tex
.
setattribute
66
local
unsetvalue
=
attributes
.
unsetvalue
67 68
typesetters
=
typesetters
or
{
}
69
local
typesetters
=
typesetters
70 71
typesetters
.
cases
=
typesetters
.
cases
or
{
}
72
local
cases
=
typesetters
.
cases
73 74
cases
.
actions
=
{
}
75
local
actions
=
cases
.
actions
76
local
a_cases
=
attributes
.
private
(
"
case
"
)
77 78
local
extract
=
bit32
.
extract
79
local
run
=
0
-- a trick to make neighbouring ranges work
80
local
blocked
=
{
}
81 82
local
function
set
(
tag
,
font
)
83
if
run
=
=
0x40
then
-- 2^6
84
run
=
1
85
else
86
run
=
run
+
1
87
end
88
local
a
=
font
*
0x10000
+
tag
*
0x100
+
run
89
blocked
[
a
]
=
false
90
return
a
91
end
92 93
local
function
get
(
a
)
94
return
95
extract
(
a
,
8
,
8
)
,
-- tag
96
extract
(
a
,
16
,
12
)
,
-- font
97
extract
(
a
,
0
,
8
)
-- run
98
end
99 100
-- a previous implementation used char(0) as placeholder for the larger font, so we needed
101
-- to remove it before it can do further harm ... that was too tricky as we use char 0 for
102
-- other cases too
103
--
104
-- we could do the whole glyph run here (till no more attributes match) but then we end up
105
-- with more code .. maybe i will clean this up anyway as the lastfont hack is somewhat ugly
106
-- ... on the other hand, we need to deal with cases like:
107
--
108
-- \WORD {far too \Word{many \WORD{more \word{pushed} in between} useless} words}
109 110
local
uccodes
=
characters
.
uccodes
111
local
lccodes
=
characters
.
lccodes
112
local
categories
=
characters
.
categories
113 114
-- true false true == mixed
115 116
local
function
replacer
(
start
,
codes
)
117
local
char
,
fnt
=
isglyph
(
start
)
118
local
dc
=
codes
[
char
]
119
if
dc
then
120
local
ifc
=
fontchar
[
fnt
]
121
if
type
(
dc
)
=
=
"
table
"
then
122
for
i
=
1
,
#
dc
do
123
if
not
ifc
[
dc
[
i
]
]
then
124
return
start
,
false
125
end
126
end
127
for
i
=
#
dc
,
1
,
-1
do
128
local
chr
=
dc
[
i
]
129
if
i
=
=
1
then
130
setchar
(
start
,
chr
)
131
else
132
local
g
=
copy_node
(
start
)
133
setchar
(
g
,
chr
)
134
insert_after
(
start
,
start
,
g
)
135
end
136
end
137
elseif
ifc
[
dc
]
then
138
setchar
(
start
,
dc
)
139
end
140
end
141
return
start
142
end
143 144
local
registered
,
n
=
{
}
,
0
145 146
local
function
register
(
name
,
f
)
147
if
type
(
f
)
=
=
"
function
"
then
148
n
=
n
+
1
149
actions
[
n
]
=
f
150
registered
[
name
]
=
n
151
return
n
152
else
153
local
n
=
registered
[
f
]
154
registered
[
name
]
=
n
155
return
n
156
end
157
end
158 159
cases
.
register
=
register
160 161
local
function
WORD
(
start
,
attr
,
lastfont
,
n
,
count
,
where
,
first
)
162
lastfont
[
n
]
=
false
163
return
replacer
(
first
or
start
,
uccodes
)
164
end
165 166
local
function
word
(
start
,
attr
,
lastfont
,
n
,
count
,
where
,
first
)
167
lastfont
[
n
]
=
false
168
return
replacer
(
first
or
start
,
lccodes
)
169
end
170 171
local
function
Words
(
start
,
attr
,
lastfont
,
n
,
count
,
where
,
first
)
-- looks quite complex
172
if
where
=
=
"
post
"
then
173
return
174
end
175
if
count
=
=
1
and
where
~
=
"
post
"
then
176
replacer
(
first
or
start
,
uccodes
)
177
return
start
,
true
178
else
179
return
start
,
true
180
end
181
end
182 183
local
function
Word
(
start
,
attr
,
lastfont
,
n
,
count
,
where
,
first
)
184
blocked
[
attr
]
=
true
185
return
Words
(
start
,
attr
,
lastfont
,
n
,
count
,
where
,
first
)
186
end
187 188
local
function
camel
(
start
,
attr
,
lastfont
,
n
,
count
,
where
,
first
)
189
word
(
start
,
attr
,
lastfont
,
n
,
count
,
where
,
first
)
190
Words
(
start
,
attr
,
lastfont
,
n
,
count
,
where
,
first
)
191
return
start
,
true
192
end
193 194
-- local function mixed(start,attr,lastfont,n,count,where,first)
195
-- if where == "post" then
196
-- return
197
-- end
198
-- local used = first or start
199
-- local char = getchar(first)
200
-- local dc = uccodes[char]
201
-- if not dc then
202
-- -- quit
203
-- elseif dc == char then
204
-- local lfa = lastfont[n]
205
-- if lfa then
206
-- setfont(first,lfa)
207
-- end
208
-- else
209
-- replacer(first or start,uccodes)
210
-- end
211
-- return start, true
212
-- end
213 214
local
function
mixed
(
start
,
attr
,
lastfont
,
n
,
count
,
where
,
first
)
215
if
where
=
=
"
post
"
then
216
return
217
end
218
local
used
=
first
or
start
219
local
char
=
getchar
(
used
)
220
local
dc
=
uccodes
[
char
]
221
if
not
dc
then
222
-- quit
223
elseif
dc
=
=
char
then
224
local
lfa
=
lastfont
[
n
]
225
if
lfa
then
226
setfont
(
used
,
lfa
)
227
end
228
elseif
check_kerns
then
229
local
p
=
getprev
(
used
)
230
if
p
and
getid
(
p
)
=
=
glyph_code
then
231
local
c
=
lccodes
[
char
]
232
local
c
=
type
(
c
)
=
=
"
table
"
and
c
[
1
]
or
c
233
replacer
(
used
,
uccodes
)
234
local
fp
=
getfont
(
p
)
235
local
fc
=
getfont
(
used
)
236
if
fp
~
=
fc
then
237
local
k
=
fonts
.
getkern
(
fontdata
[
fp
]
,
getchar
(
p
)
,
c
)
238
if
k
~
=
0
then
239
insert_after
(
p
,
p
,
newkern
(
k
)
)
240
end
241
end
242
else
243
replacer
(
used
,
uccodes
)
244
end
245
else
246
replacer
(
used
,
uccodes
)
247
end
248
return
start
,
true
249
end
250 251
local
function
Capital
(
start
,
attr
,
lastfont
,
n
,
count
,
where
,
first
,
once
)
-- 3
252
local
used
=
first
or
start
253
if
count
=
=
1
and
where
~
=
"
post
"
then
254
local
lfa
=
lastfont
[
n
]
255
if
lfa
then
256
local
dc
=
uccodes
[
getchar
(
used
)
]
257
if
dc
then
258
setfont
(
used
,
lfa
)
259
end
260
end
261
end
262
local
s
,
c
=
replacer
(
first
or
start
,
uccodes
)
263
if
once
then
264
lastfont
[
n
]
=
false
-- here
265
end
266
return
start
,
c
267
end
268 269
local
function
capital
(
start
,
attr
,
lastfont
,
n
,
where
,
count
,
first
,
count
)
-- 4
270
return
Capital
(
start
,
attr
,
lastfont
,
n
,
where
,
count
,
first
,
true
)
271
end
272 273
local
function
none
(
start
,
attr
,
lastfont
,
n
,
count
,
where
,
first
)
274
return
start
,
true
275
end
276 277
local
function
randomized
(
start
,
attr
,
lastfont
,
n
,
count
,
where
,
first
)
278
local
used
=
first
or
start
279
local
char
=
getchar
(
used
)
280
local
font
=
getfont
(
used
)
281
local
tfm
=
fontchar
[
font
]
282
lastfont
[
n
]
=
false
283
local
kind
=
categories
[
char
]
284
if
kind
=
=
"
lu
"
then
285
while
true
do
286
local
n
=
getrandom
(
"
capital lu
"
,
0x41
,
0x5A
)
287
if
tfm
[
n
]
then
-- this also intercepts tables
288
setchar
(
used
,
n
)
289
return
start
290
end
291
end
292
elseif
kind
=
=
"
ll
"
then
293
while
true
do
294
local
n
=
getrandom
(
"
capital ll
"
,
0x61
,
0x7A
)
295
if
tfm
[
n
]
then
-- this also intercepts tables
296
setchar
(
used
,
n
)
297
return
start
298
end
299
end
300
end
301
return
start
302
end
303 304
register
(
variables
.
WORD
,
WORD
)
-- 1
305
register
(
variables
.
word
,
word
)
-- 2
306
register
(
variables
.
Word
,
Word
)
-- 3
307
register
(
variables
.
Words
,
Words
)
-- 4
308
register
(
variables
.
capital
,
capital
)
-- 5
309
register
(
variables
.
Capital
,
Capital
)
-- 6
310
register
(
variables
.
none
,
none
)
-- 7 (dummy)
311
register
(
variables
.
random
,
randomized
)
-- 8
312
register
(
variables
.
mixed
,
mixed
)
-- 9
313
register
(
variables
.
camel
,
camel
)
-- 10
314 315
register
(
variables
.
cap
,
variables
.
capital
)
-- clone
316
register
(
variables
.
Cap
,
variables
.
Capital
)
-- clone
317 318
-- This can be more clever: when we unset we can actually use the same attr ref if
319
-- needed. Using properties to block further usage is not faster.
320 321
function
cases
.
handler
(
head
)
-- not real fast but also not used on much data
322
local
start
=
head
323
local
lastfont
=
{
}
324
local
lastattr
=
nil
325
local
count
=
0
326
local
previd
=
nil
327
local
prev
=
nil
328
while
start
do
-- while because start can jump ahead
329
local
id
=
getid
(
start
)
330
if
id
=
=
glyph_code
then
331
-- local attr = getattr(start,a_cases)
332
local
attr
=
takeattr
(
start
,
a_cases
)
333
if
attr
and
attr
>
0
and
not
blocked
[
attr
]
then
334
if
attr
~
=
lastattr
then
335
lastattr
=
attr
336
count
=
1
337
else
338
count
=
count
+
1
339
end
340
-- setattr(start,a_cases,unsetvalue) -- not needed
341
local
n
,
id
,
m
=
get
(
attr
)
342
if
lastfont
[
n
]
=
=
nil
then
343
lastfont
[
n
]
=
id
344
end
345
local
action
=
actions
[
n
]
-- map back to low number
346
if
action
then
347
local
quit
348
start
,
quit
=
action
(
start
,
attr
,
lastfont
,
n
,
count
)
349
if
trace_casing
then
350
report_casing
(
"
case trigger %a, instance %a, fontid %a, result %a
"
,
n
,
m
,
id
,
quit
and
"
-
"
or
"
+
"
)
351
end
352
elseif
trace_casing
then
353
report_casing
(
"
unknown case trigger %a
"
,
n
)
354
end
355
end
356
elseif
id
=
=
disc_code
then
357
-- local attr = getattr(start,a_cases)
358
local
attr
=
takeattr
(
start
,
a_cases
)
359
if
attr
and
attr
>
0
and
not
blocked
[
attr
]
then
360
if
attr
~
=
lastattr
then
361
lastattr
=
attr
362
count
=
0
363
end
364
-- setattr(start,a_cases,unsetvalue) -- not needed
365
local
n
,
id
,
m
=
get
(
attr
)
366
if
lastfont
[
n
]
=
=
nil
then
367
lastfont
[
n
]
=
id
368
end
369
local
action
=
actions
[
n
]
-- map back to low number
370
if
action
then
371
local
pre
,
post
,
replace
=
getdisc
(
start
)
372
if
replace
then
373
local
cnt
=
count
374
for
g
in
nextglyph
,
replace
do
375
cnt
=
cnt
+
1
376
takeattr
(
g
,
a_cases
)
377
-- setattr(g,a_cases,unsetvalue)
378
local
h
,
quit
=
action
(
start
,
attr
,
lastfont
,
n
,
cnt
,
"
replace
"
,
g
)
379
if
quit
then
380
break
381
end
382
end
383
end
384
if
pre
then
385
local
cnt
=
count
386
for
g
in
nextglyph
,
pre
do
387
cnt
=
cnt
+
1
388
takeattr
(
g
,
a_cases
)
389
-- setattr(g,a_cases,unsetvalue)
390
local
h
,
quit
=
action
(
start
,
attr
,
lastfont
,
n
,
cnt
,
"
pre
"
,
g
)
391
if
quit
then
392
break
393
end
394
end
395
end
396
if
post
then
397
local
cnt
=
count
398
for
g
in
nextglyph
,
post
do
399
cnt
=
cnt
+
1
400
takeattr
(
g
,
a_cases
)
401
-- setattr(g,a_cases,unsetvalue)
402
local
h
,
quit
=
action
(
start
,
attr
,
lastfont
,
n
,
cnt
,
"
post
"
,
g
)
403
if
quit
then
404
break
405
end
406
end
407
end
408
end
409
count
=
count
+
1
410
end
411
elseif
id
=
=
math_code
then
412
start
=
end_of_math
(
start
)
413
count
=
0
414
elseif
prev_id
=
=
kern_code
and
getsubtype
(
prev
)
=
=
fontkern_code
then
415
-- still inside a word ...normally kerns are added later
416
else
417
count
=
0
418
end
419
if
start
then
420
prev
=
start
421
previd
=
id
422
start
=
getnext
(
start
)
423
end
424
end
425
return
head
426
end
427 428
-- function cases.handler(head) -- not real fast but also not used on much data
429
-- local attr, start = find_attribute(head,a_cases)
430
-- if not start then
431
-- return head, false
432
-- end
433
-- local lastfont = { }
434
-- local lastattr = nil
435
-- local count = 0
436
-- local previd = nil
437
-- local prev = nil
438
-- while start do
439
-- while start do -- while because start can jump ahead
440
-- local id = getid(start)
441
-- if id == glyph_code then
442
-- -- local attr = getattr(start,a_cases)
443
-- local attr = takeattr(start,a_cases)
444
-- if attr and attr > 0 and not blocked[attr] then
445
-- if attr ~= lastattr then
446
-- lastattr = attr
447
-- count = 1
448
-- else
449
-- count = count + 1
450
-- end
451
-- -- setattr(start,a_cases,unsetvalue) -- not needed
452
-- local n, id, m = get(attr)
453
-- if lastfont[n] == nil then
454
-- lastfont[n] = id
455
-- end
456
-- local action = actions[n] -- map back to low number
457
-- if action then
458
-- start = action(start,attr,lastfont,n,count)
459
-- if trace_casing then
460
-- report_casing("case trigger %a, instance %a, fontid %a, result %a",n,m,id,ok)
461
-- end
462
-- elseif trace_casing then
463
-- report_casing("unknown case trigger %a",n)
464
-- end
465
-- end
466
-- elseif id == disc_code then
467
-- -- local attr = getattr(start,a_cases)
468
-- local attr = takeattr(start,a_cases)
469
-- if attr and attr > 0 and not blocked[attr] then
470
-- if attr ~= lastattr then
471
-- lastattr = attr
472
-- count = 0
473
-- end
474
-- -- setattr(start,a_cases,unsetvalue) -- not needed
475
-- local n, id, m = get(attr)
476
-- if lastfont[n] == nil then
477
-- lastfont[n] = id
478
-- end
479
-- local action = actions[n] -- map back to low number
480
-- if action then
481
-- local pre, post, replace = getdisc(start)
482
-- if replace then
483
-- local cnt = count
484
-- for g in glyph_code, replace do
485
-- cnt = cnt + 1
486
-- takeattr(g,a_cases)
487
-- -- setattr(g,a_cases,unsetvalue)
488
-- local h, quit = action(start,attr,lastfont,n,cnt,"replace",g)
489
-- if quit then
490
-- break
491
-- end
492
-- end
493
-- end
494
-- if pre then
495
-- local cnt = count
496
-- for g in nextglyph, pre do
497
-- cnt = cnt + 1
498
-- takeattr(g,a_cases)
499
-- -- setattr(g,a_cases,unsetvalue)
500
-- local h, quit = action(start,attr,lastfont,n,cnt,"pre",g)
501
-- if quit then
502
-- break
503
-- end
504
-- end
505
-- end
506
-- if post then
507
-- local cnt = count
508
-- for g in nextglyph, post do
509
-- cnt = cnt + 1
510
-- takeattr(g,a_cases)
511
-- -- setattr(g,a_cases,unsetvalue)
512
-- local h, quit = action(start,attr,lastfont,n,cnt,"post",g)
513
-- if quit then
514
-- break
515
-- end
516
-- end
517
-- end
518
-- end
519
-- count = count + 1
520
-- end
521
-- elseif id == math_code then
522
-- start = end_of_math(start)
523
-- count = 0
524
-- elseif prev_id == kern_code and getsubtype(prev) == fontkern_code then
525
-- -- still inside a word ...normally kerns are added later
526
-- else
527
-- count = 0
528
-- start = getnext(start)
529
-- break
530
-- end
531
-- if start then
532
-- prev = start
533
-- previd = id
534
-- start = getnext(start)
535
-- end
536
-- end
537
-- if start then
538
-- attr, start = find_attribute(start,a_cases)
539
-- end
540
-- end
541
-- return head
542
-- end
543 544
-- function cases.handler(head) -- let's assume head doesn't change ... no reason
545
-- local lastfont = { }
546
-- for first, last, size, attr in nuts.words(head,a_cases) do
547
-- local n, id, m = get(attr)
548
-- if lastfont[n] == nil then
549
-- lastfont[n] = id
550
-- end
551
-- local action = actions[n]
552
-- if action then
553
-- action(first,attr,lastfont,n)
554
-- end
555
-- end
556
-- return head
557
-- end
558 559
local
enabled
=
false
560 561
function
cases
.
set
(
n
,
id
)
562
if
n
=
=
v_reset
then
563
n
=
unsetvalue
564
else
565
n
=
registered
[
n
]
or
tonumber
(
n
)
566
if
n
then
567
if
not
enabled
then
568
enableaction
(
"
processors
"
,
"
typesetters.cases.handler
"
)
569
if
trace_casing
then
570
report_casing
(
"
enabling case handler
"
)
571
end
572
enabled
=
true
573
end
574
n
=
set
(
n
,
id
)
575
else
576
n
=
unsetvalue
577
end
578
end
579
texsetattribute
(
a_cases
,
n
)
580
-- return n -- bonus
581
end
582 583
-- interface
584 585
interfaces
.
implement
{
586
name
=
"
setcharactercasing
"
,
587
actions
=
cases
.
set
,
588
arguments
=
{
"
string
"
,
"
integer
"
}
589
}
590