1if not modules then modules = { } end modules ['typo-mar'] = {
2 version = 1.001,
3 comment = "companion to typo-mar.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
13
14
15
16
17local format, validstring = string.format, string.valid
18local insert, remove, sortedkeys, fastcopy = table.insert, table.remove, table.sortedkeys, table.fastcopy
19local setmetatable, next, tonumber = setmetatable, next, tonumber
20local formatters = string.formatters
21local toboolean = toboolean
22local settings_to_hash = utilities.parsers.settings_to_hash
23
24local attributes = attributes
25local nodes = nodes
26local variables = variables
27local context = context
28
29local trace_margindata = false trackers.register("typesetters.margindata", function(v) trace_margindata = v end)
30local trace_marginstack = false trackers.register("typesetters.margindata.stack", function(v) trace_marginstack = v end)
31local trace_margingroup = false trackers.register("typesetters.margindata.group", function(v) trace_margingroup = v end)
32
33local report_margindata = logs.reporter("margindata")
34
35local tasks = nodes.tasks
36local prependaction = tasks.prependaction
37local disableaction = tasks.disableaction
38local enableaction = tasks.enableaction
39
40local variables = interfaces.variables
41
42local conditionals = tex.conditionals
43local systemmodes = tex.systemmodes
44
45local v_top <const> = variables.top
46local v_depth <const> = variables.depth
47local v_local <const> = variables["local"]
48local v_global <const> = variables["global"]
49local v_left <const> = variables.left
50local v_right <const> = variables.right
51local v_inner <const> = variables.inner
52local v_outer <const> = variables.outer
53local v_margin <const> = variables.margin
54local v_edge <const> = variables.edge
55local v_default <const> = variables.default
56local v_normal <const> = variables.normal
57local v_yes <const> = variables.yes
58local v_continue <const> = variables.continue
59local v_first <const> = variables.first
60local v_text <const> = variables.text
61local v_paragraph <const> = variables.paragraph
62local v_line <const> = variables.line
63
64local nuts = nodes.nuts
65local tonode = nuts.tonode
66
67local hpacknodes = nuts.hpack
68local traverseid = nuts.traverseid
69local flushnodelist = nuts.flushlist
70
71local getnext = nuts.getnext
72local getprev = nuts.getprev
73local getid = nuts.getid
74local getattr = nuts.getattr
75local setattr = nuts.setattr
76local getsubtype = nuts.getsubtype
77local getlist = nuts.getlist
78local getwhd = nuts.getwhd
79local setlist = nuts.setlist
80local setlink = nuts.setlink
81local getshift = nuts.getshift
82local setshift = nuts.setshift
83local getwidth = nuts.getwidth
84local setwidth = nuts.setwidth
85local getheight = nuts.getheight
86local getexcept = nuts.getexcept
87local setexcept = nuts.setexcept
88
89local margin_excepts = false
90
91directives.register("typesetters.margindata.excepts",function(v)
92 margin_excepts = v
93end)
94
95local setattrlist = nuts.setattrlist
96local takebox = nuts.takebox
97
98local setprop = nuts.setprop
99local getprop = nuts.getprop
100
101local nodecodes = nodes.nodecodes
102local listcodes = nodes.listcodes
103local whatsitcodes = nodes.whatsitcodes
104
105local hlist_code <const> = nodecodes.hlist
106local vlist_code <const> = nodecodes.vlist
107local whatsit_code <const> = nodecodes.whatsit
108local userdefined_code <const> = whatsitcodes.userdefined
109
110local nodepool = nuts.pool
111
112local new_hlist = nodepool.hlist
113local new_usernode = nodepool.usernode
114local latelua = nodepool.latelua
115
116local texgetdimen = tex.getdimen
117
118local texgetcount = tex.getcount
119local texget = tex.get
120
121local isleftpage = layouts.status.isleftpage
122local registertogether = builders.paragraphs.registertogether
123
124local paragraphs = typesetters.paragraphs
125local addtoline = paragraphs.addtoline
126local moveinline = paragraphs.moveinline
127local calculatedelta = paragraphs.calculatedelta
128
129local a_linenumber <const> = attributes.private('linenumber')
130
131local inline_mark <const> = nodepool.userids["margins.inline"]
132
133local jobpositions = job.positions
134local getposition = jobpositions.get
135local setposition = jobpositions.set
136local getreserved = jobpositions.getreserved
137
138local margins = { }
139typesetters.margins = margins
140
141local locations = { v_left, v_right, v_inner, v_outer }
142local categories = { }
143local displaystore = { }
144local inlinestore = { }
145local nofsaved = 0
146local nofstored = 0
147local nofinlined = 0
148local nofdelayed = 0
149local nofinjected = 0
150local h_anchors = 0
151local v_anchors = 0
152
153local mt1 = {
154 __index = function(t,location)
155 local v = { [v_local] = { }, [v_global] = { } }
156 t[location] = v
157 return v
158 end
159}
160
161local mt2 = {
162 __index = function(stores,category)
163 categories[#categories+1] = category
164 local v = { }
165 setmetatable(v,mt1)
166 stores[category] = v
167 return v
168 end
169}
170
171setmetatable(displaystore,mt2)
172
173local defaults = {
174 __index = {
175 location = v_left,
176 align = v_normal,
177 method = "",
178 name = "",
179 threshold = 0,
180 margin = v_normal,
181 scope = v_global,
182 distance = 0,
183 hoffset = 0,
184 voffset = 0,
185 category = v_default,
186 line = 0,
187 vstack = 0,
188 dy = 0,
189 baseline = false,
190 inline = false,
191 leftskip = 0,
192 rightskip = 0,
193 option = { }
194 }
195}
196
197local enablelocal, enableglobal
198
199local function showstore(store,banner,location)
200 if next(store) then
201 for i, si in table.sortedpairs(store) do
202 local si =store[i]
203 report_margindata("%s: stored in %a at %s: %a => %s",banner,location,i,validstring(si.name,"no name"),nodes.toutf(getlist(si.box)))
204 end
205 else
206 report_margindata("%s: nothing stored in location %a",banner,location)
207 end
208end
209
210function margins.save(t)
211 setmetatable(t,defaults)
212 local content = takebox(t.number)
213 local location = t.location
214 local category = t.category
215 local inline = t.inline
216 local scope = t.scope
217 local name = t.name
218 local option = t.option
219 local stack = t.stack
220 if option then
221 option = settings_to_hash(option)
222 t.option = option
223 end
224 if not content then
225 report_margindata("ignoring empty margin data %a",location or "unknown")
226 return
227 end
228 setprop(content,"specialcontent","margindata")
229 local store
230 if inline then
231 store = inlinestore
232 else
233 store = displaystore[category][location]
234 if not store then
235 report_margindata("invalid location %a",location)
236 return
237 end
238 store = store[scope]
239 end
240 if not store then
241 report_margindata("invalid scope %a",scope)
242 return
243 end
244 if enablelocal and scope == v_local then
245 enablelocal()
246 if enableglobal then
247 enableglobal()
248 end
249 elseif enableglobal and scope == v_global then
250 enableglobal()
251 end
252 nofsaved = nofsaved + 1
253 nofstored = nofstored + 1
254 if trace_marginstack then
255 showstore(store,"before",location)
256 end
257 if name and name ~= "" then
258
259 if inlinestore then
260 local t = sortedkeys(store) for j=#t,1,-1 do local i = t[j]
261 local si = store[i]
262 if si.name == name then
263 local s = remove(store,i)
264 flushnodelist(s.box)
265 end
266 end
267 else
268 for i=#store,1,-1 do
269 local si = store[i]
270 if si.name == name then
271 local s = remove(store,i)
272 flushnodelist(s.box)
273 end
274 end
275 end
276 if trace_marginstack then
277 showstore(store,"between",location)
278 end
279 end
280 if t.number then
281 local leftmargindistance = texgetdimen("naturalleftmargindistance")
282 local rightmargindistance = texgetdimen("naturalrightmargindistance")
283 local strutht = texgetdimen("strutht")
284 local strutdp = texgetdimen("strutdp")
285
286 t.box = content
287 t.n = nofsaved
288
289
290 t.strutheight = strutht
291 t.strutdepth = strutdp
292
293 t.leftskip = texget("leftskip",false)
294 t.rightskip = texget("rightskip",false)
295
296 t.leftmargindistance = leftmargindistance
297 t.rightmargindistance = rightmargindistance
298 t.leftedgedistance = texgetdimen("naturalleftedgedistance")
299 + texgetdimen("leftmarginwidth")
300 + leftmargindistance
301 t.rightedgedistance = texgetdimen("naturalrightedgedistance")
302 + texgetdimen("rightmarginwidth")
303 + rightmargindistance
304 t.lineheight = texgetdimen("lineheight")
305
306
307 if inline then
308 local n = new_usernode(inline_mark,nofsaved)
309 setattrlist(n,true)
310 context(tonode(n))
311 store[nofsaved] = t
312 nofinlined = nofinlined + 1
313 else
314 insert(store,t)
315 end
316 end
317 if trace_marginstack then
318 showstore(store,"after",location)
319 end
320 if trace_margindata then
321 report_margindata("saved %a, location %a, scope %a, inline %a",nofsaved,location,scope,inline)
322 end
323end
324
325
326
327
328
329
330
331
332
333local function realign(current,candidate)
334 local location = candidate.location
335 local margin = candidate.margin
336 local hoffset = candidate.hoffset
337 local distance = candidate.distance
338 local hsize = candidate.hsize
339 local width = candidate.width
340 local align = candidate.align
341 local inline = candidate.inline
342 local anchor = candidate.anchor
343 local hook = candidate.hook
344 local scope = candidate.scope
345 local option = candidate.option
346 local reverse = hook.reverse
347 local atleft = true
348 local hmove = 0
349 local delta = 0
350 local leftpage = isleftpage()
351 local leftdelta = 0
352 local rightdelta = 0
353 local leftdistance = distance
354 local rightdistance = distance
355
356 if not anchor or anchor == "" then
357 anchor = v_text
358 end
359 if margin == v_normal then
360
361 elseif margin == v_local then
362 leftdelta = - candidate.leftskip
363 rightdelta = candidate.rightskip
364 elseif margin == v_margin then
365 leftdistance = candidate.leftmargindistance
366 rightdistance = candidate.rightmargindistance
367 elseif margin == v_edge then
368 leftdistance = candidate.leftedgedistance
369 rightdistance = candidate.rightedgedistance
370 end
371 if leftpage then
372 leftdistance, rightdistance = rightdistance, leftdistance
373 end
374 if location == v_right then
375 atleft = false
376 elseif location == v_inner then
377 if leftpage then
378 atleft = false
379 end
380 elseif location == v_outer then
381 if not leftpage then
382 atleft = false
383 end
384 else
385
386 end
387
388 local islocal = scope == v_local
389 local area = (not islocal or option[v_text]) and anchor or nil
390
391 if atleft then
392 delta = hoffset + leftdelta + leftdistance
393 else
394 delta = hoffset + rightdelta + rightdistance
395 end
396
397 local delta, hmove = calculatedelta (
398 hook,
399 width,
400 delta,
401 atleft,
402 islocal,
403 option[v_paragraph],
404 area
405 )
406
407 if hmove ~= 0 then
408 delta = delta + hmove
409 if trace_margindata then
410 report_margindata("realigned %a, location %a, margin %a, move %p",candidate.n,location,margin,hmove)
411 end
412 else
413 if trace_margindata then
414 report_margindata("realigned %a, location %a, margin %a",candidate.n,location,margin)
415 end
416 end
417 moveinline(hook,candidate.node,delta)
418end
419
420local function realigned(current,candidate)
421 realign(current,candidate)
422 nofdelayed = nofdelayed - 1
423 setprop(current,"margindata",false)
424 return true
425end
426
427
428
429
430
431
432
433
434
435
436
437local validstacknames = {
438 [v_left ] = v_left ,
439 [v_right] = v_right,
440 [v_inner] = v_inner,
441 [v_outer] = v_outer,
442}
443
444local cache = { }
445local stacked = { [v_yes] = { }, [v_continue] = { } }
446local anchors = { [v_yes] = { }, [v_continue] = { } }
447
448local function resetstacked(all)
449 stacked[v_yes] = { }
450 anchors[v_yes] = { }
451 if all then
452 stacked[v_continue] = { }
453 anchors[v_continue] = { }
454 end
455end
456
457
458
459local function sa(specification)
460 local tag = specification.tag
461 local p = cache[tag]
462 if p then
463 if trace_marginstack then
464 report_margindata("updating anchor %a",tag)
465 end
466 p.p = true
467 p.y = true
468
469 setposition("md:v",tag,p)
470 cache[tag] = nil
471 end
472end
473
474local function setanchor(v_anchor)
475 return latelua { action = sa, tag = v_anchor }
476end
477
478local function aa(specification)
479 local tag = specification.tag
480 local n = specification.n
481 local p = jobpositions.gettobesaved('md:v',tag)
482 if p then
483 if trace_marginstack then
484 report_margindata("updating injected %a",tag)
485 end
486 local pages = p.pages
487 if not pages then
488 pages = { }
489 p.pages = pages
490 end
491 pages[n] = texgetcount("realpageno")
492 elseif trace_marginstack then
493 report_margindata("not updating injected %a",tag)
494 end
495end
496
497local function addtoanchor(v_anchor,n)
498 return latelua { action = aa, tag = v_anchor, n = n }
499end
500
501local function markovershoot(current)
502 v_anchors = v_anchors + 1
503 cache[v_anchors] = fastcopy(stacked)
504 local anchor = setanchor(v_anchors)
505
506
507 local list = hpacknodes(setlink(anchor,getlist(current)),getwidth(current),"exactly")
508 if trace_marginstack then
509 report_margindata("marking anchor %a",v_anchors)
510 end
511 setlist(current,list)
512end
513
514local function inject(parent,head,candidate)
515 local box = candidate.box
516 if not box then
517 return head, nil, false
518 end
519 local width, height, depth
520 = getwhd(box)
521 local shift = getshift(box)
522 local stack = candidate.stack
523 local stackname = candidate.stackname
524 local location = candidate.location
525 local method = candidate.method
526 local voffset = candidate.voffset
527 local line = candidate.line
528 local baseline = candidate.baseline
529 local strutheight = candidate.strutheight
530 local strutdepth = candidate.strutdepth
531 local inline = candidate.inline
532 local psubtype = getsubtype(parent)
533
534
535
536 if not stackname or stackname == "" then
537 stackname = location
538 else
539 stackname = validstacknames[stackname] or location
540 end
541 local isstacked = stack == v_continue or stack == v_yes
542 local offset = isstacked and stacked[stack][stackname]
543 local firstonstack = offset == false or offset == nil
544 nofinjected = nofinjected + 1
545 nofdelayed = nofdelayed + 1
546
547 baseline = tonumber(baseline)
548 if not baseline then
549 baseline = toboolean(baseline)
550 end
551
552 if baseline == true then
553 baseline = false
554 else
555 baseline = tonumber(baseline)
556 if not baseline or baseline <= 0 then
557
558 baseline = false
559 end
560 end
561 candidate.width = width
562 candidate.hsize = getwidth(parent)
563 candidate.psubtype = psubtype
564 candidate.stackname = stackname
565 if trace_margindata then
566 report_margindata("processing, index %s, height %p, depth %p, parent %a, method %a",candidate.n,height,depth,listcodes[psubtype],method)
567 end
568
569
570
571
572
573 if isstacked then
574 firstonstack = true
575 local anchor = getposition("md:v")
576 if anchor and (location == v_inner or location == v_outer) then
577 local pages = anchor.pages
578 if pages then
579 local page = pages[nofinjected]
580 if page then
581 if isleftpage(page) then
582 stackname = location == v_inner and v_right or v_left
583 else
584 stackname = location == v_inner and v_left or v_right
585 end
586 candidate.stackname = stackname
587 offset = stack and stack ~= "" and stacked[stack][stackname]
588 end
589 end
590 end
591 local current = v_anchors + 1
592 local previous = anchors[stack][stackname]
593 if trace_margindata then
594 report_margindata("anchor %i, offset so far %p",current,offset or 0)
595 end
596 local ap = anchor and anchor[previous]
597 local ac = anchor and anchor[current]
598 if not previous then
599 elseif previous == current then
600 firstonstack = false
601 elseif ap and ac and ap.p == ac.p then
602 local distance = (ap.y or 0) - (ac.y or 0)
603 if trace_margindata then
604 report_margindata("distance %p",distance)
605 end
606 if offset > distance then
607
608 offset = offset - distance
609 firstonstack = false
610 else
611 offset = 0
612 end
613 else
614
615 end
616 anchors[v_yes] [stackname] = current
617 anchors[v_continue][stackname] = current
618 if firstonstack then
619 offset = 0
620 end
621 offset = offset + candidate.dy
622 shift = shift + offset
623 else
624 if firstonstack then
625 offset = 0
626 end
627 offset = offset + candidate.dy
628 shift = shift + offset
629 end
630
631 if margin_excepts then
632
633 local except, exdepth = getexcept(parent)
634 exdepth = exdepth + height + depth
635 setexcept(parent,except,exdepth)
636 end
637
638
639
640
641
642 if method == v_top then
643 local delta = height - getheight(parent)
644 if trace_margindata then
645 report_margindata("top aligned by %p",delta)
646 end
647 if delta < candidate.threshold then
648 shift = shift + voffset + delta
649 end
650 elseif method == v_line then
651 local _, ph, pd = getwhd(parent)
652 if pd == 0 then
653 local delta = height - ph
654 if trace_margindata then
655 report_margindata("top aligned by %p (no depth)",delta)
656 end
657 if delta < candidate.threshold then
658 shift = shift + voffset + delta
659 end
660 end
661 elseif method == v_first then
662 if baseline then
663 shift = shift + voffset + height - baseline
664 else
665 shift = shift + voffset
666 end
667 if trace_margindata then
668 report_margindata("first aligned")
669 end
670 elseif method == v_depth then
671 local delta = strutdepth
672 if trace_margindata then
673 report_margindata("depth aligned by %p",delta)
674 end
675 shift = shift + voffset + delta
676 elseif method == v_height then
677 local delta = - strutheight
678 if trace_margindata then
679 report_margindata("height aligned by %p",delta)
680 end
681 shift = shift + voffset + delta
682 elseif voffset ~= 0 then
683 if trace_margindata then
684 report_margindata("voffset %p applied",voffset)
685 end
686 shift = shift + voffset
687 end
688
689 if line ~= 0 then
690 local delta = line * candidate.lineheight
691 if trace_margindata then
692 report_margindata("offset %p applied to line %s",delta,line)
693 end
694 shift = shift + delta
695 offset = offset + delta
696 end
697 setshift(box,shift)
698 setwidth(box,0)
699
700 if isstacked then
701 setlink(box,addtoanchor(v_anchors,nofinjected))
702 box = new_hlist(box)
703
704 end
705
706 candidate.hook, candidate.node = addtoline(parent,box)
707
708 setprop(box,"margindata",candidate)
709 if trace_margindata then
710 report_margindata("injected, location %a, stack %a, shift %p",location,stackname,shift)
711 end
712
713 offset = offset + depth
714 local room = {
715 height = height,
716 depth = offset,
717 slack = candidate.bottomspace,
718 lineheight = candidate.lineheight,
719 stacked = inline and isstacked,
720 }
721 offset = offset + height
722
723 stacked[v_yes] [stackname] = offset
724 stacked[v_continue][stackname] = offset
725
726 if trace_margindata then
727 report_margindata("status, offset %s",offset)
728 end
729 return getlist(parent), room, inline and isstacked or (stack == v_continue)
730end
731
732local function flushinline(parent,head)
733 local current = head
734 local done = false
735 local continue = false
736 local room, don, con, list
737 while current and nofinlined > 0 do
738 local id = getid(current)
739 if id == whatsit_code then
740 if getsubtype(current) == userdefined_code and getprop(current,"id") == inline_mark then
741 local n = getprop(current,"data")
742 local candidate = inlinestore[n]
743 if candidate then
744 inlinestore[n] = nil
745 nofinlined = nofinlined - 1
746 head, room, con = inject(parent,head,candidate)
747 done = true
748 continue = continue or con
749 nofstored = nofstored - 1
750 if room and room.stacked then
751
752
753
754 registertogether(parent,room)
755 end
756 end
757 end
758 elseif id == hlist_code or id == vlist_code then
759
760 list, don, con = flushinline(current,getlist(current))
761 setlist(current,list)
762 continue = continue or con
763 done = done or don
764 end
765 current = getnext(current)
766 end
767 return head, done, continue
768end
769
770local function flushed(scope,parent)
771 local head = getlist(parent)
772 local done = false
773 local continue = false
774 local room, con, don
775 for c=1,#categories do
776 local category = categories[c]
777 for l=1,#locations do
778 local location = locations[l]
779 local store = displaystore[category][location][scope]
780 if store then
781 while true do
782 local candidate = remove(store,1)
783 if candidate then
784 head, room, con = inject(parent,head,candidate)
785 done = true
786 continue = continue or con
787 nofstored = nofstored - 1
788 if room then
789 registertogether(parent,room)
790 end
791 else
792 break
793 end
794 end
795 else
796
797 end
798 end
799 end
800 if nofinlined > 0 then
801 if done then
802 setlist(parent,head)
803 end
804 head, don, con = flushinline(parent,head)
805 continue = continue or con
806 done = done or don
807 end
808 if done then
809 local a = getattr(head,a_linenumber)
810 if false then
811 local l = hpacknodes(head,getwidth(parent),"exactly")
812 setlist(parent,l)
813 if a then
814 setattr(l,a_linenumber,a)
815 end
816 else
817
818 setlist(parent,head)
819 if a then
820 setattr(parent,a_linenumber,a)
821 end
822 end
823 end
824 return done, continue
825end
826
827
828
829
830local function handler(scope,head,group)
831 if nofstored > 0 then
832 if trace_margindata then
833 report_margindata("flushing stage one, stored %s, scope %s, delayed %s, group %a",nofstored,scope,nofdelayed,group)
834 end
835 local current = head
836 local done = false
837 while current do
838 local id = getid(current)
839 if (id == vlist_code or id == hlist_code) and getprop(current,"margindata") == nil then
840 local don, continue = flushed(scope,current)
841 if don then
842 done = true
843 setprop(current,"margindata",false)
844 if continue then
845 markovershoot(current)
846 end
847 if nofstored <= 0 then
848 break
849 end
850 end
851 end
852 current = getnext(current)
853 end
854 if trace_margindata then
855 if done then
856 report_margindata("flushing stage one, done, %s left",nofstored)
857 else
858 report_margindata("flushing stage one, nothing done, %s left",nofstored)
859 end
860 end
861 resetstacked()
862 end
863 return head
864end
865
866local trialtypesetting = context.trialtypesetting
867
868
869
870
871function margins.localhandler(head,group)
872
873 if trialtypesetting() then
874 return head
875 end
876
877 local inhibit = conditionals.inhibitmargindata
878 if inhibit then
879 if trace_margingroup then
880 report_margindata("ignored 3, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
881 end
882 return head
883 end
884 if nofstored > 0 then
885 return handler(v_local,head,group)
886 end
887 if trace_margingroup then
888 report_margindata("ignored 4, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
889 end
890 return head
891end
892
893function margins.globalhandler(head,group)
894
895 if trialtypesetting() then
896 return head, false
897 end
898
899 local inhibit = conditionals.inhibitmargindata
900 if inhibit or nofstored == 0 then
901 if trace_margingroup then
902 report_margindata("ignored 1, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
903 end
904 return head
905 elseif group == "hmodepar" then
906 return handler(v_global,head,group)
907 elseif group == "vmodepar" then
908 return handler(v_global,head,group)
909
910
911 elseif group == "box" then
912 return handler(v_global,head,group)
913 elseif group == "alignment" then
914 return handler(v_global,head,group)
915 else
916 if trace_margingroup then
917 report_margindata("ignored 2, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
918 end
919 return head
920 end
921end
922
923local function finalhandler(head)
924 if nofdelayed > 0 then
925 local current = head
926 while current and nofdelayed > 0 do
927 local id = getid(current)
928 if id == hlist_code then
929 local a = getprop(current,"margindata")
930 if not a then
931 finalhandler(getlist(current))
932 elseif realigned(current,a) then
933 if nofdelayed == 0 then
934 return head, true
935 end
936 end
937 elseif id == vlist_code then
938 finalhandler(getlist(current))
939 end
940 current = getnext(current)
941 end
942 end
943 return head
944end
945
946function margins.finalhandler(head)
947 if nofdelayed > 0 then
948 if trace_margindata then
949 report_margindata("flushing stage two, instore: %s, delayed: %s",nofstored,nofdelayed)
950 end
951 head = finalhandler(head)
952 resetstacked(nofdelayed==0)
953 else
954 resetstacked()
955 end
956 return head
957end
958
959
960
961
962enablelocal = function()
963 enableaction("finalizers", "typesetters.margins.localhandler")
964 enableaction("shipouts", "typesetters.margins.finalhandler")
965 enablelocal = nil
966end
967
968enableglobal = function()
969 enableaction("mvlbuilders", "typesetters.margins.globalhandler")
970 enableaction("shipouts", "typesetters.margins.finalhandler")
971 enableglobal = nil
972end
973
974statistics.register("margin data", function()
975 if nofsaved > 0 then
976 return format("%s entries, %s pending",nofsaved,nofdelayed)
977 else
978 return nil
979 end
980end)
981
982interfaces.implement {
983 name = "savemargindata",
984 actions = margins.save,
985 arguments = {
986 {
987 { "location" },
988 { "method" },
989 { "category" },
990 { "name" },
991 { "scope" },
992 { "number", "integer" },
993 { "margin" },
994 { "distance", "dimen" },
995 { "hoffset", "dimen" },
996 { "voffset", "dimen" },
997 { "dy", "dimen" },
998 { "bottomspace", "dimen" },
999 { "baseline"},
1000 { "threshold", "dimen" },
1001 { "inline", "boolean" },
1002 { "anchor" },
1003
1004
1005 { "align" },
1006 { "option" },
1007 { "line", "integer" },
1008 { "index", "integer" },
1009 { "stackname" },
1010 { "stack" },
1011 }
1012 }
1013}
1014 |