1if not modules then modules = { } end modules ['tabl-xtb'] = {
2 version = 1.001,
3 comment = "companion to tabl-xtb.mkvi",
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
25
26
27
28
29local tonumber, next = tonumber, next
30
31local commands = commands
32local context = context
33local ctxnode = context.nodes.flush
34
35local implement = interfaces.implement
36
37local tex = tex
38local texgetcount = tex.getcount
39local texsetcount = tex.setcount
40local texiscount = tex.iscount
41local texgetdimen = tex.getdimen
42local texsetdimen = tex.setdimen
43local texisdimen = tex.isdimen
44local texget = tex.get
45
46local format = string.format
47local concat = table.concat
48local points = number.points
49
50local todimen = string.todimen
51
52local ctx_beginvbox = context.beginvbox
53local ctx_endvbox = context.endvbox
54local ctx_blank = context.blank
55local ctx_nointerlineskip = context.nointerlineskip
56local ctx_dummyxcell = context.dummyxcell
57
58local variables = interfaces.variables
59
60local setmetatableindex = table.setmetatableindex
61local settings_to_hash = utilities.parsers.settings_to_hash
62
63local nuts = nodes.nuts
64local tonut = nuts.tonut
65local tonode = nuts.tonode
66
67local getnext = nuts.getnext
68local getprev = nuts.getprev
69local getlist = nuts.getlist
70local getwidth = nuts.getwidth
71local getbox = nuts.getbox
72local getwhd = nuts.getwhd
73
74local setlink = nuts.setlink
75local setdirection = nuts.setdirection
76local setshift = nuts.setshift
77
78local copynodelist = nuts.copylist
79local hpacknodelist = nuts.hpack
80local flushnodelist = nuts.flushlist
81local takebox = nuts.takebox
82local migratebox = nuts.migratebox
83
84local nodepool = nuts.pool
85
86local new_glue = nodepool.glue
87local new_kern = nodepool.kern
88local new_hlist = nodepool.hlist
89
90local lefttoright_code = tex.directioncodes.lefttoright
91
92local v_stretch = variables.stretch
93local v_normal = variables.normal
94local v_width = variables.width
95local v_height = variables.height
96local v_repeat = variables["repeat"]
97local v_max = variables.max
98local v_fixed = variables.fixed
99
100local v_before = variables.before
101local v_after = variables.after
102local v_both = variables.both
103local v_samepage = variables.samepage
104local v_tight = variables.tight
105
106local xtables = { }
107typesetters.xtables = xtables
108
109local trace_xtable = false
110local report_xtable = logs.reporter("xtable")
111
112trackers.register("xtable.construct", function(v) trace_xtable = v end)
113
114local c_tabl_x_nx = texiscount("c_tabl_x_nx")
115local c_tabl_x_ny = texiscount("c_tabl_x_ny")
116local c_tabl_x_state = texiscount("c_tabl_x_state")
117local c_tabl_x_mode = texiscount("c_tabl_x_mode")
118local c_tabl_x_skip_mode = texiscount("c_tabl_x_skip_mode")
119local d_tabl_x_final_width = texisdimen("d_tabl_x_final_width")
120local d_tabl_x_distance = texisdimen("d_tabl_x_distance")
121local d_tabl_x_width = texisdimen("d_tabl_x_width")
122local d_tabl_x_height = texisdimen("d_tabl_x_height")
123local d_tabl_x_depth = texisdimen("d_tabl_x_depth")
124local d_lineheight = texisdimen("lineheight")
125
126local c_frameddimensionstate = texiscount("frameddimensionstate")
127
128local head_mode = 1
129local foot_mode = 2
130local more_mode = 3
131local body_mode = 4
132
133local namedmodes = { [0] =
134 "null",
135 "head",
136 "foot",
137 "next",
138 "body",
139}
140
141local stack, data = { }, nil
142
143function xtables.create(settings)
144 table.insert(stack,data)
145 local rows = { }
146 local widths = { }
147 local heights = { }
148 local depths = { }
149
150 local distances = { }
151 local autowidths = { }
152 local modes = { }
153 local fixedrows = { }
154 local fixedcolumns = { }
155
156 local frozencolumns = { }
157 local options = { }
158 local rowproperties = { }
159 data = {
160 rows = rows,
161 widths = widths,
162 heights = heights,
163 depths = depths,
164
165 distances = distances,
166 modes = modes,
167 autowidths = autowidths,
168 fixedrows = fixedrows,
169 fixedcolumns = fixedcolumns,
170
171 frozencolumns = frozencolumns,
172 options = options,
173 nofrows = 0,
174 nofcolumns = 0,
175 currentrow = 0,
176 currentcolumn = 0,
177 settings = settings or { },
178 rowproperties = rowproperties,
179 }
180 local function add_zero(t,k)
181 t[k] = 0
182 return 0
183 end
184 local function add_table(t,k)
185 local v = { }
186 t[k] = v
187 return v
188 end
189 local function add_cell(row,c)
190 local cell = {
191 nx = 0,
192 ny = 0,
193 list = false,
194 wd = 0,
195 ht = 0,
196 dp = 0,
197 }
198 row[c] = cell
199 if c > data.nofcolumns then
200 data.nofcolumns = c
201 end
202 return cell
203 end
204 local function add_row(rows,r)
205 local row = { }
206 setmetatableindex(row,add_cell)
207 rows[r] = row
208 if r > data.nofrows then
209 data.nofrows = r
210 end
211 return row
212 end
213 setmetatableindex(rows,add_row)
214 setmetatableindex(widths,add_zero)
215 setmetatableindex(heights,add_zero)
216 setmetatableindex(depths,add_zero)
217 setmetatableindex(distances,add_zero)
218 setmetatableindex(modes,add_zero)
219 setmetatableindex(fixedrows,add_zero)
220 setmetatableindex(fixedcolumns,add_zero)
221 setmetatableindex(options,add_table)
222
223
224 local globaloptions = settings_to_hash(settings.option)
225
226 settings.columndistance = tonumber(settings.columndistance) or 0
227 settings.rowdistance = tonumber(settings.rowdistance) or 0
228 settings.leftmargindistance = tonumber(settings.leftmargindistance) or 0
229 settings.rightmargindistance = tonumber(settings.rightmargindistance) or 0
230 settings.options = globaloptions
231 settings.textwidth = tonumber(settings.textwidth) or texget("hsize")
232 settings.lineheight = tonumber(settings.lineheight) or texgetdimen(d_lineheight)
233 settings.maxwidth = tonumber(settings.maxwidth) or settings.textwidth/8
234
235
236
237 data.criterium_v = 2 * data.settings.lineheight
238 data.criterium_h = .75 * data.settings.textwidth
239
240 data.tight = globaloptions[v_tight] and true or false
241end
242
243function xtables.initialize_reflow_width(option,width)
244 local r = data.currentrow
245 local c = data.currentcolumn + 1
246 local drc = data.rows[r][c]
247 drc.nx = texgetcount(c_tabl_x_nx)
248 drc.ny = texgetcount(c_tabl_x_ny)
249 local distances = data.distances
250 local distance = texgetdimen(d_tabl_x_distance)
251 if distance > distances[c] then
252 distances[c] = distance
253 end
254 if option and option ~= "" then
255 local options = settings_to_hash(option)
256 data.options[r][c] = options
257 if options[v_fixed] then
258 data.frozencolumns[c] = true
259 end
260 end
261 data.currentcolumn = c
262end
263
264
265
266function xtables.set_reflow_width()
267 local r = data.currentrow
268 local c = data.currentcolumn
269 local rows = data.rows
270 local row = rows[r]
271 local cold = c
272 while row[c].span do
273 c = c + 1
274 end
275
276 if c > cold then
277 local ro = row[cold]
278 local rx = ro.nx
279 local ry = ro.ny
280 if rx > 1 or ry > 1 then
281 local rn = row[c]
282 rn.nx = rx
283 rn.ny = ry
284 ro.nx = 1
285 ro.ny = 1
286
287 end
288 end
289 local tb = getbox("b_tabl_x")
290 local drc = row[c]
291
292 drc.list = true
293
294 local width, height, depth = getwhd(tb)
295
296 local widths = data.widths
297 local heights = data.heights
298 local depths = data.depths
299 local cspan = drc.nx
300 if cspan < 2 then
301 if width > widths[c] then
302 widths[c] = width
303 end
304 else
305 local options = data.options[r][c]
306 if data.tight then
307
308 elseif not options then
309 if width > widths[c] then
310 widths[c] = width
311 end
312 elseif not options[v_tight] then
313 if width > widths[c] then
314 widths[c] = width
315 end
316 end
317 end
318
319
320
321
322
323
324
325 if drc.ny < 2 then
326
327
328 if height > heights[r] then
329 heights[r] = height
330 end
331 if depth > depths[r] then
332 depths[r] = depth
333 end
334 end
335
336 drc.wd = width
337 drc.ht = height
338 drc.dp = depth
339
340 local dimensionstate = texgetcount(c_frameddimensionstate)
341 local fixedcolumns = data.fixedcolumns
342 local fixedrows = data.fixedrows
343 if dimensionstate == 1 then
344 if cspan > 1 then
345
346 elseif width > fixedcolumns[c] then
347 fixedcolumns[c] = width
348 end
349 elseif dimensionstate == 2 then
350 fixedrows[r] = height
351 elseif dimensionstate == 3 then
352 fixedrows[r] = height
353 fixedcolumns[c] = width
354 elseif width <= data.criterium_h and height >= data.criterium_v then
355
356 if width > fixedcolumns[c] then
357
358 fixedcolumns[c] = width
359 end
360 end
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394 drc.dimensionstate = dimensionstate
395
396 local nx = drc.nx
397 local ny = drc.ny
398 if nx > 1 or ny > 1 then
399
400 local self = true
401 for y=1,ny do
402 for x=1,nx do
403 if self then
404 self = false
405 else
406 local ry = r + y - 1
407 local cx = c + x - 1
408
409
410
411 rows[ry][cx].span = true
412 end
413 end
414 end
415 c = c + nx - 1
416 end
417 if c > data.nofcolumns then
418 data.nofcolumns = c
419 end
420 data.currentcolumn = c
421end
422
423function xtables.initialize_reflow_height()
424 local r = data.currentrow
425 local c = data.currentcolumn + 1
426 local rows = data.rows
427 local row = rows[r]
428 while row[c].span do
429 c = c + 1
430 end
431 data.currentcolumn = c
432 local widths = data.widths
433 local w = widths[c]
434 local drc = row[c]
435 for x=1,drc.nx-1 do
436 w = w + widths[c+x]
437 end
438 texsetdimen(d_tabl_x_width,w)
439 local dimensionstate = drc.dimensionstate or 0
440 if dimensionstate == 1 or dimensionstate == 3 then
441
442 texsetcount(c_tabl_x_skip_mode,1)
443 elseif dimensionstate == 2 then
444
445 texsetcount(c_tabl_x_skip_mode,1)
446 elseif data.autowidths[c] then
447
448 texsetcount(c_tabl_x_skip_mode,0)
449 elseif data.fixedcolumns[c] then
450 texsetcount(c_tabl_x_skip_mode,0)
451 else
452 texsetcount(c_tabl_x_skip_mode,1)
453 end
454end
455
456function xtables.set_reflow_height()
457 local r = data.currentrow
458 local c = data.currentcolumn
459 local rows = data.rows
460 local row = rows[r]
461
462
463
464 local tb = getbox("b_tabl_x")
465 local drc = row[c]
466
467 local width, height, depth = getwhd(tb)
468
469 if drc.ny < 2 then
470 if data.fixedrows[r] == 0 then
471 if drc.ht + drc.dp <= height + depth then
472 local heights = data.heights
473 local depths = data.depths
474
475
476 if height > heights[r] then
477 heights[r] = height
478 end
479 if depth > depths[r] then
480 depths[r] = depth
481 end
482 end
483 end
484 end
485
486 drc.wd = width
487 drc.ht = height
488 drc.dp = depth
489
490
491
492end
493
494function xtables.initialize_construct()
495 local r = data.currentrow
496 local c = data.currentcolumn + 1
497 local settings = data.settings
498 local rows = data.rows
499 local row = rows[r]
500 while row[c].span do
501 c = c + 1
502 end
503 data.currentcolumn = c
504 local widths = data.widths
505 local heights = data.heights
506 local depths = data.depths
507 local distances = data.distances
508
509 local drc = row[c]
510 local wd = drc.wd
511 local ht = drc.ht
512 local dp = drc.dp
513 local nx = drc.nx - 1
514 local ny = drc.ny - 1
515
516 local width = widths[c]
517 local height = heights[r]
518 local depth = depths[r]
519
520 local total = height + depth
521
522 if nx > 0 then
523 for x=1,nx do
524 width = width + widths[c+x] + distances[c+x-1]
525 end
526 local distance = settings.columndistance
527 if distance ~= 0 then
528 width = width + nx * distance
529 end
530 end
531
532 if ny > 0 then
533 for y=1,ny do
534 local nxt = r + y
535 total = total + heights[nxt] + depths[nxt]
536 end
537 local distance = settings.rowdistance
538 if distance ~= 0 then
539 total = total + ny * distance
540 end
541 end
542
543 texsetdimen(d_tabl_x_width,width)
544 texsetdimen(d_tabl_x_height,total)
545 texsetdimen(d_tabl_x_depth,0)
546end
547
548function xtables.set_construct()
549 local r = data.currentrow
550 local c = data.currentcolumn
551 local rows = data.rows
552 local row = rows[r]
553
554
555
556 local drc = row[c]
557
558 drc.list = takebox("b_tabl_x")
559
560
561end
562
563local function showwidths(where,widths,autowidths)
564 local result = { }
565 for i=1,#widths do
566 result[i] = format("%12s%s",points(widths[i]),autowidths[i] and "*" or " ")
567 end
568 return report_xtable("%s widths: %s",where,concat(result," "))
569end
570
571function xtables.reflow_width()
572 local nofrows = data.nofrows
573 local nofcolumns = data.nofcolumns
574 local rows = data.rows
575 for r=1,nofrows do
576 local row = rows[r]
577 for c=1,nofcolumns do
578 local drc = row[c]
579 if drc.list then
580
581 drc.list = false
582 end
583 end
584 end
585
586 local settings = data.settings
587 local options = settings.options
588 local maxwidth = settings.maxwidth
589
590 local widths = data.widths
591 local heights = data.heights
592 local depths = data.depths
593 local distances = data.distances
594 local autowidths = data.autowidths
595 local fixedcolumns = data.fixedcolumns
596 local frozencolumns = data.frozencolumns
597 local width = 0
598 local distance = 0
599 local nofwide = 0
600 local widetotal = 0
601 local available = settings.textwidth - settings.leftmargindistance - settings.rightmargindistance
602 if trace_xtable then
603 showwidths("stage 1",widths,autowidths)
604 end
605 local noffrozen = 0
606
607 if options[v_max] then
608 for c=1,nofcolumns do
609 width = width + widths[c]
610 if width > maxwidth then
611 autowidths[c] = true
612 nofwide = nofwide + 1
613 widetotal = widetotal + widths[c]
614 end
615 if c < nofcolumns then
616 distance = distance + distances[c]
617 end
618 if frozencolumns[c] then
619 noffrozen = noffrozen + 1
620 end
621 end
622 else
623 for c=1,nofcolumns do
624 local fixedwidth = fixedcolumns[c]
625 if fixedwidth > 0 then
626 widths[c] = fixedwidth
627 width = width + fixedwidth
628 else
629 local wc = widths[c]
630 width = width + wc
631
632 if wc > maxwidth then
633 autowidths[c] = true
634 nofwide = nofwide + 1
635 widetotal = widetotal + wc
636 end
637 end
638 if c < nofcolumns then
639 distance = distance + distances[c]
640 end
641 if frozencolumns[c] then
642 noffrozen = noffrozen + 1
643 end
644 end
645 end
646 if trace_xtable then
647 showwidths("stage 2",widths,autowidths)
648 end
649 local delta = available - width - distance - (nofcolumns-1) * settings.columndistance
650 if delta == 0 then
651
652 if trace_xtable then
653 report_xtable("perfect fit")
654 end
655 elseif delta > 0 then
656
657 if not options[v_stretch] then
658
659 if trace_xtable then
660 report_xtable("too wide but no stretch, delta %p",delta)
661 end
662 elseif options[v_width] then
663 local factor = delta / width
664 if trace_xtable then
665 report_xtable("proportional stretch, delta %p, width %p, factor %a",delta,width,factor)
666 end
667 for c=1,nofcolumns do
668 widths[c] = widths[c] + factor * widths[c]
669 end
670 else
671
672 local extra = delta / (nofcolumns - noffrozen)
673 if trace_xtable then
674 report_xtable("normal stretch, delta %p, extra %p",delta,extra)
675 end
676 for c=1,nofcolumns do
677 if not frozencolumns[c] then
678 widths[c] = widths[c] + extra
679 end
680 end
681 end
682 elseif nofwide > 0 then
683 while true do
684 done = false
685 local available = (widetotal + delta) / nofwide
686 if trace_xtable then
687 report_xtable("shrink check, total %p, delta %p, columns %s, fixed %p",widetotal,delta,nofwide,available)
688 end
689 for c=1,nofcolumns do
690 if autowidths[c] and available >= widths[c] then
691 autowidths[c] = nil
692 nofwide = nofwide - 1
693 widetotal = widetotal - widths[c]
694 done = true
695 end
696 end
697 if not done then
698 break
699 end
700 end
701
702
703 if options[v_width] then
704 local factor = (widetotal + delta) / width
705 if trace_xtable then
706 report_xtable("proportional shrink used, total %p, delta %p, columns %s, factor %s",widetotal,delta,nofwide,factor)
707 end
708 for c=1,nofcolumns do
709 if autowidths[c] then
710 widths[c] = factor * widths[c]
711 end
712 end
713 else
714 local available = (widetotal + delta) / nofwide
715 if trace_xtable then
716 report_xtable("normal shrink used, total %p, delta %p, columns %s, fixed %p",widetotal,delta,nofwide,available)
717 end
718 for c=1,nofcolumns do
719 if autowidths[c] then
720 widths[c] = available
721 end
722 end
723 end
724 end
725 if trace_xtable then
726 showwidths("stage 3",widths,autowidths)
727 end
728
729 data.currentrow = 0
730 data.currentcolumn = 0
731end
732
733function xtables.reflow_height()
734 data.currentrow = 0
735 data.currentcolumn = 0
736 local settings = data.settings
737
738
739
740 local nofrows = data.nofrows
741 local nofcolumns = data.nofcolumns
742 local widths = data.widths
743 local heights = data.heights
744 local depths = data.depths
745
746 for r=1,nofrows do
747 for c=1,nofcolumns do
748 local drc = data.rows[r][c]
749 if drc then
750 local ny = drc.ny
751 if ny > 1 then
752 local height = heights[r]
753 local depth = depths[r]
754 local total = height + depth
755 local htdp = drc.ht + drc.dp
756 for y=1,ny-1 do
757 local nxt = r + y
758 total = total + heights[nxt] + depths[nxt]
759 end
760 local delta = htdp - total
761 if delta > 0 then
762 delta = delta / ny
763 for y=0,ny-1 do
764 local nxt = r + y
765 heights[nxt] = heights[nxt] + delta
766 end
767 end
768 end
769 end
770 end
771 end
772
773 if settings.options[v_height] then
774 local totalheight = 0
775 local totaldepth = 0
776 for i=1,nofrows do
777 totalheight = totalheight + heights[i]
778 totalheight = totalheight + depths [i]
779 end
780 local total = totalheight + totaldepth
781 local leftover = settings.textheight - total
782 if leftover > 0 then
783 local leftheight = (totalheight / total) * leftover / #heights
784 local leftdepth = (totaldepth / total) * leftover / #depths
785 for i=1,nofrows do
786 heights[i] = heights[i] + leftheight
787 depths [i] = depths [i] + leftdepth
788 end
789 end
790 end
791end
792
793local function showspans(data)
794 local rows = data.rows
795 local modes = data.modes
796 local nofcolumns = data.nofcolumns
797 local nofrows = data.nofrows
798 for r=1,nofrows do
799 local line = { }
800 local row = rows[r]
801 for c=1,nofcolumns do
802 local cell =row[c]
803 if cell.list then
804 line[#line+1] = "list"
805 elseif cell.span then
806 line[#line+1] = "span"
807 else
808 line[#line+1] = "none"
809 end
810 end
811 report_xtable("%3d : %s : % t",r,namedmodes[modes[r]] or "----",line)
812 end
813end
814
815function xtables.construct()
816 local rows = data.rows
817 local heights = data.heights
818 local depths = data.depths
819 local widths = data.widths
820
821 local distances = data.distances
822 local modes = data.modes
823 local settings = data.settings
824 local nofcolumns = data.nofcolumns
825 local nofrows = data.nofrows
826 local columndistance = settings.columndistance
827 local rowdistance = settings.rowdistance
828 local leftmargindistance = settings.leftmargindistance
829 local rightmargindistance = settings.rightmargindistance
830 local rowproperties = data.rowproperties
831
832
833 if trace_xtable then
834 showspans(data)
835 end
836
837 local ranges = {
838 [head_mode] = { },
839 [foot_mode] = { },
840 [more_mode] = { },
841 [body_mode] = { },
842 }
843 for r=1,nofrows do
844 local m = modes[r]
845 if m == 0 then
846 m = body_mode
847 end
848 local range = ranges[m]
849 range[#range+1] = r
850 end
851
852
853
854 local function packaged_column(r)
855 local row = rows[r]
856 local start = nil
857 local stop = nil
858 if leftmargindistance > 0 then
859 start = new_kern(leftmargindistance)
860 stop = start
861 end
862 local hasspan = false
863 for c=1,nofcolumns do
864 local drc = row[c]
865 if not hasspan then
866 hasspan = drc.span
867 end
868 local list = drc.list
869 if list then
870 local w, h, d = getwhd(list)
871 setshift(list,h+d)
872
873
874
875 local h = new_hlist(list)
876 migratebox(list,h)
877 list = h
878
879 if start then
880 setlink(stop,list)
881 else
882 start = list
883 end
884 stop = list
885 end
886 local step = widths[c]
887 if c < nofcolumns then
888 step = step + columndistance + distances[c]
889 end
890 local kern = new_kern(step)
891 if stop then
892 setlink(stop,kern)
893 else
894 start = kern
895 end
896 stop = kern
897 end
898 if start then
899 if rightmargindistance > 0 then
900 local kern = new_kern(rightmargindistance)
901 setlink(stop,kern)
902
903 end
904 return start, heights[r] + depths[r], hasspan
905 end
906 end
907 local function collect_range(range)
908 local result, nofr = { }, 0
909 local nofrange = #range
910 for i=1,#range do
911 local r = range[i]
912
913 local list, size, hasspan = packaged_column(r)
914 if list then
915 if hasspan and nofr > 0 then
916 result[nofr][4] = true
917 end
918 nofr = nofr + 1
919 local rp = rowproperties[r]
920
921
922 local hbox = hpacknodelist(list)
923 setdirection(hbox,lefttoright_code)
924 result[nofr] = {
925 hbox,
926 size,
927 i < nofrange and rowdistance > 0 and rowdistance or false,
928 false,
929 rp or false,
930 }
931 end
932 end
933 if nofr > 0 then
934
935 result[1] [5] = false
936 result[nofr][5] = false
937 for i=2,nofr-1 do
938 local r = result[i][5]
939 if r == v_both or r == v_before then
940 result[i-1][5] = true
941 elseif r == v_after then
942 result[i][5] = true
943 end
944 end
945 end
946 return result
947 end
948 local body = collect_range(ranges[body_mode])
949 data.results = {
950 [head_mode] = collect_range(ranges[head_mode]),
951 [foot_mode] = collect_range(ranges[foot_mode]),
952 [more_mode] = collect_range(ranges[more_mode]),
953 [body_mode] = body,
954 }
955 if #body == 0 then
956 texsetcount("global",c_tabl_x_state,0)
957 texsetdimen("global",d_tabl_x_final_width,0)
958 else
959 texsetcount("global",c_tabl_x_state,1)
960 texsetdimen("global",d_tabl_x_final_width,getwidth(body[1][1]))
961 end
962end
963
964
965
966local function inject(row,copy,package)
967 local list = row[1]
968 if copy then
969 row[1] = copynodelist(list)
970 end
971 if package then
972 ctx_beginvbox()
973 ctxnode(tonode(list))
974 ctxnode(tonode(new_kern(row[2])))
975 ctx_endvbox()
976 ctx_nointerlineskip()
977 if row[4] then
978
979 elseif row[5] then
980 if row[3] then
981 ctx_blank { v_samepage, row[3] .. "sp" }
982 else
983 ctx_blank { v_samepage }
984 end
985 elseif row[3] then
986 ctx_blank { row[3] .. "sp" }
987 else
988 ctxnode(tonode(new_glue(0)))
989 end
990 else
991 ctxnode(tonode(list))
992 ctxnode(tonode(new_kern(row[2])))
993 if row[3] then
994 ctxnode(tonode(new_glue(row[3])))
995 end
996 end
997end
998
999local function total(row,distance)
1000 local n = #row > 0 and rowdistance or 0
1001 for i=1,#row do
1002 local ri = row[i]
1003 n = n + ri[2] + (ri[3] or 0)
1004 end
1005 return n
1006end
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019local function spanheight(body,i)
1020 local height, n = 0, 1
1021 while true do
1022 local bi = body[i]
1023 if bi then
1024 height = height + bi[2] + (bi[3] or 0)
1025 if bi[4] then
1026 n = n + 1
1027 i = i + 1
1028 else
1029 break
1030 end
1031 else
1032 break
1033 end
1034 end
1035 return height, n
1036end
1037
1038function xtables.flush(directives)
1039 local height = directives.height
1040 local method = directives.method or v_normal
1041 local settings = data.settings
1042 local results = data.results
1043 local rowdistance = settings.rowdistance
1044 local head = results[head_mode]
1045 local foot = results[foot_mode]
1046 local more = results[more_mode]
1047 local body = results[body_mode]
1048 local repeatheader = settings.header == v_repeat
1049 local repeatfooter = settings.footer == v_repeat
1050 if height and height > 0 then
1051 ctx_beginvbox()
1052 local bodystart = data.bodystart or 1
1053 local bodystop = data.bodystop or #body
1054 if bodystart > 0 and bodystart <= bodystop then
1055 local bodysize = height
1056 local footsize = total(foot,rowdistance)
1057 local headsize = total(head,rowdistance)
1058 local moresize = total(more,rowdistance)
1059 local firstsize, firstspans = spanheight(body,bodystart)
1060 if bodystart == 1 then
1061 bodysize = bodysize - headsize - footsize
1062 if headsize > 0 and bodysize >= firstsize then
1063 for i=1,#head do
1064 inject(head[i],repeatheader)
1065 end
1066 if rowdistance > 0 then
1067 ctxnode(tonode(new_glue(rowdistance)))
1068 end
1069 if not repeatheader then
1070 results[head_mode] = { }
1071 end
1072 end
1073 elseif moresize > 0 then
1074 bodysize = bodysize - footsize - moresize
1075 if bodysize >= firstsize then
1076 for i=1,#more do
1077 inject(more[i],true)
1078 end
1079 if rowdistance > 0 then
1080 ctxnode(tonode(new_glue(rowdistance)))
1081 end
1082 end
1083 elseif headsize > 0 and repeatheader then
1084 bodysize = bodysize - footsize - headsize
1085 if bodysize >= firstsize then
1086 for i=1,#head do
1087 inject(head[i],true)
1088 end
1089 if rowdistance > 0 then
1090 ctxnode(tonode(new_glue(rowdistance)))
1091 end
1092 end
1093 else
1094 bodysize = bodysize - footsize
1095 end
1096 if bodysize >= firstsize then
1097 local i = bodystart
1098 while i <= bodystop do
1099 local total, spans = spanheight(body,i)
1100 local bs = bodysize - total
1101 if bs > 0 then
1102 bodysize = bs
1103 for s=1,spans do
1104 inject(body[i])
1105 body[i] = nil
1106 i = i + 1
1107 end
1108 bodystart = i
1109 else
1110 break
1111 end
1112 end
1113 if bodystart > bodystop then
1114
1115 if footsize > 0 then
1116 if rowdistance > 0 then
1117 ctxnode(tonode(new_glue(rowdistance)))
1118 end
1119 for i=1,#foot do
1120 inject(foot[i])
1121 end
1122 results[foot_mode] = { }
1123 end
1124 results[body_mode] = { }
1125 texsetcount("global",c_tabl_x_state,0)
1126 else
1127
1128
1129 if repeatfooter and footsize > 0 then
1130 if rowdistance > 0 then
1131 ctxnode(tonode(new_glue(rowdistance)))
1132 end
1133 for i=1,#foot do
1134 inject(foot[i],true)
1135 end
1136 else
1137
1138 end
1139 texsetcount("global",c_tabl_x_state,2)
1140 end
1141 else
1142 if firstsize > height then
1143
1144 for s=1,firstspans do
1145 inject(body[bodystart])
1146 body[bodystart] = nil
1147 bodystart = bodystart + 1
1148 end
1149 end
1150 texsetcount("global",c_tabl_x_state,2)
1151 end
1152 else
1153 texsetcount("global",c_tabl_x_state,0)
1154 end
1155 data.bodystart = bodystart
1156 data.bodystop = bodystop
1157 ctx_endvbox()
1158 else
1159 if method == variables.split then
1160
1161
1162 for i=1,#head do
1163 inject(head[i],false,true)
1164 end
1165 if #head > 0 and rowdistance > 0 then
1166 ctx_blank { rowdistance .. "sp" }
1167 end
1168 for i=1,#body do
1169 inject(body[i],false,true)
1170 end
1171 if #foot > 0 and rowdistance > 0 then
1172 ctx_blank { rowdistance .. "sp" }
1173 end
1174 for i=1,#foot do
1175 inject(foot[i],false,true)
1176 end
1177 else
1178 ctx_beginvbox()
1179 for i=1,#head do
1180 inject(head[i])
1181 end
1182 if #head > 0 and rowdistance > 0 then
1183 ctxnode(tonode(new_glue(rowdistance)))
1184 end
1185 for i=1,#body do
1186 inject(body[i])
1187 end
1188 if #foot > 0 and rowdistance > 0 then
1189 ctxnode(tonode(new_glue(rowdistance)))
1190 end
1191 for i=1,#foot do
1192 inject(foot[i])
1193 end
1194 ctx_endvbox()
1195 end
1196 results[head_mode] = { }
1197 results[body_mode] = { }
1198 results[foot_mode] = { }
1199 texsetcount("global",c_tabl_x_state,0)
1200 end
1201end
1202
1203function xtables.cleanup()
1204 for mode, result in next, data.results do
1205 for _, r in next, result do
1206 flushnodelist(r[1])
1207 end
1208 end
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224 data = table.remove(stack)
1225end
1226
1227function xtables.next_row(specification)
1228 local r = data.currentrow + 1
1229 data.modes[r] = texgetcount(c_tabl_x_mode)
1230 data.currentrow = r
1231 data.currentcolumn = 0
1232 data.rowproperties[r] = specification
1233end
1234
1235function xtables.finish_row()
1236 local c = data.currentcolumn
1237 local r = data.currentrow
1238 local d = data.rows[r][c]
1239 local n = data.nofcolumns - c
1240 if d then
1241 local nx = d.nx
1242 if nx > 0 then
1243 n = n - nx + 1
1244 end
1245 end
1246 if n > 0 then
1247 for i=1,n do
1248 ctx_dummyxcell()
1249 end
1250 end
1251end
1252
1253
1254
1255implement {
1256 name = "x_table_create",
1257 actions = xtables.create,
1258 arguments = {
1259 {
1260 { "option" },
1261 { "textwidth", "dimen" },
1262 { "textheight", "dimen" },
1263 { "maxwidth", "dimen" },
1264 { "lineheight", "dimen" },
1265 { "columndistance", "dimen" },
1266 { "leftmargindistance", "dimen" },
1267 { "rightmargindistance", "dimen" },
1268 { "rowdistance", "dimen" },
1269 { "header" },
1270 { "footer" },
1271 }
1272 }
1273}
1274
1275implement {
1276 name = "x_table_flush",
1277 actions = xtables.flush,
1278 arguments = {
1279 {
1280 { "method" },
1281 { "height", "dimen" }
1282 }
1283 }
1284}
1285
1286implement { name = "x_table_reflow_width", actions = xtables.reflow_width }
1287implement { name = "x_table_reflow_height", actions = xtables.reflow_height }
1288implement { name = "x_table_construct", actions = xtables.construct }
1289implement { name = "x_table_cleanup", actions = xtables.cleanup }
1290implement { name = "x_table_next_row", actions = xtables.next_row }
1291implement { name = "x_table_next_row_option", actions = xtables.next_row, arguments = "string" }
1292implement { name = "x_table_finish_row", actions = xtables.finish_row }
1293implement { name = "x_table_init_reflow_width", actions = xtables.initialize_reflow_width }
1294implement { name = "x_table_init_reflow_height", actions = xtables.initialize_reflow_height }
1295implement { name = "x_table_init_reflow_width_option", actions = xtables.initialize_reflow_width, arguments = "string" }
1296implement { name = "x_table_init_reflow_height_option", actions = xtables.initialize_reflow_height, arguments = "string" }
1297implement { name = "x_table_init_construct", actions = xtables.initialize_construct }
1298implement { name = "x_table_set_reflow_width", actions = xtables.set_reflow_width }
1299implement { name = "x_table_set_reflow_height", actions = xtables.set_reflow_height }
1300implement { name = "x_table_set_construct", actions = xtables.set_construct }
1301implement { name = "x_table_r", actions = function() context(data.currentrow or 0) end }
1302implement { name = "x_table_c", actions = function() context(data.currentcolumn or 0) end }
1303
1304
1305
1306do
1307
1308 local context = context
1309 local ctxcore = context.core
1310
1311 local startxtable = ctxcore.startxtable
1312 local stopxtable = ctxcore.stopxtable
1313
1314 local startcollecting = context.startcollecting
1315 local stopcollecting = context.stopcollecting
1316
1317 function ctxcore.startxtable(...)
1318 startcollecting()
1319 startxtable(...)
1320 end
1321
1322 function ctxcore.stopxtable()
1323 stopxtable()
1324 stopcollecting()
1325 end
1326
1327end
1328 |