1if not modules then modules = { } end modules ['font-otj'] = {
2 version = 1.001,
3 optimize = true,
4 comment = "companion to font-lib.mkiv",
5 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6 copyright = "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
33if not nodes.properties then return end
34
35local next, rawget, tonumber = next, rawget, tonumber
36local fastcopy = table.fastcopy
37
38local registertracker = trackers.register
39local registerdirective = directives.register
40
41local trace_injections = false registertracker("fonts.injections", function(v) trace_injections = v end)
42local trace_marks = false registertracker("fonts.injections.marks", function(v) trace_marks = v end)
43local trace_cursive = false registertracker("fonts.injections.cursive", function(v) trace_cursive = v end)
44local trace_spaces = false registertracker("fonts.injections.spaces", function(v) trace_spaces = v end)
45
46
47
48
49
50
51
52local report_injections = logs.reporter("fonts","injections")
53local report_spaces = logs.reporter("fonts","spaces")
54
55local attributes, nodes, node = attributes, nodes, node
56
57fonts = fonts
58local hashes = fonts.hashes
59local fontdata = hashes.identifiers
60local fontmarks = hashes.marks
61
62
63
64nodes.injections = nodes.injections or { }
65local injections = nodes.injections
66
67local tracers = nodes.tracers
68local setcolor = tracers and tracers.colors.set
69local resetcolor = tracers and tracers.colors.reset
70
71local nodecodes = nodes.nodecodes
72local glyph_code = nodecodes.glyph
73local disc_code = nodecodes.disc
74local kern_code = nodecodes.kern
75local glue_code = nodecodes.glue
76
77local nuts = nodes.nuts
78local nodepool = nuts.pool
79
80local tonode = nuts.tonode
81local tonut = nuts.tonut
82
83local setfield = nuts.setfield
84local getnext = nuts.getnext
85local getprev = nuts.getprev
86local getid = nuts.getid
87local getfont = nuts.getfont
88local getchar = nuts.getchar
89local getoffsets = nuts.getoffsets
90local getboth = nuts.getboth
91local getdisc = nuts.getdisc
92local setdisc = nuts.setdisc
93local getreplace = nuts.getreplace
94local setreplace = nuts.setreplace
95local setoffsets = nuts.setoffsets
96local ischar = nuts.ischar
97local getkern = nuts.getkern
98local setkern = nuts.setkern
99local setlink = nuts.setlink
100local setwidth = nuts.setwidth
101local getwidth = nuts.getwidth
102
103local nextchar = nuts.traversers.char
104local nextglue = nuts.traversers.glue
105
106local insertnodebefore = nuts.insertbefore
107local insertnodeafter = nuts.insertafter
108
109local properties = nodes.properties.data
110
111local fontkern = nuts.pool and nuts.pool.fontkern
112local italickern = nuts.pool and nuts.pool.italickern
113
114local useitalickerns = false
115
116directives.register("fonts.injections.useitalics", function(v)
117 if v then
118 report_injections("using italics for space kerns (tracing only)")
119 end
120 useitalickerns = v
121end)
122
123if not fontkern then
124
125 local thekern = nuts.new("kern",0)
126 local setkern = nuts.setkern
127 local copy_node = nuts.copy
128
129 fontkern = function(k)
130 local n = copy_node(thekern)
131 setkern(n,k)
132 return n
133 end
134
135end
136
137if not italickern then
138
139 local thekern = nuts.new("kern",3)
140 local setkern = nuts.setkern
141 local copy_node = nuts.copy
142
143 italickern = function(k)
144 local n = copy_node(thekern)
145 setkern(n,k)
146 return n
147 end
148
149end
150
151function injections.installnewkern() end
152
153local nofregisteredkerns = 0
154local nofregisteredpositions = 0
155local nofregisteredmarks = 0
156local nofregisteredcursives = 0
157local keepregisteredcounts = false
158
159function injections.keepcounts()
160 keepregisteredcounts = true
161end
162
163function injections.resetcounts()
164 nofregisteredkerns = 0
165 nofregisteredpositions = 0
166 nofregisteredmarks = 0
167 nofregisteredcursives = 0
168 keepregisteredcounts = false
169end
170
171
172
173function injections.reset(n)
174 local p = rawget(properties,n)
175 if p then
176 p.injections = false
177 else
178 properties[n] = false
179 end
180end
181
182function injections.copy(target,source)
183 local sp = rawget(properties,source)
184 if sp then
185 local tp = rawget(properties,target)
186 local si = sp.injections
187 if si then
188 si = fastcopy(si)
189 if tp then
190 tp.injections = si
191 else
192 properties[target] = {
193 injections = si,
194 }
195 end
196 elseif tp then
197 tp.injections = false
198 else
199 properties[target] = { injections = { } }
200 end
201 else
202 local tp = rawget(properties,target)
203 if tp then
204 tp.injections = false
205 else
206 properties[target] = false
207 end
208 end
209end
210
211function injections.setligaindex(n,index)
212 local p = rawget(properties,n)
213 if p then
214 local i = p.injections
215 if i then
216 i.ligaindex = index
217 else
218 p.injections = {
219 ligaindex = index
220 }
221 end
222 else
223 properties[n] = {
224 injections = {
225 ligaindex = index
226 }
227 }
228 end
229end
230
231function injections.getligaindex(n,default)
232 local p = rawget(properties,n)
233 if p then
234 local i = p.injections
235 if i then
236 return i.ligaindex or default
237 end
238 end
239 return default
240end
241
242function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext,r2lflag)
243
244
245
246
247
248
249
250
251 local dx = factor*(exit[1]-entry[1])
252 local dy = -factor*(exit[2]-entry[2])
253 local ws = tfmstart.width
254 local wn = tfmnext.width
255 nofregisteredcursives = nofregisteredcursives + 1
256 if rlmode < 0 then
257 dx = -(dx + wn)
258 else
259 dx = dx - ws
260 end
261 if dx == 0 then
262
263 dx = 0
264 end
265
266 local p = rawget(properties,start)
267 if p then
268 local i = p.injections
269 if i then
270 i.cursiveanchor = true
271 else
272 p.injections = {
273 cursiveanchor = true,
274 }
275 end
276 else
277 properties[start] = {
278 injections = {
279 cursiveanchor = true,
280 },
281 }
282 end
283 local p = rawget(properties,nxt)
284 if p then
285 local i = p.injections
286 if i then
287 i.cursivex = dx
288 i.cursivey = dy
289 else
290 p.injections = {
291 cursivex = dx,
292 cursivey = dy,
293 }
294 end
295 else
296 properties[nxt] = {
297 injections = {
298 cursivex = dx,
299 cursivey = dy,
300 },
301 }
302 end
303 return dx, dy, nofregisteredcursives
304end
305
306
307
308function injections.setposition(kind,current,factor,rlmode,spec,injection)
309 local x = factor * (spec[1] or 0)
310 local y = factor * (spec[2] or 0)
311 local w = factor * (spec[3] or 0)
312 local h = factor * (spec[4] or 0)
313 if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then
314 local yoffset = y - h
315 local leftkern = x
316 local rightkern = w - x
317 if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
318 nofregisteredpositions = nofregisteredpositions + 1
319 if rlmode and rlmode < 0 then
320 leftkern, rightkern = rightkern, leftkern
321 end
322 if not injection then
323 injection = "injections"
324 end
325 local p = rawget(properties,current)
326 if p then
327 local i = p[injection]
328 if i then
329 if leftkern ~= 0 then
330 i.leftkern = (i.leftkern or 0) + leftkern
331 end
332 if rightkern ~= 0 then
333 i.rightkern = (i.rightkern or 0) + rightkern
334 end
335 if yoffset ~= 0 then
336 i.yoffset = (i.yoffset or 0) + yoffset
337 end
338 elseif leftkern ~= 0 or rightkern ~= 0 then
339 p[injection] = {
340 leftkern = leftkern,
341 rightkern = rightkern,
342 yoffset = yoffset,
343 }
344 else
345 p[injection] = {
346 yoffset = yoffset,
347 }
348 end
349 elseif leftkern ~= 0 or rightkern ~= 0 then
350 properties[current] = {
351 [injection] = {
352 leftkern = leftkern,
353 rightkern = rightkern,
354 yoffset = yoffset,
355 },
356 }
357 else
358 properties[current] = {
359 [injection] = {
360 yoffset = yoffset,
361 },
362 }
363 end
364 return x, y, w, h, nofregisteredpositions
365 end
366 end
367 return x, y, w, h
368end
369
370
371
372
373
374function injections.setkern(current,factor,rlmode,x,injection)
375 local dx = factor * x
376 if dx ~= 0 then
377 nofregisteredkerns = nofregisteredkerns + 1
378 local p = rawget(properties,current)
379 if not injection then
380 injection = "injections"
381 end
382 if p then
383 local i = p[injection]
384 if i then
385 i.leftkern = dx + (i.leftkern or 0)
386 else
387 p[injection] = {
388 leftkern = dx,
389 }
390 end
391 else
392 properties[current] = {
393 [injection] = {
394 leftkern = dx,
395 },
396 }
397 end
398 return dx, nofregisteredkerns
399 else
400 return 0, 0
401 end
402end
403
404
405
406
407
408function injections.setmove(current,factor,rlmode,x,injection)
409 local dx = factor * x
410 if dx ~= 0 then
411 nofregisteredkerns = nofregisteredkerns + 1
412 local p = rawget(properties,current)
413 if not injection then
414 injection = "injections"
415 end
416 if rlmode and rlmode < 0 then
417
418
419 if p then
420 local i = p[injection]
421 if i then
422 i.rightkern = dx + (i.rightkern or 0)
423 else
424 p[injection] = {
425 rightkern = dx,
426 }
427 end
428 else
429 properties[current] = {
430 [injection] = {
431 rightkern = dx,
432 },
433 }
434 end
435 else
436 if p then
437 local i = p[injection]
438 if i then
439 i.leftkern = dx + (i.leftkern or 0)
440 else
441 p[injection] = {
442 leftkern = dx,
443 }
444 end
445 else
446 properties[current] = {
447 [injection] = {
448 leftkern = dx,
449 },
450 }
451 end
452 end
453 return dx, nofregisteredkerns
454 else
455 return 0, 0
456 end
457end
458
459function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark)
460 local dx = factor*(ba[1]-ma[1])
461 local dy = factor*(ba[2]-ma[2])
462 nofregisteredmarks = nofregisteredmarks + 1
463 if rlmode >= 0 then
464 dx = tfmbase.width - dx
465 end
466 local p = rawget(properties,start)
467
468 if p then
469 local i = p.injections
470 if i then
471 if i.markmark then
472
473 else
474
475
476
477
478
479
480
481
482
483 i.markx = dx
484 i.marky = dy
485 i.markdir = rlmode or 0
486 i.markbase = nofregisteredmarks
487 i.markbasenode = base
488 i.markmark = mkmk
489 i.checkmark = checkmark
490 end
491 else
492 p.injections = {
493 markx = dx,
494 marky = dy,
495 markdir = rlmode or 0,
496 markbase = nofregisteredmarks,
497 markbasenode = base,
498 markmark = mkmk,
499 checkmark = checkmark,
500 }
501 end
502 else
503 properties[start] = {
504 injections = {
505 markx = dx,
506 marky = dy,
507 markdir = rlmode or 0,
508 markbase = nofregisteredmarks,
509 markbasenode = base,
510 markmark = mkmk,
511 checkmark = checkmark,
512 },
513 }
514 end
515 return dx, dy, nofregisteredmarks
516end
517
518local function dir(n)
519 return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset"
520end
521
522local function showchar(n,nested)
523 local char = getchar(n)
524 report_injections("%wfont %s, char %U, glyph %c",nested and 2 or 0,getfont(n),char,char)
525end
526
527local function show(n,what,nested,symbol)
528 if n then
529 local p = rawget(properties,n)
530 if p then
531 local i = p[what]
532 if i then
533 local leftkern = i.leftkern or 0
534 local rightkern = i.rightkern or 0
535 local yoffset = i.yoffset or 0
536 local markx = i.markx or 0
537 local marky = i.marky or 0
538 local markdir = i.markdir or 0
539 local markbase = i.markbase or 0
540 local cursivex = i.cursivex or 0
541 local cursivey = i.cursivey or 0
542 local ligaindex = i.ligaindex or 0
543 local cursbase = i.cursiveanchor
544 local margin = nested and 4 or 2
545
546 if rightkern ~= 0 or yoffset ~= 0 then
547 report_injections("%w%s pair: lx %p, rx %p, dy %p",margin,symbol,leftkern,rightkern,yoffset)
548 elseif leftkern ~= 0 then
549 report_injections("%w%s kern: dx %p",margin,symbol,leftkern)
550 end
551 if markx ~= 0 or marky ~= 0 or markbase ~= 0 then
552 report_injections("%w%s mark: dx %p, dy %p, dir %s, base %s",margin,symbol,markx,marky,markdir,markbase ~= 0 and "yes" or "no")
553 end
554 if cursivex ~= 0 or cursivey ~= 0 then
555 if cursbase then
556 report_injections("%w%s curs: base dx %p, dy %p",margin,symbol,cursivex,cursivey)
557 else
558 report_injections("%w%s curs: dx %p, dy %p",margin,symbol,cursivex,cursivey)
559 end
560 elseif cursbase then
561 report_injections("%w%s curs: base",margin,symbol)
562 end
563 if ligaindex ~= 0 then
564 report_injections("%w%s liga: index %i",margin,symbol,ligaindex)
565 end
566 end
567 end
568 end
569end
570
571local function showsub(n,what,where)
572 report_injections("begin subrun: %s",where)
573 for n in nextchar, n do
574 showchar(n,where)
575 show(n,what,where," ")
576 end
577 report_injections("end subrun")
578end
579
580local function trace(head,where)
581 report_injections()
582 report_injections("begin run %s: %s kerns, %s positions, %s marks and %s cursives registered",
583 where or "",nofregisteredkerns,nofregisteredpositions,nofregisteredmarks,nofregisteredcursives)
584 local n = head
585 while n do
586 local id = getid(n)
587 if id == glyph_code then
588 showchar(n)
589 show(n,"injections",false," ")
590 show(n,"preinjections",false,"<")
591 show(n,"postinjections",false,">")
592 show(n,"replaceinjections",false,"=")
593 show(n,"emptyinjections",false,"*")
594 elseif id == disc_code then
595 local pre, post, replace = getdisc(n)
596 if pre then
597 showsub(pre,"preinjections","pre")
598 end
599 if post then
600 showsub(post,"postinjections","post")
601 end
602 if replace then
603 showsub(replace,"replaceinjections","replace")
604 end
605 show(n,"emptyinjections",false,"*")
606 end
607 n = getnext(n)
608 end
609 report_injections("end run")
610end
611
612local function show_result(head)
613 local current = head
614 local skipping = false
615 while current do
616 local id = getid(current)
617 if id == glyph_code then
618 local w = getwidth(current)
619 local x, y = getoffsets(current)
620 report_injections("char: %C, width %p, xoffset %p, yoffset %p",getchar(current),w,x,y)
621 skipping = false
622 elseif id == kern_code then
623 report_injections("kern: %p",getkern(current))
624 skipping = false
625 elseif not skipping then
626 report_injections()
627 skipping = true
628 end
629 current = getnext(current)
630 end
631 report_injections()
632end
633
634
635
636
637
638
639
640
641
642local function inject_kerns_only(head,where)
643 if trace_injections then
644 trace(head,"kerns")
645 end
646 local current = head
647 local prev = nil
648 local next = nil
649 local prevdisc = nil
650
651 local pre = nil
652 local post = nil
653 local replace = nil
654 local pretail = nil
655 local posttail = nil
656 local replacetail = nil
657 while current do
658 local next = getnext(current)
659 local char, id = ischar(current)
660 if char then
661 local p = rawget(properties,current)
662 if p then
663 local i = p.injections
664 if i then
665
666 local leftkern = i.leftkern
667 if leftkern and leftkern ~= 0 then
668 if prev and getid(prev) == glue_code then
669 if useitalickerns then
670 head = insertnodebefore(head,current,italickern(leftkern))
671 else
672 setwidth(prev, getwidth(prev) + leftkern)
673 end
674 else
675 head = insertnodebefore(head,current,fontkern(leftkern))
676 end
677 end
678 end
679 if prevdisc then
680 local done = false
681 if post then
682 local i = p.postinjections
683 if i then
684 local leftkern = i.leftkern
685 if leftkern and leftkern ~= 0 then
686 setlink(posttail,fontkern(leftkern))
687 done = true
688 end
689 end
690 end
691 if replace then
692 local i = p.replaceinjections
693 if i then
694 local leftkern = i.leftkern
695 if leftkern and leftkern ~= 0 then
696 setlink(replacetail,fontkern(leftkern))
697 done = true
698 end
699 end
700 else
701 local i = p.emptyinjections
702 if i then
703
704 local leftkern = i.leftkern
705 if leftkern and leftkern ~= 0 then
706 replace = fontkern(leftkern)
707 done = true
708 end
709 end
710 end
711 if done then
712 setdisc(prevdisc,pre,post,replace)
713 end
714 end
715 end
716 prevdisc = nil
717
718 elseif char == false then
719
720 prevdisc = nil
721
722 elseif id == disc_code then
723 pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
724 local done = false
725 if pre then
726
727 for n in nextchar, pre do
728 local p = rawget(properties,n)
729 if p then
730 local i = p.injections or p.preinjections
731 if i then
732 local leftkern = i.leftkern
733 if leftkern and leftkern ~= 0 then
734 pre = insertnodebefore(pre,n,fontkern(leftkern))
735 done = true
736 end
737 end
738 end
739 end
740 end
741 if post then
742
743 for n in nextchar, post do
744 local p = rawget(properties,n)
745 if p then
746 local i = p.injections or p.postinjections
747 if i then
748 local leftkern = i.leftkern
749 if leftkern and leftkern ~= 0 then
750 post = insertnodebefore(post,n,fontkern(leftkern))
751 done = true
752 end
753 end
754 end
755 end
756 end
757 if replace then
758
759 for n in nextchar, replace do
760 local p = rawget(properties,n)
761 if p then
762 local i = p.injections or p.replaceinjections
763 if i then
764 local leftkern = i.leftkern
765 if leftkern and leftkern ~= 0 then
766 replace = insertnodebefore(replace,n,fontkern(leftkern))
767 done = true
768 end
769 end
770 end
771 end
772 end
773 if done then
774 setdisc(current,pre,post,replace)
775 end
776
777 prevdisc = current
778 else
779
780 prevdisc = nil
781 end
782 prev = current
783 current = next
784 end
785
786 if keepregisteredcounts then
787 keepregisteredcounts = false
788 else
789 nofregisteredkerns = 0
790 end
791 if trace_injections then
792 show_result(head)
793 end
794 return head
795end
796
797local function inject_positions_only(head,where)
798 if trace_injections then
799 trace(head,"positions")
800 end
801 local current = head
802 local prev = nil
803 local next = nil
804 local prevdisc = nil
805 local prevglyph = nil
806 local pre = nil
807 local post = nil
808 local replace = nil
809 local pretail = nil
810 local posttail = nil
811 local replacetail = nil
812 while current do
813 local next = getnext(current)
814 local char, id = ischar(current)
815 if char then
816 local p = rawget(properties,current)
817 if p then
818 local i = p.injections
819 if i then
820
821 local yoffset = i.yoffset
822 if yoffset and yoffset ~= 0 then
823 setoffsets(current,false,yoffset)
824 end
825 local leftkern = i.leftkern
826 local rightkern = i.rightkern
827 if leftkern and leftkern ~= 0 then
828 if rightkern and leftkern == -rightkern then
829 setoffsets(current,leftkern,false)
830 rightkern = 0
831 elseif prev and getid(prev) == glue_code then
832 if useitalickerns then
833 head = insertnodebefore(head,current,italickern(leftkern))
834 else
835 setwidth(prev, getwidth(prev) + leftkern)
836 end
837 else
838 head = insertnodebefore(head,current,fontkern(leftkern))
839 end
840 end
841 if rightkern and rightkern ~= 0 then
842 if next and getid(next) == glue_code then
843 if useitalickerns then
844 insertnodeafter(head,current,italickern(rightkern))
845 else
846 setwidth(next, getwidth(next) + rightkern)
847 end
848 else
849 insertnodeafter(head,current,fontkern(rightkern))
850 end
851 end
852 elseif next then
853 local i = p.emptyinjections
854 if i then
855
856 local rightkern = i.rightkern
857 if rightkern and rightkern ~= 0 and getid(next) == disc_code then
858 local replace = getreplace(next)
859 if replace then
860
861 else
862 setreplace(next,fontkern(rightkern))
863 end
864 end
865 end
866 end
867 if prevdisc then
868 local done = false
869 if post then
870 local i = p.postinjections
871 if i then
872 local leftkern = i.leftkern
873 if leftkern and leftkern ~= 0 then
874 setlink(posttail,fontkern(leftkern))
875 done = true
876 end
877 end
878 end
879 if replace then
880 local i = p.replaceinjections
881 if i then
882 local leftkern = i.leftkern
883 if leftkern and leftkern ~= 0 then
884 setlink(replacetail,fontkern(leftkern))
885 done = true
886 end
887 end
888 else
889 local i = p.emptyinjections
890 if i then
891
892 local leftkern = i.leftkern
893 if leftkern and leftkern ~= 0 then
894 replace = fontkern(leftkern)
895 done = true
896 end
897 end
898 end
899 if done then
900 setdisc(prevdisc,pre,post,replace)
901 end
902 end
903 end
904 prevdisc = nil
905 prevglyph = current
906 elseif char == false then
907 prevdisc = nil
908 prevglyph = current
909 elseif id == disc_code then
910 pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
911 local done = false
912 if pre then
913
914 for n in nextchar, pre do
915 local p = rawget(properties,n)
916 if p then
917 local i = p.injections or p.preinjections
918 if i then
919 local yoffset = i.yoffset
920 if yoffset and yoffset ~= 0 then
921 setoffsets(n,false,yoffset)
922 end
923 local leftkern = i.leftkern
924 if leftkern and leftkern ~= 0 then
925 pre = insertnodebefore(pre,n,fontkern(leftkern))
926 done = true
927 end
928 local rightkern = i.rightkern
929 if rightkern and rightkern ~= 0 then
930 insertnodeafter(pre,n,fontkern(rightkern))
931 done = true
932 end
933 end
934 end
935 end
936 end
937 if post then
938
939 for n in nextchar, post do
940 local p = rawget(properties,n)
941 if p then
942 local i = p.injections or p.postinjections
943 if i then
944 local yoffset = i.yoffset
945 if yoffset and yoffset ~= 0 then
946 setoffsets(n,false,yoffset)
947 end
948 local leftkern = i.leftkern
949 if leftkern and leftkern ~= 0 then
950 post = insertnodebefore(post,n,fontkern(leftkern))
951 done = true
952 end
953 local rightkern = i.rightkern
954 if rightkern and rightkern ~= 0 then
955 insertnodeafter(post,n,fontkern(rightkern))
956 done = true
957 end
958 end
959 end
960 end
961 end
962 if replace then
963
964 for n in nextchar, replace do
965 local p = rawget(properties,n)
966 if p then
967 local i = p.injections or p.replaceinjections
968 if i then
969 local yoffset = i.yoffset
970 if yoffset and yoffset ~= 0 then
971 setoffsets(n,false,yoffset)
972 end
973 local leftkern = i.leftkern
974 if leftkern and leftkern ~= 0 then
975 replace = insertnodebefore(replace,n,fontkern(leftkern))
976 done = true
977 end
978 local rightkern = i.rightkern
979 if rightkern and rightkern ~= 0 then
980 insertnodeafter(replace,n,fontkern(rightkern))
981 done = true
982 end
983 end
984 end
985 end
986 end
987 if prevglyph then
988 if pre then
989 local p = rawget(properties,prevglyph)
990 if p then
991 local i = p.preinjections
992 if i then
993
994 local rightkern = i.rightkern
995 if rightkern and rightkern ~= 0 then
996 pre = insertnodebefore(pre,pre,fontkern(rightkern))
997 done = true
998 end
999 end
1000 end
1001 end
1002 if replace then
1003 local p = rawget(properties,prevglyph)
1004 if p then
1005 local i = p.replaceinjections
1006 if i then
1007
1008 local rightkern = i.rightkern
1009 if rightkern and rightkern ~= 0 then
1010 replace = insertnodebefore(replace,replace,fontkern(rightkern))
1011 done = true
1012 end
1013 end
1014 end
1015 end
1016 end
1017 if done then
1018 setdisc(current,pre,post,replace)
1019 end
1020 prevglyph = nil
1021 prevdisc = current
1022 else
1023 prevglyph = nil
1024 prevdisc = nil
1025 end
1026 prev = current
1027 current = next
1028 end
1029
1030 if keepregisteredcounts then
1031 keepregisteredcounts = false
1032 else
1033 nofregisteredpositions = 0
1034 end
1035 if trace_injections then
1036 show_result(head)
1037 end
1038 return head
1039end
1040
1041local function showoffset(n,flag)
1042 local x, y = getoffsets(n)
1043 if x ~= 0 or y ~= 0 then
1044 setcolor(n,"darkgray")
1045 end
1046end
1047
1048local function inject_everything(head,where)
1049 if trace_injections then
1050 trace(head,"everything")
1051 end
1052 local hascursives = nofregisteredcursives > 0
1053 local hasmarks = nofregisteredmarks > 0
1054
1055 local current = head
1056 local last = nil
1057 local prev = nil
1058 local next = nil
1059 local prevdisc = nil
1060 local prevglyph = nil
1061 local pre = nil
1062 local post = nil
1063 local replace = nil
1064 local pretail = nil
1065 local posttail = nil
1066 local replacetail = nil
1067
1068 local cursiveanchor = nil
1069 local minc = 0
1070 local maxc = 0
1071 local glyphs = { }
1072 local marks = { }
1073 local nofmarks = 0
1074
1075
1076
1077
1078
1079 local function processmark(p,n,pn)
1080 local px, py = getoffsets(p)
1081 local nx, ny = getoffsets(n)
1082 local ox = 0
1083 local rightkern = nil
1084 local pp = rawget(properties,p)
1085 if pp then
1086 pp = pp.injections
1087 if pp then
1088 rightkern = pp.rightkern
1089 end
1090 end
1091 local markdir = pn.markdir
1092 if rightkern then
1093 ox = px - (pn.markx or 0) - rightkern
1094 if markdir and markdir < 0 then
1095
1096 if not pn.markmark then
1097 ox = ox + (pn.leftkern or 0)
1098 end
1099 else
1100
1101
1102
1103
1104
1105
1106
1107
1108 if false then
1109
1110 local leftkern = pp.leftkern
1111 if leftkern then
1112 ox = ox - leftkern
1113 end
1114 end
1115 end
1116 else
1117 ox = px - (pn.markx or 0)
1118 if markdir and markdir < 0 then
1119 if not pn.markmark then
1120 local leftkern = pn.leftkern
1121 if leftkern then
1122 ox = ox + leftkern
1123 end
1124 end
1125 end
1126 if pn.checkmark then
1127 local wn = getwidth(n)
1128 if wn and wn ~= 0 then
1129 wn = wn/2
1130 if trace_injections then
1131 report_injections("correcting non zero width mark %C",getchar(n))
1132 end
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144 insertnodebefore(n,n,fontkern(-wn))
1145 insertnodeafter(n,n,fontkern(-wn))
1146 end
1147 end
1148 end
1149 local oy = ny + py + (pn.marky or 0)
1150 if not pn.markmark then
1151 local yoffset = pn.yoffset
1152 if yoffset then
1153 oy = oy + yoffset
1154 end
1155 end
1156 setoffsets(n,ox,oy)
1157 if trace_marks then
1158 showoffset(n,true)
1159 end
1160 end
1161
1162
1163
1164 while current do
1165 local next = getnext(current)
1166 local char, id = ischar(current)
1167 if char then
1168 local p = rawget(properties,current)
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187 if p then
1188 local i = p.injections
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207 if i then
1208 local pm = i.markbasenode
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227 if pm then
1228 nofmarks = nofmarks + 1
1229 marks[nofmarks] = current
1230 else
1231 local yoffset = i.yoffset
1232 if yoffset and yoffset ~= 0 then
1233 setoffsets(current,false,yoffset)
1234 end
1235 if hascursives then
1236 local cursivex = i.cursivex
1237 if cursivex then
1238 if cursiveanchor then
1239 if cursivex ~= 0 then
1240 i.leftkern = (i.leftkern or 0) + cursivex
1241 end
1242 if maxc == 0 then
1243 minc = 1
1244 maxc = 1
1245 glyphs[1] = cursiveanchor
1246 else
1247 maxc = maxc + 1
1248 glyphs[maxc] = cursiveanchor
1249 end
1250 properties[cursiveanchor].cursivedy = i.cursivey
1251 last = current
1252 else
1253 maxc = 0
1254 end
1255 elseif maxc > 0 then
1256 local nx, ny = getoffsets(current)
1257 for i=maxc,minc,-1 do
1258 local ti = glyphs[i]
1259 ny = ny + properties[ti].cursivedy
1260 setoffsets(ti,false,ny)
1261 if trace_cursive then
1262 showoffset(ti)
1263 end
1264 end
1265 maxc = 0
1266 cursiveanchor = nil
1267 end
1268 if i.cursiveanchor then
1269 cursiveanchor = current
1270 else
1271 if maxc > 0 then
1272 local nx, ny = getoffsets(current)
1273 for i=maxc,minc,-1 do
1274 local ti = glyphs[i]
1275 ny = ny + properties[ti].cursivedy
1276 setoffsets(ti,false,ny)
1277 if trace_cursive then
1278 showoffset(ti)
1279 end
1280 end
1281 maxc = 0
1282 end
1283 cursiveanchor = nil
1284 end
1285 end
1286
1287 local leftkern = i.leftkern
1288 local rightkern = i.rightkern
1289 if leftkern and leftkern ~= 0 then
1290 if rightkern and leftkern == -rightkern then
1291 setoffsets(current,leftkern,false)
1292 rightkern = 0
1293 elseif prev and getid(prev) == glue_code then
1294 if useitalickerns then
1295 head = insertnodebefore(head,current,italickern(leftkern))
1296 else
1297 setwidth(prev, getwidth(prev) + leftkern)
1298 end
1299 else
1300 head = insertnodebefore(head,current,fontkern(leftkern))
1301 end
1302 end
1303 if rightkern and rightkern ~= 0 then
1304 if next and getid(next) == glue_code then
1305 if useitalickerns then
1306 insertnodeafter(head,current,italickern(rightkern))
1307 else
1308 setwidth(next, getwidth(next) + rightkern)
1309 end
1310 else
1311 insertnodeafter(head,current,fontkern(rightkern))
1312 end
1313 end
1314 end
1315 elseif next then
1316 local i = p.emptyinjections
1317 if i then
1318
1319 local rightkern = i.rightkern
1320 if rightkern and rightkern ~= 0 and getid(next) == disc_code then
1321 local replace = getreplace(next)
1322 if replace then
1323
1324 else
1325 setreplace(next,fontkern(rightkern))
1326 end
1327 end
1328 end
1329 end
1330 if prevdisc then
1331 if p then
1332 local done = false
1333 if post then
1334 local i = p.postinjections
1335 if i then
1336 local leftkern = i.leftkern
1337 if leftkern and leftkern ~= 0 then
1338 setlink(posttail,fontkern(leftkern))
1339 done = true
1340 end
1341 end
1342 end
1343 if replace then
1344 local i = p.replaceinjections
1345 if i then
1346 local leftkern = i.leftkern
1347 if leftkern and leftkern ~= 0 then
1348 setlink(replacetail,fontkern(leftkern))
1349 done = true
1350 end
1351 end
1352 else
1353 local i = p.emptyinjections
1354 if i then
1355 local leftkern = i.leftkern
1356 if leftkern and leftkern ~= 0 then
1357 replace = fontkern(leftkern)
1358 done = true
1359 end
1360 end
1361 end
1362 if done then
1363 setdisc(prevdisc,pre,post,replace)
1364 end
1365 end
1366 end
1367 else
1368
1369 if hascursives and maxc > 0 then
1370 local nx, ny = getoffsets(current)
1371 for i=maxc,minc,-1 do
1372 local ti = glyphs[i]
1373 ny = ny + properties[ti].cursivedy
1374 local xi, yi = getoffsets(ti)
1375 setoffsets(ti,xi,yi + ny)
1376 end
1377 maxc = 0
1378 cursiveanchor = nil
1379 end
1380 end
1381 prevdisc = nil
1382 prevglyph = current
1383 elseif char == false then
1384
1385 prevdisc = nil
1386 prevglyph = current
1387 elseif id == disc_code then
1388
1389 pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
1390 local done = false
1391 if pre then
1392
1393 for n in nextchar, pre do
1394 local p = rawget(properties,n)
1395 if p then
1396 local i = p.injections or p.preinjections
1397 if i then
1398 local yoffset = i.yoffset
1399 if yoffset and yoffset ~= 0 then
1400 setoffsets(n,false,yoffset)
1401 end
1402 local leftkern = i.leftkern
1403 if leftkern and leftkern ~= 0 then
1404 pre = insertnodebefore(pre,n,fontkern(leftkern))
1405 done = true
1406 end
1407 local rightkern = i.rightkern
1408 if rightkern and rightkern ~= 0 then
1409 insertnodeafter(pre,n,fontkern(rightkern))
1410 done = true
1411 end
1412 if hasmarks then
1413 local pm = i.markbasenode
1414 if pm then
1415 processmark(pm,n,i)
1416 end
1417 end
1418 end
1419 end
1420 end
1421 end
1422 if post then
1423
1424 for n in nextchar, post do
1425 local p = rawget(properties,n)
1426 if p then
1427 local i = p.injections or p.postinjections
1428 if i then
1429 local yoffset = i.yoffset
1430 if yoffset and yoffset ~= 0 then
1431 setoffsets(n,false,yoffset)
1432 end
1433 local leftkern = i.leftkern
1434 if leftkern and leftkern ~= 0 then
1435 post = insertnodebefore(post,n,fontkern(leftkern))
1436 done = true
1437 end
1438 local rightkern = i.rightkern
1439 if rightkern and rightkern ~= 0 then
1440 insertnodeafter(post,n,fontkern(rightkern))
1441 done = true
1442 end
1443 if hasmarks then
1444 local pm = i.markbasenode
1445 if pm then
1446 processmark(pm,n,i)
1447 end
1448 end
1449 end
1450 end
1451 end
1452 end
1453 if replace then
1454
1455 for n in nextchar, replace do
1456 local p = rawget(properties,n)
1457 if p then
1458 local i = p.injections or p.replaceinjections
1459 if i then
1460 local yoffset = i.yoffset
1461 if yoffset and yoffset ~= 0 then
1462 setoffsets(n,false,yoffset)
1463 end
1464 local leftkern = i.leftkern
1465 if leftkern and leftkern ~= 0 then
1466 replace = insertnodebefore(replace,n,fontkern(leftkern))
1467 done = true
1468 end
1469 local rightkern = i.rightkern
1470 if rightkern and rightkern ~= 0 then
1471 insertnodeafter(replace,n,fontkern(rightkern))
1472 done = true
1473 end
1474 if hasmarks then
1475 local pm = i.markbasenode
1476 if pm then
1477 processmark(pm,n,i)
1478 end
1479 end
1480 end
1481 end
1482 end
1483 end
1484 if prevglyph then
1485 if pre then
1486 local p = rawget(properties,prevglyph)
1487 if p then
1488 local i = p.preinjections
1489 if i then
1490
1491 local rightkern = i.rightkern
1492 if rightkern and rightkern ~= 0 then
1493 pre = insertnodebefore(pre,pre,fontkern(rightkern))
1494 done = true
1495 end
1496 end
1497 end
1498 end
1499 if replace then
1500 local p = rawget(properties,prevglyph)
1501 if p then
1502 local i = p.replaceinjections
1503 if i then
1504
1505 local rightkern = i.rightkern
1506 if rightkern and rightkern ~= 0 then
1507 replace = insertnodebefore(replace,replace,fontkern(rightkern))
1508 done = true
1509 end
1510 end
1511 end
1512 end
1513 end
1514 if done then
1515 setdisc(current,pre,post,replace)
1516 end
1517 prevglyph = nil
1518 prevdisc = current
1519 else
1520
1521 prevglyph = nil
1522 prevdisc = nil
1523 end
1524 prev = current
1525 current = next
1526 end
1527
1528 if hascursives and maxc > 0 then
1529 local nx, ny = getoffsets(last)
1530 for i=maxc,minc,-1 do
1531 local ti = glyphs[i]
1532 ny = ny + properties[ti].cursivedy
1533 setoffsets(ti,false,ny)
1534 if trace_cursive then
1535 showoffset(ti)
1536 end
1537 end
1538 end
1539
1540 if nofmarks > 0 then
1541 for i=1,nofmarks do
1542 local m = marks[i]
1543 local p = rawget(properties,m)
1544 local i = p.injections
1545 local b = i.markbasenode
1546 processmark(b,m,i)
1547 end
1548 elseif hasmarks then
1549
1550 end
1551
1552 if keepregisteredcounts then
1553 keepregisteredcounts = false
1554 else
1555 nofregisteredkerns = 0
1556 nofregisteredpositions = 0
1557 nofregisteredmarks = 0
1558 nofregisteredcursives = 0
1559 end
1560 if trace_injections then
1561 show_result(head)
1562 end
1563 return head
1564end
1565
1566
1567
1568local triggers = false
1569
1570function nodes.injections.setspacekerns(font,sequence)
1571 if triggers then
1572 triggers[font] = sequence
1573 else
1574 triggers = { [font] = sequence }
1575 end
1576end
1577
1578local getthreshold
1579
1580if context then
1581
1582 local threshold = 1
1583 local parameters = fonts.hashes.parameters
1584
1585 directives.register("otf.threshold", function(v) threshold = tonumber(v) or 1 end)
1586
1587 getthreshold = function(font)
1588 local p = parameters[font]
1589 local f = p.factor
1590 local s = p.spacing
1591 local t = threshold * (s and s.width or p.space or 0) - 2
1592 return t > 0 and t or 0, f
1593 end
1594
1595else
1596
1597 injections.threshold = 0
1598
1599 getthreshold = function(font)
1600 local p = fontdata[font].parameters
1601 local f = p.factor
1602 local s = p.spacing
1603 local t = injections.threshold * (s and s.width or p.space or 0) - 2
1604 return t > 0 and t or 0, f
1605 end
1606
1607end
1608
1609injections.getthreshold = getthreshold
1610
1611function injections.isspace(n,threshold,id)
1612 if (id or getid(n)) == glue_code then
1613 local w = getwidth(n)
1614 if threshold and w > threshold then
1615 return 32
1616 end
1617 end
1618end
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643local getspaceboth = getboth
1644
1645function injections.installgetspaceboth(gb)
1646 getspaceboth = gb or getboth
1647end
1648
1649local function injectspaces(head)
1650
1651 if not triggers then
1652 return head
1653 end
1654 local lastfont = nil
1655 local spacekerns = nil
1656 local leftkerns = nil
1657 local rightkerns = nil
1658 local factor = 0
1659 local threshold = 0
1660 local leftkern = false
1661 local rightkern = false
1662
1663 local function updatefont(font,trig)
1664 leftkerns = trig.left
1665 rightkerns = trig.right
1666 lastfont = font
1667 threshold,
1668 factor = getthreshold(font)
1669 end
1670
1671 for n in nextglue, head do
1672 local prev, next = getspaceboth(n)
1673 local prevchar = prev and ischar(prev)
1674 local nextchar = next and ischar(next)
1675 if nextchar then
1676 local font = getfont(next)
1677 local trig = triggers[font]
1678 if trig then
1679 if lastfont ~= font then
1680 updatefont(font,trig)
1681 end
1682 if rightkerns then
1683 rightkern = rightkerns[nextchar]
1684 end
1685 end
1686 end
1687 if prevchar then
1688 local font = getfont(prev)
1689 local trig = triggers[font]
1690 if trig then
1691 if lastfont ~= font then
1692 updatefont(font,trig)
1693 end
1694 if leftkerns then
1695 leftkern = leftkerns[prevchar]
1696 end
1697 end
1698 end
1699 if leftkern then
1700 local old = getwidth(n)
1701 if old > threshold then
1702 if rightkern then
1703 if useitalickerns then
1704 local lnew = leftkern * factor
1705 local rnew = rightkern * factor
1706 if trace_spaces then
1707 report_spaces("%C [%p + %p + %p] %C",prevchar,lnew,old,rnew,nextchar)
1708 end
1709 head = insertnodebefore(head,n,italickern(lnew))
1710 insertnodeafter(head,n,italickern(rnew))
1711 else
1712 local new = old + (leftkern + rightkern) * factor
1713 if trace_spaces then
1714 report_spaces("%C [%p -> %p] %C",prevchar,old,new,nextchar)
1715 end
1716 setwidth(n,new)
1717 end
1718 rightkern = false
1719 else
1720 if useitalickerns then
1721 local new = leftkern * factor
1722 if trace_spaces then
1723 report_spaces("%C [%p + %p]",prevchar,old,new)
1724 end
1725 insertnodeafter(head,n,italickern(new))
1726 else
1727 local new = old + leftkern * factor
1728 if trace_spaces then
1729 report_spaces("%C [%p -> %p]",prevchar,old,new)
1730 end
1731 setwidth(n,new)
1732 end
1733 end
1734 end
1735 leftkern = false
1736 elseif rightkern then
1737 local old = getwidth(n)
1738 if old > threshold then
1739 if useitalickerns then
1740 local new = rightkern * factor
1741 if trace_spaces then
1742 report_spaces("[%p + %p] %C",old,new,nextchar)
1743 end
1744 insertnodeafter(head,n,italickern(new))
1745 else
1746 local new = old + rightkern * factor
1747 if trace_spaces then
1748 report_spaces("[%p -> %p] %C",old,new,nextchar)
1749 end
1750 setwidth(n,new)
1751 end
1752 else
1753
1754 end
1755 rightkern = false
1756 end
1757 end
1758
1759 triggers = false
1760
1761 return head
1762end
1763
1764
1765
1766function injections.handler(head,where)
1767 if triggers then
1768 head = injectspaces(head)
1769 end
1770
1771 if nofregisteredmarks > 0 or nofregisteredcursives > 0 then
1772 if trace_injections then
1773 report_injections("injection variant %a","everything")
1774 end
1775 return inject_everything(head,where)
1776 elseif nofregisteredpositions > 0 then
1777 if trace_injections then
1778 report_injections("injection variant %a","positions")
1779 end
1780 return inject_positions_only(head,where)
1781 elseif nofregisteredkerns > 0 then
1782 if trace_injections then
1783 report_injections("injection variant %a","kerns")
1784 end
1785 return inject_kerns_only(head,where)
1786 else
1787 return head
1788 end
1789end
1790
1791 |