1if not modules then modules = { } end modules ['font-txt'] = {
2 version = 1.001,
3 comment = "companion to font-ini.mkiv",
4 original = "derived from a prototype by Kai Eigner",
5 author = "Hans Hagen",
6 copyright = "TAT Zetwerk / PRAGMA ADE / ConTeXt Development Team",
7 license = "see context related readme files"
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
79local fonts = fonts
80local otf = fonts.handlers.otf
81local nodes = nodes
82
83local utfchar = utf.char
84
85local nuts = nodes.nuts
86
87local getnext = nuts.getnext
88local setnext = nuts.setnext
89local getprev = nuts.getprev
90local setprev = nuts.setprev
91local getid = nuts.getid
92local getfont = nuts.getfont
93local getchar = nuts.getchar
94local getdisc = nuts.getdisc
95local setdisc = nuts.setdisc
96local getboth = nuts.getboth
97local getscales = nuts.getscales
98local setlink = nuts.setlink
99local getkern = nuts.getkern
100local getwidth = nuts.getwidth
101
102local ischar = nuts.ischar
103local isglyph = nuts.isglyph
104local usesfont = nuts.usesfont
105
106local copy_node_list = nuts.copylist
107local find_node_tail = nuts.tail
108local flushlist = nuts.flushlist
109local freenode = nuts.free
110local endofmath = nuts.endofmath
111
112local startofpar = nuts.startofpar
113
114local nodecodes = nodes.nodecodes
115
116local glyph_code <const> = nodecodes.glyph
117local glue_code <const> = nodecodes.glue
118local disc_code <const> = nodecodes.disc
119local kern_code <const> = nodecodes.kern
120local math_code <const> = nodecodes.math
121local dir_code <const> = nodecodes.dir
122local par_code <const> = nodecodes.par
123
124local righttoleft_code <const> = tex.directioncodes.righttoleft
125
126local txtdirstate = otf.helpers.txtdirstate
127local pardirstate = otf.helpers.pardirstate
128
129local fonthashes = fonts.hashes
130local fontdata = fonthashes.identifiers
131
132local function deletedisc(head)
133 local current = head
134 local next = nil
135 while current do
136 next = getnext(current)
137 if getid(current) == disc_code then
138 local pre, post, replace, pre_tail, post_tail, replace_tail = getdisc(current,true)
139 setdisc(current)
140 if pre then
141 flushlist(pre)
142 end
143 if post then
144 flushlist(post)
145 end
146 local p, n = getboth(current)
147 if replace then
148 if current == head then
149 head = replace
150 setprev(replace)
151 else
152 setlink(p,replace)
153 end
154 setlink(replace_tail,n)
155 elseif current == head then
156 head = n
157 setprev(n)
158 else
159 setlink(p,n)
160 end
161 freenode(current)
162 end
163 current = next
164 end
165 return head
166end
167
168
169
170
171
172local function eqnode(n,m)
173 local n_char = isglyph(n)
174 if n_char then
175 return n_char == ischar(m,getfont(n))
176 elseif n_id == kern_code then
177 return getkern(n) == getkern(m)
178 end
179end
180
181local function equalnode(n,m)
182 if not n then
183 return not m
184 elseif not m then
185 return false
186 end
187 local n_char, n_id = isglyph(n)
188 if n_char then
189 return n_char == ischar(m,n_id)
190 elseif n_id == whatsit_code then
191 return false
192 elseif n_id == glue_code then
193 return true
194 elseif n_id == kern_code then
195 return getkern(n) == getkern(m)
196 elseif n_id == disc_code then
197 local n_pre, n_post, n_replace = getdisc(n)
198 local m_pre, m_post, m_replace = getdisc(m)
199 while n_pre and m_pre do
200 if not eqnode(n_pre,m_pre) then
201 return false
202 end
203 n_pre = getnext(n_pre)
204 m_pre = getnext(m_pre)
205 end
206 if n_pre or m_pre then
207 return false
208 end
209 while n_post and m_post do
210 if not eqnode(n_post,m_post) then
211 return false
212 end
213 n_post = getnext(n_post)
214 m_post = getnext(m_post)
215 end
216 if n_post or m_post then
217 return false
218 end
219 while n_replace and m_replace do
220 if not eqnode(n_replace,m_replace) then
221 return false
222 end
223 n_replace = getnext(n_replace)
224 m_replace = getnext(m_replace)
225 end
226 if n_replace or m_replace then
227 return false
228 end
229 return true
230 end
231 return false
232end
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250do
251
252 local currentscale, currentxscale, currentyscale
253
254 local function texthandler(head,font,dynamic,rlmode,handler,startspacing,stopspacing,nesting)
255 if not head then
256 return
257 end
258 if startspacing == nil then
259 startspacing = false
260 end
261 if stopspacing == nil then
262 stopspacing = false
263 end
264
265 if getid(head) == par_code and startofpar(head) then
266 rlmode = pardirstate(head)
267 elseif rlmode == righttoleft_code then
268 rlmode = -1
269 else
270 rlmode = 0
271 end
272
273 local dirstack = { }
274 local rlparmode = 0
275 local topstack = 0
276 local text = { }
277 local size = 0
278 local current = head
279 local start = nil
280 local stop = nil
281 local startrlmode = rlmode
282
283 local function handle(leading,trailing)
284 local stop = current or start
285 if getid(stop) ~= glyph_code then
286 stop = getprev(stop)
287 end
288 head = handler(head,font,dynamic,rlmode,start,stop,text,leading,trailing)
289 size = 0
290 text = { }
291 start = nil
292 end
293
294 while current do
295 local char, id = ischar(current,font,dynamic,currentscale,currentxscale,currentyscale)
296 if char then
297 if not start then
298 start = current
299 startrlmode = rlmode
300 end
301 local char = getchar(current)
302 size = size + 1
303 text[size] = char
304 current = getnext(current)
305 elseif char == false then
306
307 if start and size > 0 then
308 handle(startspacing,false)
309 end
310 startspacing = false
311 local s, sx, sy = getscales(current)
312 if s ~= currentscale or sx ~= currentxscale or sy ~= currentyscale then
313 if start and size > 0 then
314 handle(startspacing,false)
315 end
316 startspacing = false
317 currentscale, currentxscale, currentyscale = s, sx, sy
318
319 else
320 current = getnext(current)
321 currentscale, currentxscale, currentyscale = false, false, false
322 end
323 elseif id == glue_code then
324
325
326 local width = getwidth(current)
327 if width > 0 then
328 if start and size > 0 then
329 handle(startspacing,true)
330 end
331 startspacing = true
332 stopspacing = false
333 else
334 if start and size > 0 then
335 head = handle(startspacing)
336 end
337 startspacing = false
338 stopspacing = false
339 end
340 current = getnext(current)
341 elseif id == disc_code and usesfont(current,font) then
342
343
344
345
346
347
348
349
350
351
352 local pre = nil
353 local post = nil
354 local currentnext = getnext(current)
355 local current_pre, current_post, current_replace = getdisc(current)
356 setdisc(current)
357 if start then
358 pre = copy_node_list(start,current)
359 stop = getprev(current)
360
361
362 if start == head then
363 head = current
364 end
365 setlink(getprev(start),current)
366 setlink(stop,current_pre)
367 current_pre = start
368 setprev(current_pre)
369 start = nil
370 stop = nil
371 startrlmode = rlmode
372 end
373 while currentnext do
374 local char, id = ischar(currentnext,font)
375 if char or id == disc_code then
376 stop = currentnext
377 currentnext = getnext(currentnext)
378 elseif id == glue_code then
379 local width = getwidth(currentnext)
380 if width and width > 0 then
381 stopspacing = true
382 else
383 stopspacing = false
384 end
385 break
386 else
387 break
388 end
389 end
390 if stop then
391 local currentnext = getnext(current)
392 local stopnext = getnext(stop)
393 post = copy_node_list(currentnext,stopnext)
394 if current_post then
395 setlink(find_node_tail(current_post),currentnext)
396 else
397 setprev(currentnext)
398 current_post = currentnext
399 end
400 setlink(current,stopnext)
401 setnext(stop)
402 stop = nil
403 end
404 if pre then
405 setlink(find_node_tail(pre),current_replace)
406 current_replace = pre
407 pre = nil
408 end
409 if post then
410 if current_replace then
411 setlink(find_node_tail(current_replace),post)
412 else
413 current_replace = post
414 end
415 post = nil
416 end
417 size = 0
418 text = { }
419 if current_pre then
420 current_pre = texthandler(current_pre,font,dynamic,rlmode,handler,startspacing,false,"pre")
421 end
422 if current_post then
423 current_post = texthandler(current_post,font,dynamic,rlmode,handler,false,stopspacing,"post")
424 end
425 if current_replace then
426 current_replace = texthandler(current_replace,font,dynamic,rlmode,handler,startspacing,stopspacing,"replace")
427 end
428 startspacing = false
429 stopspacing = false
430 local cpost = current_post and find_node_tail(current_post)
431 local creplace = current_replace and find_node_tail(current_replace)
432 local cpostnew = nil
433 local creplacenew = nil
434 local newcurrent = nil
435 while cpost and equalnode(cpost,creplace) do
436 cpostnew = cpost
437 creplacenew = creplace
438 if creplace then
439 creplace = getprev(creplace)
440 end
441 cpost = getprev(cpost)
442 end
443 if cpostnew then
444 if cpostnew == current_post then
445 current_post = nil
446 else
447 setnext(getprev(cpostnew))
448 end
449 flushlist(cpostnew)
450 if creplacenew == current_replace then
451 current_replace = nil
452 else
453 setnext(getprev(creplacenew))
454 end
455 local c = getnext(current)
456 setlink(current,creplacenew)
457 local creplacenewtail = find_node_tail(creplacenew)
458 setlink(creplacenewtail,c)
459 newcurrent = creplacenewtail
460 end
461 current_post = current_post and deletedisc(current_post)
462 current_replace = current_replace and deletedisc(current_replace)
463 local cpre = current_pre
464 local creplace = current_replace
465 local cprenew = nil
466 local creplacenew = nil
467 while cpre and equalnode(cpre, creplace) do
468 cprenew = cpre
469 creplacenew = creplace
470 if creplace then
471 creplace = getnext(creplace)
472 end
473 cpre = getnext(cpre)
474 end
475 if cprenew then
476 cpre = current_pre
477 current_pre = getnext(cprenew)
478 if current_pre then
479 setprev(current_pre)
480 end
481 setnext(cprenew)
482 flushlist(cpre)
483 creplace = current_replace
484 current_replace = getnext(creplacenew)
485 if current_replace then
486 setprev(current_replace)
487 end
488 setlink(getprev(current),creplace)
489 if current == head then
490 head = creplace
491 end
492 setlink(creplacenew,current)
493 end
494 setdisc(current,current_pre,current_post,current_replace)
495 current = currentnext
496 else
497 if start and size > 0 then
498 handle(startspacing,stopspacing)
499 end
500 startspacing = false
501 stopspacing = false
502 if id == math_code then
503 current = getnext(endofmath(current))
504 elseif id == dir_code then
505 startspacing = false
506 topstack, rlmode = txtdirstate(current,dirstack,topstack,rlparmode)
507 current = getnext(current)
508
509
510
511
512 else
513 current = getnext(current)
514 end
515 end
516 end
517 if start and size > 0 then
518 handle(startspacing,stopspacing)
519 end
520 return head, true
521 end
522
523 function fonts.handlers.otf.texthandler(head,font,dynamic,direction,action)
524 currentscale = false
525 currentxscale = false
526 currentyscale = false
527 if action then
528 return texthandler(head,font,dynamic,direction == righttoleft_code and -1 or 0,action)
529 else
530 return head, false
531 end
532 end
533
534
535
536
537 local report_text = logs.reporter("otf plugin","text")
538 local nofruns = 0
539 local nofsnippets = 0
540 local f_unicode = string.formatters["%U"]
541
542 local function showtext(head,font,dynamic,rlmode,start,stop,list,before,after)
543 if list then
544 nofsnippets = nofsnippets + 1
545 local plus = { }
546 for i=1,#list do
547 local u = list[i]
548 list[i] = utfchar(u)
549 plus[i] = f_unicode(u)
550 end
551 report_text("%03i : [%s] %t [%s]-> % t", nofsnippets, before and "+" or "-", list, after and "+" or "-", plus)
552 else
553 report_text()
554 report_text("invalid list")
555 report_text()
556 end
557 return head, false
558 end
559
560 fonts.handlers.otf.registerplugin("text",function(head,font,dynamic,direction)
561 nofruns = nofruns + 1
562 nofsnippets = 0
563 report_text("start run %i",nofruns)
564 local h, d = texthandler(head,font,dynamic,direction,showtext)
565 report_text("stop run %i",nofruns)
566 return h, d
567 end)
568
569end
570 |