1if not modules then modules = { } end modules ['spac-prf'] = {
2 version = 1.001,
3 comment = "companion to spac-prf.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
10
11
12
13local unpack, rawget = unpack, rawget
14
15local formatters = string.formatters
16
17local nodecodes = nodes.nodecodes
18local gluecodes = nodes.gluecodes
19
20local glyph_code <const> = nodecodes.glyph
21local disc_code <const> = nodecodes.disc
22local kern_code <const> = nodecodes.kern
23local penalty_code <const> = nodecodes.penalty
24local glue_code <const> = nodecodes.glue
25local hlist_code <const> = nodecodes.hlist
26local vlist_code <const> = nodecodes.vlist
27local unset_code <const> = nodecodes.unset
28local math_code <const> = nodecodes.math
29local rule_code <const> = nodecodes.rule
30local marginkern_code <const> = nodecodes.marginkern
31
32local leaders_code <const> = gluecodes.leaders
33local lineskip_code <const> = gluecodes.lineskip
34local baselineskip_code <const> = gluecodes.baselineskip
35
36local strutrule_code <const> = nodes.rulecodes.strut
37local linelist_code <const> = nodes.listcodes.line
38
39local texlists = tex.lists
40local settexattribute = tex.setattribute
41local texgetdimen = tex.getdimen
42
43local d_strutht <const> = tex.isdimen("strutht")
44local d_strutdp <const> = tex.isdimen("strutdp")
45
46local newindex = lua.newindex
47
48local nuts = nodes.nuts
49local tonut = nodes.tonut
50local tonode = nuts.tonode
51
52local getreplace = nuts.getreplace
53local getattr = nuts.getattr
54local getid = nuts.getid
55local getboth = nuts.getboth
56local getnext = nuts.getnext
57local getprev = nuts.getprev
58local getsubtype = nuts.getsubtype
59local getlist = nuts.getlist
60local gettexbox = nuts.getbox
61local getwhd = nuts.getwhd
62local getglue = nuts.getglue
63local getkern = nuts.getkern
64local getshift = nuts.getshift
65local getwidth = nuts.getwidth
66local getheight = nuts.getheight
67local getdepth = nuts.getdepth
68local getboxglue = nuts.getboxglue
69local effectiveglue = nuts.effectiveglue
70local findattribute = nuts.findattribute
71local getspeciallist = nuts.getspeciallist
72
73local getlistdimensions = nuts.getlistdimensions
74
75local nextnode = nuts.traversers.node
76local nextglue = nuts.traversers.glue
77
78local setlink = nuts.setlink
79local setlist = nuts.setlist
80local setattr = nuts.setattr
81local setwhd = nuts.setwhd
82local setshift = nuts.setshift
83local setwidth = nuts.setwidth
84local setheight = nuts.setheight
85local setdepth = nuts.setdepth
86
87local properties = nodes.properties.data
88local setprop = nuts.setprop
89local getprop = nuts.getprop
90local theprop = nuts.theprop
91
92local floor = math.floor
93local ceiling = math.ceil
94local min = math.min
95local max = math.max
96
97local new_rule = nuts.pool.rule
98local new_glue = nuts.pool.glue
99local new_kern = nuts.pool.kern
100local hpack_nodes = nuts.hpack
101local find_node_tail = nuts.tail
102local setglue = nuts.setglue
103
104local a_visual <const> = attributes.private("visual")
105local a_snapmethod <const> = attributes.private("snapmethod")
106local a_profilemethod <const> = attributes.private("profilemethod")
107
108
109local variables = interfaces.variables
110local v_none <const> = variables.none
111local v_fixed <const> = variables.fixed
112local v_strict <const> = variables.strict
113local v_yes <const> = variables.yes
114
115local setcolor = nodes.tracers.colors.set
116local settransparency = nodes.tracers.transparencies.set
117
118local enableaction = nodes.tasks.enableaction
119
120local profiling = { }
121builders.profiling = profiling
122
123local report = logs.reporter("profiling")
124
125local show_profile = false trackers.register("profiling.show", function(v) show_profile = v end)
126local trace_profile = false trackers.register("profiling.trace",function(v) trace_profile = v end)
127
128local function getprofile(line,step)
129
130
131
132
133 local line = tonut(line)
134 local current = getlist(line)
135
136 if not current then
137 return
138 end
139
140
141
142 local heights = { }
143 local depths = { }
144 local width = 0
145 local position = 0
146 local step = step or 65536
147 local margin = step / 4
148 local min = 0
149 local max = ceiling(getwidth(line)/step) + 1
150 local wd = 0
151 local ht = 0
152 local dp = 0
153
154 for i=min,max do
155 heights[i] = 0
156 depths [i] = 0
157 end
158
159
160
161 local function process(current)
162 for current, id, subtype in nextnode, current do
163 if id == glyph_code then
164 wd, ht, dp = getwhd(current)
165 elseif id == kern_code then
166 wd = getkern(current)
167 ht = 0
168 dp = 0
169 elseif id == disc_code then
170 local replace = getreplace(current)
171 if replace then
172 process(replace)
173 end
174 goto done
175 elseif id == glue_code then
176 wd = effectiveglue(current, line)
177
178 if subtype >= leaders_code then
179 local leader = getleader(current)
180 local w
181 w, ht, dp = getwhd(leader)
182 else
183 ht = 0
184 dp = 0
185 end
186 elseif id == hlist_code then
187
188
189 local shift = getshift(current)
190 local w, h, d = getwhd(current)
191 if getprop(current,"specialcontent") then
192
193 wd = w
194 ht = 0
195 dp = 0
196 else
197 wd = w
198 ht = h - shift
199 dp = d + shift
200 end
201 elseif id == vlist_code or id == unset_code then
202 local shift = getshift(current)
203 wd, ht, dp = getwhd(current)
204 elseif id == rule_code then
205 wd, ht, dp = getwhd(current)
206 elseif id == math_code then
207
208 wd = getkern(current) + getwidth(current)
209 ht = 0
210 dp = 0
211 else
212 goto done
213 end
214
215 position = width
216 width = position + wd
217 p = floor((position - margin)/step + 0.5)
218 w = floor((width + margin)/step - 0.5)
219 if p < 0 then
220 p = 0
221 end
222 if w < 0 then
223 w = 0
224 end
225 if p > w then
226 w, p = p, w
227 end
228 if w > max then
229 for i=max+1,w+1 do
230 heights[i] = 0
231 depths [i] = 0
232 end
233 max = w
234 end
235 for i=p,w do
236 if ht > heights[i] then
237 heights[i] = ht
238 end
239 if dp > depths[i] then
240 depths[i] = dp
241 end
242 end
243 ::done::
244 end
245 end
246
247 process(current)
248
249 return {
250 heights = heights,
251 depths = depths,
252 min = min,
253 max = max,
254 step = step,
255 }
256
257end
258
259profiling.get = getprofile
260
261local function getpagelist()
262 local pagehead = texlists.pagehead
263 if pagehead then
264 pagehead = tonut(texlists.pagehead)
265 pagetail = find_node_tail(pagehead)
266 else
267 pagetail = nil
268 end
269 return pagehead, pagetail
270end
271
272local function setprofile(n,step)
273 local p = rawget(properties,n)
274 if p then
275 local pp = p.profile
276 if not pp then
277 pp = getprofile(n,step)
278 p.profile = pp
279 end
280 return pp
281 else
282 local pp = getprofile(n,step)
283 properties[n] = { profile = pp }
284 return pp
285 end
286end
287
288local function hasprofile(n)
289 local p = rawget(properties,n)
290 if p then
291 return p.profile
292 end
293end
294
295local function addstring(height,depth)
296 local typesetters = nuts.typesetters
297 local hashes = fonts.hashes
298 local infofont = fonts.infofont()
299 local emwidth = hashes.emwidths [infofont]
300 local exheight = hashes.exheights[infofont]
301 local httext = height
302 local dptext = depth
303 local httext = typesetters.tohpack(height,infofont)
304 local dptext = typesetters.tohpack(depth,infofont)
305 setshift(httext,- 1.2 * exheight)
306 setshift(dptext, 0.6 * exheight)
307 local text = hpack_nodes(setlink(
308 new_kern(-getwidth(httext)-emwidth),
309 httext,
310 new_kern(-getwidth(dptext)),
311 dptext
312 ))
313 setwhd(text,0,0,0)
314 return text
315end
316
317local function addprofile(node,profile,step)
318
319 local line = tonut(node)
320
321 if not profile then
322 profile = setprofile(line,step)
323 end
324
325 if not profile then
326 report("some error")
327 return node
328 end
329
330 if profile.shown then
331 return node
332 end
333
334 local list = getlist(line)
335 profile.shown = true
336
337 local heights = profile.heights
338 local depths = profile.depths
339 local step = profile.step
340
341 local head = nil
342 local tail = nil
343
344 local lastht = 0
345 local lastdp = 0
346 local lastwd = 0
347
348 local visual = "f:s:t"
349
350 local function progress()
351 if lastwd == 0 then
352 return
353 end
354 local what = nil
355
356 if lastht == 0 and lastdp == 0 then
357 what = new_kern(lastwd)
358 else
359 what = new_rule(lastwd,lastht,lastdp)
360 setcolor(what,visual)
361 settransparency(what,visual)
362 end
363 if tail then
364 setlink(tail,what)
365 else
366 head = what
367 end
368 tail = what
369 end
370
371
372
373 for i=profile.min,profile.max do
374 local ht = heights[i]
375 local dp = depths[i]
376 if ht ~= lastht or dp ~= lastdp and lastwd > 0 then
377 progress()
378 lastht = ht
379 lastdp = dp
380 lastwd = step
381 else
382 lastwd = lastwd + step
383 end
384 end
385 if lastwd > 0 then
386 progress()
387 end
388
389 local rule = hpack_nodes(head)
390
391 setwhd(rule,0,0,0)
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406 setlink(rule,list)
407 setlist(line,rule)
408
409end
410
411profiling.add = addprofile
412
413local methods = { }
414
415local function getdelta(t_profile,b_profile)
416 local t_heights = t_profile.heights
417 local t_depths = t_profile.depths
418 local t_max = t_profile.max
419 local b_heights = b_profile.heights
420 local b_depths = b_profile.depths
421 local b_max = b_profile.max
422
423 local max = t_max
424 local delta = 0
425
426 if t_max > b_max then
427 for i=b_max+1,t_max do
428 b_depths [i] = 0
429 b_heights[i] = 0
430 end
431 max = t_max
432 elseif b_max > t_max then
433 for i=t_max+1,b_max do
434 t_depths [i] = 0
435 t_heights[i] = 0
436 end
437 max = b_max
438 end
439
440 for i=0,max do
441 local ht = b_heights[i]
442 local dp = t_depths[i]
443 local hd = ht + dp
444 if hd > delta then
445 delta = hd
446 end
447 end
448
449 return delta
450end
451
452
453
454
455
456
457
458
459
460
461
462
463local function inject(top,bot,amount)
464 if amount ~= 0 then
465 local glue = new_glue(amount)
466
467 setattr(glue,a_profilemethod,0)
468
469 setattr(glue,a_visual,nodes.visualizers.modes.glue)
470
471 setlink(top,glue,bot)
472
473 report("injected correction %p at page",amount,tex.getcount("realpageno"))
474 end
475end
476
477methods[v_none] = function()
478 return false
479end
480
481methods[v_strict] = function(top,bot,t_profile,b_profile,specification)
482
483 local top = tonut(top)
484 local bot = tonut(bot)
485
486 local strutht = specification.height or texgetdimen(d_strutht)
487 local strutdp = specification.depth or texgetdimen(d_strutdp)
488 local lineheight = strutht + strutdp
489
490 local depth = getdepth(top)
491 local height = getheight(bot)
492 local total = depth + height
493 local distance = specification.distance or 0
494 local delta = lineheight - total
495
496
497
498
499 if delta >= distance then
500 inject(top,bot,delta)
501 return true
502 end
503
504 local delta = getdelta(t_profile,b_profile)
505 local skip = delta - total + distance
506
507
508
509
510 inject(top,bot,skip)
511 return true
512
513end
514
515
516
517methods[v_fixed] = function(top,bot,t_profile,b_profile,specification)
518
519 local top = tonut(top)
520 local bot = tonut(bot)
521
522 local strutht = specification.height or texgetdimen(d_strutht)
523 local strutdp = specification.depth or texgetdimen(d_strutdp)
524 local lineheight = strutht + strutdp
525
526 local depth = getdepth(top)
527 local height = getheight(bot)
528 local total = depth + height
529 local distance = specification.distance or 0
530 local delta = lineheight - total
531
532 local snapmethod = getattr(top,a_snapmethod)
533
534 if snapmethod then
535
536
537
538 if delta < lineheight then
539 setdepth(top,strutdp)
540 setheight(bot,strutht)
541 return true
542 end
543
544 local delta = getdelta(t_profile,b_profile)
545
546 local dp = strutdp
547 while depth > lineheight - strutdp do
548 depth = depth - lineheight
549 dp = dp + lineheight
550 end
551 setdepth(top,dp)
552 local ht = strutht
553 while height > lineheight - strutht do
554 height = height - lineheight
555 ht = ht + lineheight
556 end
557 setheight(bot,ht)
558 local lines = floor(delta/lineheight)
559 if lines > 0 then
560 inject(top,bot,-lines * lineheight)
561 end
562
563 return true
564
565 end
566
567 if total < lineheight then
568 setdepth(top,strutdp)
569 setheight(bot,strutht)
570 return true
571 end
572
573 if depth < strutdp then
574 setdepth(top,strutdp)
575 total = total - depth + strutdp
576 end
577 if height < strutht then
578 setheight(bot,strutht)
579 total = total - height + strutht
580 end
581
582 local delta = getdelta(t_profile,b_profile)
583
584 local target = total - delta
585 local factor = specification.factor or 1
586 local step = lineheight / factor
587 local correction = 0
588 local nofsteps = 0
589 while correction < target - step - distance do
590 correction = correction + step
591 nofsteps = nofsteps + 1
592 end
593
594 if trace_profile then
595 report("top line : %s %05i > %s",t_profile.shown and "+" or "-",top,nodes.toutf(getlist(top)))
596 report("bottom line : %s %05i > %s",b_profile.shown and "+" or "-",bot,nodes.toutf(getlist(bot)))
597 report(" depth : %p",depth)
598 report(" height : %p",height)
599 report(" total : %p",total)
600 report(" lineheight : %p",lineheight)
601 report(" delta : %p",delta)
602 report(" target : %p",target)
603 report(" factor : %i",factor)
604 report(" distance : %p",distance)
605 report(" step : %p",step)
606 report(" nofsteps : %i",nofsteps)
607
608 report(" correction : %p",correction)
609 end
610
611 inject(top,bot,-correction)
612
613 return true
614
615end
616
617function profiling.distance(top,bot,specification)
618 local step = specification.step
619 local method = specification.method
620 local ptop = getprofile(top,step)
621 local pbot = getprofile(bot,step)
622 local action = methods[method or v_strict] or methods[v_strict]
623 return action(top,bot,ptop,pbot,specification)
624end
625
626local specifications = { }
627
628function profiling.fixedprofile(current)
629 local a = getattr(current,a_profilemethod)
630 if a then
631 local s = specifications[a]
632 if s then
633 return s.method == v_fixed
634 end
635 end
636 return false
637end
638
639local function profilelist(line,mvl)
640
641 local top = nil
642 local bot = nil
643
644 local t_profile = nil
645 local b_profile = nil
646
647 local specification = nil
648 local lastattr = nil
649 local method = nil
650 local action = nil
651
652 local distance = 0
653 local lastglue = nil
654
655 local pagehead = nil
656 local pagetail = nil
657
658 if mvl then
659
660 pagehead, pagetail = getpagelist()
661
662 if pagetail then
663 for current, id, subtype in nextnode, pagetail do
664 if id == hlist_code then
665 if subtype == linelist_code then
666 t_profile = hasprofile(current)
667 if t_profile then
668 top = current
669 end
670 end
671 break
672 elseif id == glue_code then
673 local wd = getwidth(current)
674 if not wd or wd == 0 then
675
676 else
677 break
678 end
679 elseif id == penalty_code then
680
681 else
682 break
683 end
684 end
685 end
686
687 end
688
689 for current, id, subtype in nextnode, line do
690
691 local attr = getattr(current,a_profilemethod)
692
693 if attr then
694
695 if attr ~= lastattr then
696 specification = specifications[attr]
697 method = specification and specification.method
698 action = method and methods[method] or methods[v_strict]
699 lastattr = attr
700 end
701
702 if id == hlist_code then
703 if subtype == linelist_code then
704 if top == current then
705
706 bot = nil
707 elseif top then
708 bot = current
709 b_profile = setprofile(bot)
710 if show_profile then
711 addprofile(bot,b_profile)
712 end
713 if not t_profile.done then
714 if action then
715 local ok = action(top,bot,t_profile,b_profile,specification)
716 if ok and lastglue and distance ~= 0 then
717 setglue(lastglue)
718 end
719 end
720 t_profile.done = true
721 end
722 top = bot
723 bot = nil
724 t_profile = b_profile
725 b_profile = nil
726 distance = 0
727 else
728 top = current
729 t_profile = setprofile(top)
730 bot = nil
731 if show_profile then
732 addprofile(top,t_profile)
733 end
734 end
735 else
736 top = nil
737 bot = nil
738 end
739 elseif id == glue_code then
740 if top then
741
742 local wd = getwidth(current)
743 if wd > 0 then
744 distance = wd
745 lastglue = current
746 elseif wd < 0 then
747 top = nil
748 bot = nil
749 else
750
751 end
752
753
754
755
756 else
757 top = nil
758 bot = nil
759 end
760 elseif id == penalty_code then
761
762 else
763 top = nil
764 bot = nil
765 end
766 else
767 top = nil
768 bot = nil
769 end
770 end
771 if top then
772 t_profile = setprofile(top)
773 if show_profile then
774 addprofile(top,t_profile)
775 end
776 end
777end
778
779profiling.list = profilelist
780
781local enabled = false
782
783
784
785function profiling.set(specification)
786 if not enabled then
787 enableaction("mvlbuilders", "builders.profiling.pagehandler")
788
789
790 enabled = true
791 end
792 local n = #specifications + 1
793 specifications[n] = specification
794 settexattribute(a_profilemethod,n)
795end
796
797function profiling.profilebox(specification)
798 local boxnumber = specification.box
799 local current = getlist(gettexbox(boxnumber))
800 local top = nil
801 local bot = nil
802 local t_profile = nil
803 local b_profile = nil
804 local method = specification and specification.method
805 local action = method and methods[method] or methods[v_strict]
806 local lastglue = nil
807 local distance = 0
808 for current, id, subtype in nextnode, current do
809 if id == hlist_code then
810 if subtype == linelist_code then
811 if top then
812 bot = current
813 b_profile = setprofile(bot)
814 if show_profile then
815 addprofile(bot,b_profile)
816 end
817 if not t_profile.done then
818 if action then
819 local ok = action(top,bot,t_profile,b_profile,specification)
820 if ok and lastglue and distance ~= 0 then
821 setglue(lastglue)
822 end
823 end
824 t_profile.done = true
825 end
826 top = bot
827 t_profile = b_profile
828 b_profile = nil
829 distance = 0
830 else
831 top = current
832 t_profile = setprofile(top)
833 if show_profile then
834 addprofile(top,t_profile)
835 end
836 bot = nil
837 end
838 else
839 top = nil
840 bot = nil
841 end
842 elseif id == glue_code then
843 if subtype == lineskip_code or subtype == baselineskip_code then
844 if top then
845 local wd = getwidth(current)
846 if wd > 0 then
847 distance = wd
848 lastglue = current
849 elseif wd < 0 then
850 top = nil
851 bot = nil
852 else
853
854 end
855 else
856 top = nil
857 bot = nil
858 end
859 else
860 top = nil
861 bot = nil
862 end
863 elseif id == penalty_code then
864
865 else
866 top = nil
867 bot = nil
868 end
869 end
870
871 if top then
872 t_profile = setprofile(top)
873 if show_profile then
874 addprofile(top,t_profile)
875 end
876 end
877
878end
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895function profiling.pagehandler(head)
896 if head then
897 profilelist(head,true)
898 end
899 return head
900end
901
902interfaces.implement {
903 name = "setprofile",
904 actions = profiling.set,
905 arguments = {
906 {
907 { "name" },
908 { "height", "dimen" },
909 { "depth", "dimen" },
910 { "distance", "dimen" },
911 { "factor", "integer" },
912 { "lines", "integer" },
913 { "method" }
914 }
915 }
916}
917
918interfaces.implement {
919 name = "profilebox",
920 actions = profiling.profilebox,
921 arguments = {
922 {
923 { "box", "integer" },
924 { "height", "dimen" },
925 { "depth", "dimen" },
926 { "distance", "dimen" },
927 { "factor", "integer" },
928 { "lines", "integer" },
929 { "method" }
930 }
931 }
932}
933
934
935
936
937
938
939
940do
941
942
943
944 local a_lineprofile <const> = attributes.private("lineprofile")
945
946 local registervalue = attributes.registervalue
947 local getvalue = attributes.getvalue
948 local texsetattribute = tex.setattribute
949
950 local function getdepthprofile(line,step,margin,max,list)
951
952 local width = 0
953 local position = 0
954 local profile = newindex(max+2,0)
955 local wd = 0
956 local ht = 0
957 local dp = 0
958
959 profile[0] = 0
960
961 local function process(current)
962 for current, id, subtype in nextnode, current do
963 if id == glyph_code then
964 wd, ht, dp = getwhd(current)
965 elseif id == kern_code then
966 wd = getkern(current)
967 dp = 0
968 elseif id == disc_code then
969 local replace = getreplace(current)
970 if replace then
971 process(replace)
972 end
973 goto done
974 elseif id == glue_code then
975 wd = effectiveglue(current, line)
976
977 if subtype >= leaders_code then
978 local leader = getleader(current)
979 local w
980 w, ht, dp = getwhd(leader)
981 else
982 dp = 0
983 end
984 elseif id == hlist_code then
985 local w, h, d, shift = getlistdimensions(current)
986 if getprop(current,"specialcontent") then
987
988 wd = w
989 dp = 0
990 else
991 wd = w
992 dp = d + shift
993 end
994 elseif id == vlist_code then
995 local shift
996 wd, ht, dp, shift = getlistdimensions(current)
997 dp = dp + shift
998 elseif id == rule_code then
999 if subtype == strutrule_code then
1000 dp = 0
1001 else
1002 wd, ht, dp = getwhd(current)
1003 end
1004 elseif id == math_code then
1005
1006 wd = getkern(current) + getwidth(current)
1007 dp = 0
1008 else
1009 goto done
1010 end
1011
1012 position = width
1013 width = position + wd
1014 p = floor((position - margin)/step + 0.5)
1015 w = floor((width + margin)/step - 0.5)
1016 if p < 0 then
1017 p = 0
1018 end
1019 if w < 0 then
1020 w = 0
1021 end
1022 if p > w then
1023 w, p = p, w
1024 end
1025 if w > max then
1026 for i=max+1,w+1 do
1027 profile[i] = 0
1028 end
1029 max = w
1030 end
1031 for i=p,w do
1032 if dp > profile[i] then
1033 profile[i] = dp
1034 end
1035 end
1036 ::done::
1037 end
1038 end
1039
1040 process(list)
1041
1042 return profile
1043
1044 end
1045
1046 local function getheightprofile(line,step,margin,max,list)
1047
1048 local width = 0
1049 local position = 0
1050 local profile = newindex(max+2,0)
1051 local wd = 0
1052 local ht = 0
1053 local dp = 0
1054
1055 profile[0] = 0
1056
1057 local function process(current)
1058 for current, id, subtype in nextnode, current do
1059 if id == glyph_code then
1060 wd, ht, dp = getwhd(current)
1061 elseif id == kern_code then
1062 wd = getkern(current)
1063 ht = 0
1064 elseif id == disc_code then
1065 local replace = getreplace(current)
1066 if replace then
1067 process(replace)
1068 end
1069 goto done
1070 elseif id == glue_code then
1071 wd = effectiveglue(current, line)
1072
1073 if subtype >= leaders_code then
1074 local leader = getleader(current)
1075 local w
1076 w, ht, dp = getwhd(leader)
1077 else
1078 ht = 0
1079 end
1080 elseif id == hlist_code then
1081 local w, h, d, shift = getlistdimensions(current)
1082 if getprop(current,"specialcontent") then
1083
1084 wd = w
1085 ht = 0
1086 else
1087 wd = w
1088 ht = h - shift
1089 end
1090 elseif id == vlist_code then
1091 local shift
1092 wd, ht, dp, shift = getlistdimensions(current)
1093 ht = ht - shift
1094 elseif id == rule_code then
1095 if subtype == strutrule_code then
1096 ht = 0
1097 else
1098 wd, ht, dp = getwhd(current)
1099 end
1100 elseif id == math_code then
1101
1102 wd = getkern(current) + getwidth(current)
1103 ht = 0
1104 else
1105 goto done
1106 end
1107
1108 position = width
1109 width = position + wd
1110 p = floor((position - margin)/step + 0.5)
1111 w = floor((width + margin)/step - 0.5)
1112 if p < 0 then
1113 p = 0
1114 end
1115 if w < 0 then
1116 w = 0
1117 end
1118 if p > w then
1119 w, p = p, w
1120 end
1121 if w > max then
1122 for i=max+1,w+1 do
1123 profile[i] = 0
1124 end
1125 max = w
1126 end
1127 for i=p,w do
1128 if ht > profile[i] then
1129 profile[i] = ht
1130 end
1131 end
1132 ::done::
1133 end
1134 end
1135
1136 process(list)
1137
1138 return profile
1139
1140 end
1141
1142 local show_lineprofile = false
1143 local show_linedetails = false
1144
1145 trackers.register("profiling.lines.show", function(v)
1146 local visualizers = nodes.visualizers
1147 glue_mode = visualizers.modes.glue
1148 line_mode = visualizers.modes.line
1149 show_lineprofile = v
1150 visualizers.enable()
1151 end)
1152
1153 trackers.register("profiling.lines.details", function(v)
1154 show_linedetail = v
1155 end)
1156
1157 local defaultstep = 65536 * 2
1158 local defaultmethod = "a"
1159 local defaultfactor = 1
1160
1161
1162
1163
1164 function profilelines(list,prev)
1165
1166 if not list then
1167 return
1168 end
1169
1170 local _, start = findattribute(list,a_lineprofile)
1171 if not start then
1172 return
1173 end
1174
1175
1176 for current, subtype in nextglue, start do
1177 if subtype == lineskip_code and not getprop(current,"profiled") then
1178 local detail = getattr(current,a_lineprofile)
1179 if detail then
1180 local amount = getwidth(current)
1181 if amount > 0 then
1182 detail = getvalue(a_lineprofile,detail) or { }
1183
1184 local top, bot = getboth(current)
1185 setprop(current,"profiled",amount)
1186 if not top and prev and detail.paragraph == v_yes then
1187 top = prev
1188 end
1189 if top then
1190 if getid(top) == penalty_code then
1191 top = getprev(top)
1192 end
1193 if top and bot then
1194 if getid(top) == hlist_code and getsubtype(top) == linelist_code then
1195 if getid(bot) == hlist_code and getsubtype(bot) == linelist_code then
1196 local toplist = getlist(top)
1197 local botlist = getlist(bot)
1198 if toplist and botlist then
1199
1200 local step = detail.step or defaultstep
1201 local factor = tonumber(detail.factor) or defaultfactor
1202 local method = detail.method or defaultmethod
1203 local margin = step / 4
1204
1205 if factor > 1 then
1206 factor = 1
1207 elseif factor <= 0 then
1208 factor = 0
1209 end
1210
1211 local natural = getdepth(top) + getheight(bot)
1212 local added = factor * amount
1213 local possible = natural - added
1214 local overshoot = 0
1215 local topmax = ceiling(getwidth(top)/step) + 1
1216 local botmax = ceiling(getwidth(bot)/step) + 1
1217
1218 local depths = getdepthprofile (top,step,margin,topmax,toplist)
1219 local heights = getheightprofile(bot,step,margin,botmax,botlist)
1220 local steps = min(#depths,#heights)
1221 for i=1,steps do
1222 local o = heights[i] + depths[i] - possible
1223 if o > overshoot then
1224
1225 overshoot = o
1226
1227
1228
1229 end
1230 end
1231
1232
1233
1234
1235 if overshoot ~= amount then
1236 setwidth(current,overshoot)
1237 if show_lineprofile then
1238 setattr(current,a_visual,glue_mode)
1239 setattr(bot,a_visual,line_mode)
1240 setattr(top,a_visual,line_mode)
1241 end
1242 if show_linedetail then
1243 report("lineskip changed from %p to %p on page %i",amount,overshoot,tex.getcount("realpageno"))
1244 end
1245 end
1246 end
1247 end
1248 end
1249 end
1250 end
1251 end
1252 end
1253 end
1254 prev = nil
1255 end
1256 end
1257
1258 builders.profiling.profilelines = profilelines
1259
1260 function profiling.boxlinehandler(head)
1261 if head then
1262 profilelines(head)
1263 end
1264 return head
1265 end
1266
1267 function profiling.pagelinehandler(head,...)
1268 if head then
1269 local h, t = getspeciallist("pagehead")
1270 profilelines(head,t)
1271 end
1272 return head
1273 end
1274
1275
1276
1277 function profiling.setlines(specification)
1278 if not enabled then
1279 enableaction("mvlbuilders", "builders.profiling.pagelinehandler")
1280 enableaction("vboxbuilders", "builders.profiling.boxlinehandler")
1281 enabled = true
1282 end
1283 texsetattribute(a_lineprofile,registervalue(a_lineprofile,specification))
1284 end
1285
1286 interfaces.implement {
1287 name = "setlineprofile",
1288 actions = profiling.setlines,
1289 arguments = {
1290 {
1291 { "name" },
1292 { "method" },
1293 { "step", "dimension" },
1294 { "factor" },
1295 { "paragraph" },
1296 }
1297 }
1298 }
1299
1300 interfaces.implement {
1301 name = "lineprofilebox",
1302 public = true,
1303 protected = true,
1304 actions = function(box)
1305 local okay = nuts.getbox(box)
1306 local list = getlist(okay)
1307 if list then
1308 profiling.boxlinehandler(list)
1309 end
1310 end,
1311 arguments = "integer"
1312 }
1313
1314end
1315 |