1if not modules then modules = { } end modules ["page-cst"] = {
2 version = 1.001,
3 comment = "companion to page-cst.mkxl",
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
17
18
19
20local next, type, tonumber, tostring, rawget = next, type, tonumber, tostring, rawget
21local ceil, odd, round, abs = math.ceil, math.odd, math.round, math.abs
22local format, lower, splitstring = string.format, string.lower, string.split
23local copy, concat = table.copy, table.concat
24local stepper = utilities.parsers.stepper
25local settings_to_array = utilities.parsers.settings_to_array
26
27local trace_state = false trackers.register("columnsets.trace", function(v) trace_state = v end)
28local trace_details = false trackers.register("columnsets.details", function(v) trace_details = v end)
29local trace_cells = false trackers.register("columnsets.cells", function(v) trace_cells = v end)
30local trace_flush = false trackers.register("columnsets.flush", function(v) trace_flush = v end)
31
32local report = logs.reporter("column sets")
33
34local setmetatableindex = table.setmetatableindex
35
36local properties = nodes.properties.data
37
38local nuts = nodes.nuts
39local tonode = nuts.tonode
40local tonut = nuts.tonut
41
42local setlink = nuts.setlink
43local setbox = nuts.setbox
44local getid = nuts.getid
45local getwhd = nuts.getwhd
46local setwhd = nuts.setwhd
47local getlist = nuts.getlist
48local takebox = nuts.takebox
49local vpack = nuts.vpack
50local getbox = nuts.getbox
51
52local texgetcount = tex.getcount
53local texgetdimen = tex.getdimen
54local texsetcount = tex.setcount
55local texsetdimen = tex.setdimen
56local texiscount = tex.iscount
57local texisdimen = tex.isdimen
58
59local nodepool = nuts.pool
60local new_vlist = nodepool.vlist
61local new_trace_rule = nodepool.rule
62local new_empty_rule = nodepool.emptyrule
63
64local context = context
65local implement = interfaces.implement
66
67local expandmacro = token.expandmacro
68
69local variables = interfaces.variables
70local v_all <const> = variables.all
71local v_here <const> = variables.here
72local v_fixed <const> = variables.fixed
73local v_top <const> = variables.top
74local v_bottom <const> = variables.bottom
75local v_repeat <const> = variables["repeat"]
76local v_yes <const> = variables.yes
77local v_page <const> = variables.page
78local v_sheet <const> = variables.sheet
79local v_first <const> = variables.first
80local v_last <const> = variables.last
81
82
83
84
85local v_spread <const> = variables.spread
86
87local integer_value <const> = tokens.values.integer
88local dimension_value <const> = tokens.values.dimension
89
90local hlist_code <const> = nodes.nodecodes.hlist
91local vlist_code <const> = nodes.nodecodes.vlist
92
93pagebuilders = pagebuilders or { }
94local columnsets = pagebuilders.columnsets or { }
95
96pagebuilders.columnsets = columnsets
97
98local data = { [""] = { } }
99local last = 0
100local nums = { }
101
102columnsets.data = data
103
104local nofcolumngaps = 0
105
106local c_realpageno <const> = texiscount("realpageno")
107
108local d_bodyfontsize <const> = texisdimen("bodyfontsize")
109local d_makeupwidth <const> = texisdimen("makeupwidth")
110local d_globalbodyfontstrutheight <const> = texisdimen("globalbodyfontstrutheight")
111local d_globalbodyfontstrutdepth <const> = texisdimen("globalbodyfontstrutdepth")
112local d_backspace <const> = texisdimen("backspace")
113
114local c_page_mvl_reserved_state <const> = texiscount("c_page_mvl_reserved_state")
115local c_page_mvl_first_column <const> = texiscount("c_page_mvl_first_column")
116local c_page_mvl_last_column <const> = texiscount("c_page_mvl_last_column")
117local c_page_mvl_current_sheet <const> = texiscount("c_page_mvl_current_sheet")
118local c_page_mvl_current_spreadsheet <const> = texiscount("c_page_mvl_current_spreadsheet")
119local c_page_mvl_max_used_cells <const> = texiscount("c_page_mvl_max_used_cells")
120
121local d_page_mvl_reserved_height <const> = texisdimen("d_page_mvl_reserved_height")
122local d_page_mvl_reserved_width <const> = texisdimen("d_page_mvl_reserved_width")
123local d_page_mvl_column_width <const> = texisdimen("d_page_mvl_column_width")
124local d_page_mvl_span_width <const> = texisdimen("d_page_mvl_span_width")
125
126
127
128
129
130
131
132
133
134
135
136
137local function savedstate(dataset)
138 return {
139 count = dataset.count,
140 sheet = dataset.sheet,
141 spread = dataset.spread,
142 firstcolumn = dataset.firstcolumn,
143 lastcolumn = dataset.lastcolumn,
144 state = dataset.state,
145 }
146end
147
148local function restorestate(dataset,saved)
149 dataset.count = saved.count
150 dataset.sheet = saved.sheet
151 dataset.spread = saved.spread
152 dataset.firstcolumn = saved.firstcolumn
153 dataset.lastcolumn = saved.lastcolumn
154 dataset.state = saved.state
155end
156
157local function setstate(dataset,flushing)
158 local realpage = texgetcount(c_realpageno)
159 if flushing then
160 dataset.count = dataset.count + 1
161 dataset.sheet = dataset.sheet + 1
162 if odd(dataset.count) then
163 local spread = dataset.spread
164 dataset.spread = spread + 1
165 end
166 if odd(realpage) then
167 dataset.state = "right"
168
169 else
170 dataset.state = "left"
171
172 end
173 else
174 dataset.spread = 1
175 dataset.sheet = 1
176 if odd(realpage) then
177 dataset.initial = "right"
178 dataset.count = 2
179 else
180 dataset.initial = "left"
181 dataset.count = 1
182 end
183 dataset.state = dataset.initial
184 end
185 if odd(dataset.count) then
186 dataset.firstcolumn = 1
187 dataset.lastcolumn = dataset.nofleft
188 else
189 dataset.firstcolumn = dataset.nofleft + 1
190 dataset.lastcolumn = dataset.firstcolumn + dataset.nofright - 1
191 dataset.state = "right"
192 end
193 dataset.currentcolumn = dataset.firstcolumn
194 dataset.currentrow = 1
195end
196
197local function block1(list,cells,offset)
198 for c, n in next, list do
199 local column = cells[offset+c]
200 if column then
201 for r=1,n do
202 if not column[r] then
203 column[r] = true
204 end
205 end
206 end
207 end
208end
209
210local function block2(list,cells,offset,rows)
211 for c, n in next, list do
212 local column = cells[offset+c]
213 if column then
214 if n > 0 then
215 for r=n+1,rows do
216 if not column[r] then
217 column[r] = true
218 end
219 end
220 elseif n < 0 then
221 for r=rows,rows+n+1,-1 do
222 if not column[r] then
223 column[r] = true
224 end
225 end
226 end
227 end
228 end
229end
230
231local function check(dataset,byspread)
232 local cells = dataset.cells
233 local sheet = dataset.sheet
234 local start = dataset.start
235 local lines = dataset.lines
236 local count = dataset.count
237 local rows = dataset.nofrows
238
239 if byspread or not odd(count) then
240 local list = rawget(start,count)
241 if list then
242 block1(list,cells,dataset.nofleft)
243 start[count] = nil
244 end
245 else
246 local list = rawget(start,count)
247 if list then
248 block1(list,cells,0)
249 end
250 local list = rawget(start,count+1)
251 if list then
252 block1(list,cells,dataset.nofleft)
253 end
254 end
255
256 if byspread or not odd(count) then
257 local list = rawget(lines,count)
258 if list then
259 block2(list,cells,dataset.nofleft,rows)
260 end
261 else
262 local list = rawget(lines,count)
263 if list then
264 block2(list,cells,0,rows)
265 end
266 local list = rawget(lines,count+1)
267 if list then
268 block2(list,cells,dataset.nofleft,rows)
269 end
270 end
271
272
273
274
275end
276
277local function getlocations(dataset,location)
278 local locations = dataset.locations
279 if not locations then
280 local split = settings_to_array(location)
281 locations = { }
282 for i=1,#split do
283 local t = splitstring(split[i],",")
284 local c = tonumber(t[1])
285 local r = tonumber(t[2])
286 local l = tonumber(t[3])
287 if c and r then
288
289 locations[#locations+1] = { c, r, l }
290 end
291 end
292 dataset.locations = locations
293 end
294 if not dataset.putdirect then
295
296 local cells = dataset.cells
297 local noflines = #cells[1]
298 for c=1,dataset.currentcolumn - 1 do
299 local f = cells[c]
300 for l=1,noflines do
301 if not f[l] then
302 f[l] = true
303 end
304 end
305 end
306 dataset.putdirect = true
307 end
308 return locations
309end
310
311local function updatedistances(dataset)
312 dataset.distances = dataset.distances or setmetatableindex(function(t,k)
313 return dataset.distance
314 end)
315end
316
317local function updatewidths(dataset)
318 dataset.widths = dataset.widths or setmetatableindex(function(t,k)
319 return dataset.width
320 end)
321end
322
323local function updatespans(dataset)
324 local widths = dataset.widths
325 local distances = dataset.distances
326 local nofleft = dataset.nofleft
327 local nofright = dataset.nofright
328 local spans = { }
329 local function calculate(first,last)
330 for i=first,last do
331 local s = { }
332 local d = 0
333 for j=i,last do
334 d = d + widths[j]
335 s[#s+1] = round(d)
336 d = d + distances[j]
337 end
338 spans[i] = s
339 end
340 end
341 calculate(1,nofleft)
342 calculate(nofleft+1,nofleft+nofright)
343 dataset.spans = spans
344end
345
346local function updatespreads(dataset)
347 local nofleft = dataset.nofleft
348 local nofright = dataset.nofright
349 local spreads = copy(dataset.spans)
350 dataset.spreads = spreads
351 local gap = 2 * texgetdimen(d_backspace)
352 for l=1,nofleft do
353 local s = spreads[l]
354 local n = #s
355 local o = s[n] + gap
356 for r=1,nofright do
357 n = n + 1
358 s[n] = s[r] + o
359 end
360 end
361end
362
363local function emptycells(dataset)
364 local cells = { }
365 for c=1,dataset.nofleft + dataset.nofright do
366 cells[c] = { }
367 for r=1,dataset.nofrows do
368 cells[c][r] = false
369 end
370 end
371 return cells
372end
373
374local function futurecells(dataset)
375 local future = dataset.future
376 local spread = dataset.spread
377 local cells = dataset.future[spread]
378 if not cells then
379 cells = emptycells(dataset)
380 future[spread] = cells
381 end
382 return cells
383end
384
385local function setfirstcells(dataset)
386 local cells = dataset.future[1] or emptycells(dataset)
387 dataset.cells = cells
388 dataset.future[1] = cells
389end
390
391local function findgap(dataset,everything)
392 local cells = dataset.cells
393 local mvls = dataset.mvls
394 local nofcolumns = dataset.nofcolumns
395 local nofrows = dataset.nofrows
396 local currentrow = dataset.currentrow
397 local currentcolumn = dataset.currentcolumn
398 local currentmvl = dataset.currentmvl or 1
399 local disabled = dataset.disabled
400
401 local foundc = 0
402 local foundr = 0
403 local foundn = 0
404 for c=currentcolumn,everything and dataset.nofcolumns or dataset.lastcolumn do
405 if (mvls[c] == currentmvl or currentmvl == 1) and not disabled[c] then
406 local column = cells[c]
407 foundn = 0
408 for r=currentrow,nofrows do
409 if not column[r] then
410 if foundc == 0 then
411 foundc = c
412 foundr = r
413 foundn = 0
414 end
415 foundn = foundn + 1
416 elseif foundn > 0 then
417 return foundc, foundr, foundn
418 end
419 end
420 if foundn > 0 then
421 return foundc, foundr, foundn
422 else
423 currentrow = 1
424 end
425 else
426 currentrow = 1
427 end
428 end
429end
430
431
432
433do
434
435 local function checkwidth(dataset,width,where)
436 local widths = dataset.widths
437 local distances = dataset.distances
438
439 if not width then
440 width = 0
441 end
442
443
444 if width == 0 or dataset.autowidth then
445 local nofleft = dataset.nofleft
446 local nofright = dataset.nofright
447 local name = dataset.name
448 local dl = 0
449 local dr = 0
450
451 for i=1,nofleft-1 do
452 dl = dl + distances[i]
453 end
454 for i=1,nofright-1 do
455 dr = dr + distances[nofleft+i]
456 end
457 local nl = nofleft
458 local nr = nofright
459 local wl = dataset.maxwidth
460 local wr = wl
461 for i=1,nofleft do
462 local w = rawget(widths,i)
463 if w then
464 nl = nl - 1
465 wl = wl - w
466 end
467 end
468 for i=1,nofright do
469 local w = rawget(widths,nofleft+i)
470 if w then
471 nr = nr - 1
472 wr = wr - w
473 end
474 end
475
476 if nl > 0 then
477 dl = (wl - dl) / nl
478 end
479 if nr > 0 then
480 dr = (wr - dr) / nr
481 end
482
483 if dl > dr then
484 report("using %s page column width %p in columnset %a","right",dr,name)
485 width = dr
486 elseif dl < dr then
487 report("using %s page column width %p in columnset %a","left",dl,name)
488 width = dl
489 else
490 width = dl
491 end
492 else
493 end
494
495 width = round(width)
496 dataset.width = width
497
498 return width
499 end
500
501 function columnsets.define(t,resetting)
502 local name = t.name
503 local nofleft = t.nofleft or 1
504 local nofright = t.nofright or 1
505 local nofcolumns = nofleft + nofright
506 local doublesided = false
507 local dataset = data[name] or { }
508
509 data[name] = dataset
510 if not nums[name] then
511 last = last + 1
512 nums[name] = last
513 nums[last] = name
514 end
515
516 dataset.doublesided = doublesided
517 dataset.name = name
518 dataset.identifier = nums[name]
519 dataset.nofleft = nofleft
520 dataset.nofright = nofright
521 dataset.nofcolumns = nofcolumns
522 dataset.nofrows = t.nofrows or 1
523 dataset.distance = t.distance or texgetdimen(d_bodyfontsize)
524 dataset.maxwidth = t.maxwidth or texgetdimen(d_makeupwidth)
525 dataset.lineheight = t.lineheight or texgetdimen(d_globalbodyfontstrutheight)
526 dataset.linedepth = t.linedepth or texgetdimen(d_globalbodyfontstrutdepth)
527 dataset.quit = 0
528 dataset.limit = t.limit or dataset.limit
529
530 dataset.cells = { }
531 dataset.currentcolumn = 1
532 dataset.currentrow = 1
533 dataset.currentmvl = 2
534
535 dataset.future = { }
536
537 dataset.lines = dataset.lines or setmetatableindex("table")
538 dataset.start = dataset.start or setmetatableindex("table")
539 dataset.mvls = dataset.mvls or setmetatableindex(function(t,k) t[k] = 1 return 1 end)
540 dataset.mvlc = dataset.mvlc or { }
541 dataset.disabled = dataset.disabled or { }
542
543 dataset.count = 1
544 dataset.sheet = 1
545 dataset.spread = 1
546
547 dataset.sheets = dataset.sheets or setmetatableindex("table")
548 dataset.delayed = dataset.delayed or setmetatableindex("table")
549 dataset.spreadsheets = dataset.spreadsheets or setmetatableindex("table")
550 dataset.nofsheets = dataset.nofsheets or 0
551 dataset.nofdelayed = dataset.nofdelayed or 0
552 dataset.nofspreadsheets = dataset.nofspreadsheets or 0
553 dataset.lastmvl = dataset.lastmvl or 1
554
555 updatedistances(dataset)
556 updatewidths(dataset)
557 dataset.autowidth = not t.width or t.width == 0
558 dataset.width = checkwidth(dataset,t.width,"define")
559 updatespans(dataset)
560 updatespreads(dataset)
561
562 texsetdimen(d_page_mvl_column_width,dataset.width)
563
564 setstate(dataset)
565
566 return dataset
567 end
568
569 function columnsets.reset(t)
570 local dataset = columnsets.define(t)
571 if dataset then
572 setfirstcells(dataset)
573 check(dataset)
574 checkwidth(dataset,t.width,"reset")
575 end
576 end
577
578 function columnsets.clean(name)
579 local dataset = data[name]
580 if dataset then
581 dataset.sheets = setmetatableindex("table")
582 dataset.nofsheets = 0
583 dataset.delayed = setmetatableindex("table")
584 dataset.nofdelayed = 0
585 end
586 end
587
588 implement {
589 name = "definecolumnset",
590 actions = columnsets.define,
591 arguments = { {
592 { "name", "string" },
593 { "method", "string" },
594 { "nofleft", "integer" },
595 { "nofright", "integer" },
596 { "nofrows", "integer" },
597 } }
598 }
599
600 implement {
601 name = "resetcolumnset",
602 actions = columnsets.reset,
603 arguments = { {
604 { "name", "string" },
605 { "nofleft", "integer" },
606 { "nofright", "integer" },
607 { "nofrows", "integer" },
608 { "lineheight", "dimension" },
609 { "linedepth", "dimension" },
610 { "width", "dimension" },
611 { "distance", "dimension" },
612 { "maxwidth", "dimension" },
613 { "limit", "integer" },
614 } }
615 }
616
617 implement {
618 name = "cleancolumnset",
619 actions = columnsets.clean,
620 arguments = "argument",
621 }
622
623 function columnsets.setlines(t)
624 local dataset = data[t.name]
625 if dataset then
626 dataset.lines[t.sheet][t.column] = t.value
627 if t.sheet > dataset.nofsheets then
628 dataset.nofsheets = t.sheet
629 end
630 end
631 end
632
633 function columnsets.setstart(t)
634 local dataset = data[t.name]
635 if dataset then
636 dataset.start[t.sheet][t.column] = t.value
637 if t.sheet > dataset.nofsheets then
638 dataset.nofsheets = t.sheet
639 end
640 end
641 end
642
643 function columnsets.setproperties(t)
644 local dataset = data[t.name]
645 if dataset then
646 local column = t.column
647 dataset.distances[column] = t.distance
648 dataset.widths[column] = t.width
649 end
650 end
651
652 implement {
653 name = "setcolumnsetlines",
654 actions = columnsets.setlines,
655 arguments = { {
656 { "name", "string" },
657 { "sheet", "integer" },
658 { "column", "integer" },
659 { "value", "integer" },
660 } }
661 }
662
663 implement {
664 name = "setcolumnsetstart",
665 actions = columnsets.setstart,
666 arguments = { {
667 { "name", "string" },
668 { "sheet", "integer" },
669 { "column", "integer" },
670 { "value", "integer" },
671 } }
672 }
673
674 implement {
675 name = "setcolumnsetproperties",
676 actions = columnsets.setproperties,
677 arguments = { {
678 { "name", "string" },
679 { "column", "integer" },
680 { "distance", "dimension" },
681 { "width", "dimension" },
682 } }
683 }
684
685end
686
687
688
689do
690
691 local setmacrofrommark = token.setmacrofrommark
692 local getusedmarks = tex.getusedmarks
693
694 local updatetopmarks = nuts.updatetopmarks
695 local updatemarks = nuts.updatemarks
696 local updatefirstmarks = nuts.updatefirstmarks
697
698 function columnsets.prepareflush(name)
699 local dataset = data[name]
700 local cells = dataset.cells
701 local firstcolumn = dataset.firstcolumn
702 local lastcolumn = dataset.lastcolumn
703 local nofrows = dataset.nofrows
704 local lineheight = dataset.lineheight
705 local linedepth = dataset.linedepth
706 local widths = dataset.widths
707 local height = (lineheight+linedepth)*nofrows
708 local count = dataset.count
709
710
711
712
713
714
715
716 if trace_flush then
717 report(
718 "flushing: spread %i, count %i, sheet %i, first %i, last %i",
719 dataset.spread,dataset.count,dataset.sheet,firstcolumn,lastcolumn
720 )
721 end
722
723 local columns = { }
724 dataset.columns = columns
725
726 local used = 0
727
728 updatetopmarks()
729 for c=firstcolumn,lastcolumn do
730 local column = cells[c]
731 for r=1,nofrows do
732 local cell = column[r]
733 if (cell == false) or (cell == true) then
734 if trace_cells then
735 column[r] = new_trace_rule(65536*2,lineheight,linedepth)
736 else
737 column[r] = new_empty_rule(0,lineheight,linedepth)
738 end
739 else
740
741
742
743 local list = getlist(cell)
744 while list do
745 if getid(list) == vlist_code then
746 updatemarks(list)
747 end
748 list = getlist(list)
749 end
750 used = r
751 end
752 end
753 for r=1,nofrows-1 do
754 setlink(column[r],column[r+1])
755 end
756 columns[c] = new_vlist(column[1],widths[c],height,0)
757 end
758 updatefirstmarks()
759
760 texsetcount("global",c_page_mvl_max_used_cells,used)
761
762 if odd(dataset.count) then
763 local spread = dataset.spread
764 dataset.lines [spread] = nil
765 dataset.start [spread] = nil
766
767
768 end
769
770 texsetcount(c_page_mvl_first_column,firstcolumn)
771 texsetcount(c_page_mvl_last_column,lastcolumn)
772 end
773
774 function columnsets.flushcolumn(name,column)
775 local dataset = data[name]
776 local columns = dataset.columns
777 local packed = columns[column]
778 setbox("b_page_mvl_column",packed)
779 end
780
781 function columnsets.finishflush(name)
782 local dataset = data[name]
783 setstate(dataset,true)
784 dataset.cells = dataset.future[dataset.spread] or emptycells(dataset)
785 end
786
787 implement {
788 name = "preparecolumnsetflush",
789 actions = columnsets.prepareflush,
790 arguments = "argument",
791 }
792
793 implement {
794 name = "flushcolumnsetcolumn",
795 actions = columnsets.flushcolumn,
796 arguments = { "argument" ,"integer" },
797 }
798
799 implement {
800 name = "finishcolumnsetflush",
801 actions = columnsets.finishflush,
802 arguments = "argument",
803 }
804
805end
806
807
808
809do
810
811 function columnsets.block(t)
812 local dataset = data[t.name]
813 local cells = dataset.cells
814 local nofcolumns = dataset.nofcolumns
815 local nofrows = dataset.nofrows
816
817 local c = t.c or 0
818 local r = t.r or 0
819 if c == 0 or r == 0 or c > nofcolumns or r > nofrows then
820 return
821 end
822 local nc = t.nc or 0
823 local nr = t.nr or 0
824 if nc == 0 then
825 return
826 end
827 if nr == 0 then
828 return
829 end
830 local rr = r + nr - 1
831 local cc = c + nc - 1
832 if rr > nofrows then
833 rr = nofrows
834 end
835 if cc > nofcolumns then
836 cc = nofcolumns
837 end
838 for i=c,cc do
839 local column = cells[i]
840 for j=r,rr do
841 column[j] = true
842 end
843 end
844 end
845
846 local function here(c,r,nr,nofcolumns,nofrows,cells,width,spans)
847 if c < 1 then
848 c = 1
849 end
850 if r < 1 then
851 r = 1
852 end
853 local rr = r + nr - 1
854 if rr > nofrows then
855
856 return false
857 end
858 local cc = 0
859 local wd = spans[c]
860 local wc = 0
861 local nc = 0
862 for i=c,nofcolumns do
863 nc = nc + 1
864 wc = wd[nc]
865 if not wc then
866 break
867 elseif wc >= width then
868 cc = i
869 break
870 end
871 end
872 if cc == 0 or cc > nofcolumns then
873
874 return false
875 end
876 for i=c,cc do
877 local column = cells[i]
878 if column then
879 for j=r,rr do
880 if column[j] then
881
882 return false
883 end
884 end
885 end
886 end
887
888 return c, r, nc
889 end
890
891
892
893 local methods = {
894 [v_here] = here,
895 [v_fixed] = here,
896 tblr = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
897 for j=r,nofrows-nr+1 do
898 for i=c,nofcolumns do
899 if not cells[i][j] then
900 local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
901 if c then
902 return c, r, cc
903 end
904 end
905 end
906 end
907 end,
908 lrtb = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
909 for i=c,nofcolumns do
910 for j=r,nofrows-nr+1 do
911 if not cells[i][j] then
912 local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
913 if c then
914 return c, r, cc
915 end
916 end
917 end
918 end
919 end,
920 tbrl = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
921 for j=r,nofrows-nr+1 do
922 for i=nofcolumns,c,-1 do
923 if not cells[i][j] then
924 local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
925 if c then
926 return c, r, cc
927 end
928 end
929 end
930 end
931 end,
932 rltb = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
933 for i=nofcolumns,c,-1 do
934 for j=r,nofrows-nr+1 do
935 if not cells[i][j] then
936 local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
937 if c then
938 return c, r, cc
939 end
940 end
941 end
942 end
943 end,
944 btlr = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
945
946 for j=nofrows-nr+1-r+1,1,-1 do
947 for i=c,nofcolumns do
948 if not cells[i][j] then
949 local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
950 if c then
951 return c, r, cc
952 end
953 end
954 end
955 end
956 end,
957 lrbt = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
958 for i=c,nofcolumns do
959
960 for j=nofrows-nr+1-r+1,1,-1 do
961 if not cells[i][j] then
962 local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
963 if c then
964 return c, r, cc
965 end
966 end
967 end
968 end
969 end,
970 btrl = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
971
972 for j=nofrows-nr+1-r+1,1,-1 do
973 for i=nofcolumns,c,-1 do
974 if not cells[i][j] then
975 local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
976 if c then
977 return c, r, cc
978 end
979 end
980 end
981 end
982 end,
983 rlbt = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
984 for i=nofcolumns,c,-1 do
985
986 for j=nofrows-nr+1-r+1,1,-1 do
987 if not cells[i][j] then
988 local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
989 if c then
990 return c, r, cc
991 end
992 end
993 end
994 end
995 end,
996 fxtb = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
997 for i=c,nofcolumns do
998 for j=r,nofrows-nr+1 do
999 if not cells[i][j] then
1000 local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
1001 if c then
1002 return c, r, cc
1003 end
1004 end
1005 r = 1
1006 end
1007 end
1008 end,
1009 fxbt = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
1010 for i=c,nofcolumns do
1011 for j=nofrows-nr+1,r,-1 do
1012 if not cells[i][j] then
1013 local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
1014 if c then
1015 return c, r, cc
1016 end
1017 end
1018 end
1019 r = 1
1020 end
1021 end,
1022 [v_top] = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
1023 for i=c,nofcolumns do
1024 for j=1,nofrows-nr+1 do
1025 if cells[i][j] then
1026 break
1027 else
1028 local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
1029 if c then
1030 return c, r, cc
1031 end
1032 end
1033 end
1034 end
1035 end,
1036 [v_bottom] = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
1037 for i=c,nofcolumns do
1038 for j=1,nofrows-nr+1 do
1039 if cells[i][j] then
1040 break
1041 else
1042 local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
1043 if c then
1044 return c, r, cc
1045 end
1046 end
1047 end
1048 end
1049 end,
1050 }
1051
1052 local threshold = 50
1053
1054 function columnsets.check(t)
1055 local dataset = data[t.name]
1056 local cells = dataset.cells
1057 local nofcolumns = dataset.nofcolumns
1058 local nofrows = dataset.nofrows
1059 local widths = dataset.widths
1060 local lineheight = dataset.lineheight
1061 local linedepth = dataset.linedepth
1062 local distances = dataset.distances
1063 local spans = dataset.spans
1064
1065 local method = lower(t.method or "tblr")
1066 local boxwidth = t.width or 0
1067 local boxheight = t.height or 0
1068 local boxnumber = t.box
1069 local box = boxnumber and getbox(boxnumber)
1070
1071local ntop = t.ntop or 0
1072local nbottom = t.nbottom or 0
1073
1074 if boxwidth > 0 and boxheight > 0 then
1075
1076 elseif box then
1077 local wd, ht, dp = getwhd(box)
1078 boxwidth = wd
1079 boxheight = ht + dp
1080 else
1081 report("empty box")
1082 return
1083 end
1084
1085 boxwidth = boxwidth - 100
1086 boxheight = boxheight - 100
1087
1088 local mm, cc, rr = string.match(method,"^(....):(%d*)%*(%d*)$")
1089 if mm and (cc or rr) then
1090 method = mm
1091 t.c = tonumber(cc) or t.c
1092 t.r = tonumber(rr) or t.r
1093 end
1094
1095
1096
1097
1098
1099
1100
1101
1102 local c = t.c or 0
1103 local r = t.r or 0
1104 if c == 0 then
1105 c = dataset.currentcolumn
1106 end
1107 if r == 0 then
1108 r = dataset.currentrow
1109 end
1110 if c == 0 or r == 0 or c > nofcolumns or r > nofrows then
1111 texsetcount(c_page_mvl_reserved_state,5)
1112 return
1113 end
1114
1115 local nr = ceil(boxheight/(lineheight+linedepth))
1116
1117 local action = methods[method]
1118 local cfound = false
1119 local rfound = false
1120 local lastcolumn = dataset.lastcolumn
1121
1122 if dataset.byspread then
1123 lastcolumn = dataset.nofleft + dataset.nofright
1124 elseif odd(dataset.count) then
1125 c = 1
1126 lastcolumn = dataset.nofleft
1127 else
1128 c = dataset.nofleft + 1
1129 lastcolumn = dataset.nofleft + dataset.nofright
1130 end
1131
1132 if t.c and t.c >= 1 and t.c <= lastcolumn then
1133 c = t.c
1134 end
1135 if t.r and t.r >= 1 and t.r <= nofrows then
1136 r = t.r
1137 end
1138 local w = boxwidth - threshold
1139 if action then
1140
1141 cfound, rfound, nc = action(c,r,nr,lastcolumn,nofrows,cells,w,spans)
1142 end
1143 if not cfound and method ~= v_here then
1144
1145 cfound, rfound, nc = here(c,r,nr,lastcolumn,nofrows,cells,w,spans)
1146 end
1147if cfound then
1148 if rfound == 1 then
1149 ntop = 0
1150 end
1151 if rfound + nr - 1 == dataset.nofrows then
1152 nbottom = 0
1153 end
1154 if (rfound - ntop) < 0 then
1155 cfound = false
1156 elseif (rfound + nr + nbottom - 1) > dataset.nofrows then
1157 cfound = false
1158 end
1159end
1160 if cfound then
1161 local ht = nr*(lineheight+linedepth)
1162 local wd = spans[cfound][nc]
1163 dataset.reserved_ht = ht
1164 dataset.reserved_wd = wd
1165 dataset.reserved_c = cfound
1166 dataset.reserved_r = rfound
1167 dataset.reserved_nc = nc
1168 dataset.reserved_nr = nr
1169 dataset.reserved_ntop = ntop
1170 dataset.reserved_nbottom = nbottom
1171 texsetcount(c_page_mvl_reserved_state,0)
1172 texsetdimen(d_page_mvl_reserved_height,ht)
1173 texsetdimen(d_page_mvl_reserved_width,wd)
1174
1175 else
1176 dataset.reserved_ht = false
1177 dataset.reserved_wd = false
1178 dataset.reserved_c = false
1179 dataset.reserved_r = false
1180 dataset.reserved_nc = false
1181 dataset.reserved_nr = false
1182 dataset.reserved_ntop = false
1183 dataset.reserved_nbottom = false
1184 texsetcount(c_page_mvl_reserved_state,4)
1185
1186
1187
1188 end
1189 end
1190
1191 function columnsets.put(t)
1192 local dataset = data[t.name]
1193 local cells = dataset.cells
1194 local widths = dataset.widths
1195 local lineheight = dataset.lineheight
1196 local linedepth = dataset.linedepth
1197 local boxnumber = t.box
1198 local box = boxnumber and takebox(boxnumber)
1199
1200 local c = t.c or dataset.reserved_c
1201 local r = t.r or dataset.reserved_r
1202 if not c or not r then
1203
1204 return
1205 end
1206 local lastc = c + dataset.reserved_nc - 1
1207 local lastr = r + dataset.reserved_nr - 1
1208 local ntop = dataset.reserved_ntop or 0
1209 local nbottom = dataset.reserved_nbottom or 0
1210
1211 for i=c,lastc do
1212 local column = cells[i]
1213 for j=r-ntop,lastr+nbottom do
1214 column[j] = true
1215 end
1216 end
1217 cells[c][r] = box
1218 setwhd(box,widths[c],lineheight,linedepth)
1219 dataset.reserved_c = false
1220 dataset.reserved_r = false
1221 dataset.reserved_nc = false
1222 dataset.reserved_nr = false
1223 dataset.reserved_ntop = false
1224 dataset.reserved_nbottom = false
1225
1226 end
1227
1228 function columnsets.resetdirect(name)
1229 local dataset = data[name]
1230 if dataset then
1231 dataset.locations = nil
1232 dataset.putdirect = nil
1233 end
1234 end
1235
1236 function columnsets.putdirect(t)
1237 local boxnumber = t.box
1238 local box = boxnumber and takebox(boxnumber)
1239 if not box then
1240 return
1241 end
1242
1243 local location = t.location
1244 local lines = t.lines
1245 local span = 1
1246 if not location or not lines then
1247 return
1248 end
1249
1250 local dataset = data[t.name]
1251 local locations = getlocations(dataset,location)
1252 if #locations == 0 then
1253 return
1254 end
1255
1256 local cells = dataset.cells
1257 local widths = dataset.widths
1258 local future = dataset.future
1259
1260 local c, r, l, s = 0, 0, 0, 0
1261 for i=1,#future do
1262 cells = future[i]
1263 for j=1,#locations do
1264 local v = locations[j]
1265 c, r, l = v[1], v[2], v[3]
1266 if not cells[c][r] then
1267 s = i
1268 goto FOUND
1269 end
1270 end
1271 end
1272 if cells[c][r] then
1273 cells = emptycells(dataset)
1274 future[#future+1] = cells
1275 for j=1,#locations do
1276 local v = locations[j]
1277 c, r, l = v[1], v[2], v[3]
1278 if cells[c][r] then
1279
1280 else
1281 s = #future
1282 goto FOUND
1283 end
1284 end
1285 end
1286
1287 ::FOUND::
1288 if s == 0 then
1289 return
1290 end
1291 local wd, ht, dp = getwhd(box)
1292
1293 local lastc = c + span - 1
1294 local lastr = r + lines - 1
1295
1296 for i=c,lastc do
1297 local column = cells[i]
1298 for j=r,lastr do
1299 column[j] = true
1300 end
1301 end
1302 cells[c][r] = box
1303
1304 setwhd(box,widths[c],dataset.lineheight,dataset.linedepth)
1305 end
1306
1307
1308
1309 function columnsets.add(name,box)
1310 local dataset = data[name]
1311 local cells = dataset.cells
1312 local nofcolumns = dataset.nofcolumns
1313 local nofrows = dataset.nofrows
1314 local currentrow = dataset.currentrow
1315 local currentcolumn = dataset.currentcolumn
1316 local currentcells = dataset.currentcells
1317 local lineheight = dataset.lineheight
1318 local linedepth = dataset.linedepth
1319 local widths = dataset.widths
1320
1321 local b = takebox(box)
1322 nofcolumngaps = nofcolumngaps + 1
1323
1324 properties[b] = { columngap = nofcolumngaps }
1325
1326 setwhd(b,widths[currentcolumn],lineheight,linedepth)
1327 local column = cells[currentcolumn]
1328 column[currentrow] = b or true
1329 for i=currentrow+1,currentrow+currentcells-1 do
1330 if not column[i] then
1331 column[i] = true
1332 end
1333 end
1334
1335 dataset.currentrow = currentrow + currentcells
1336 end
1337
1338 implement {
1339 name = "blockcolumnset",
1340 actions = columnsets.block,
1341 arguments = { {
1342 { "name", "string" },
1343 { "c", "integer" },
1344 { "r", "integer" },
1345 { "nc", "integer" },
1346 { "nr", "integer" },
1347 { "method", "string" },
1348 { "box", "integer" },
1349 } }
1350 }
1351
1352 implement {
1353 name = "checkcolumnset",
1354 actions = columnsets.check,
1355 arguments = { {
1356 { "name", "string" },
1357 { "method", "string" },
1358 { "c", "integer" },
1359 { "r", "integer" },
1360 { "ntop", "integer" },
1361 { "nbottom", "integer" },
1362 { "method", "string" },
1363 { "box", "integer" },
1364 { "width", "dimension" },
1365 { "height", "dimension" },
1366 { "option", "string" },
1367 } }
1368 }
1369
1370 implement {
1371 name = "putincolumnset",
1372 actions = columnsets.put,
1373 arguments = { {
1374 { "name", "string" },
1375 { "c", "integer" },
1376 { "r", "integer" },
1377 { "method", "string" },
1378 { "box", "integer" },
1379 } }
1380 }
1381
1382 implement {
1383 name = "columnsetresetdirect",
1384 actions = columnsets.resetdirect,
1385 arguments = "string"
1386 }
1387
1388 implement {
1389 name = "putincolumnsetdirect",
1390 actions = columnsets.putdirect,
1391 arguments = { {
1392 { "name", "string" },
1393 { "location", "string" },
1394 { "lines", "integer" },
1395 { "box", "integer" },
1396 } }
1397 }
1398
1399 implement {
1400 name = "addtocolumnset",
1401 actions = columnsets.add,
1402 arguments = { "argument", "integer" },
1403 }
1404
1405end
1406
1407
1408
1409do
1410
1411 function columnsets.addmvl(name,box,slot,reduce,balance)
1412 local dataset = data[name]
1413 local cells = dataset.cells
1414 local currentrow = dataset.currentrow
1415 local currentcolumn = dataset.currentcolumn
1416 local currentcells = dataset.currentcells
1417 local lineheight = dataset.lineheight
1418 local linedepth = dataset.linedepth
1419 local widths = dataset.widths
1420
1421 local b = takebox(box)
1422
1423 nofcolumngaps = nofcolumngaps + 1
1424
1425 properties[b] = { columngap = nofcolumngaps }
1426 if reduce then
1427 local w, h, d = getwhd(b)
1428 local usedcells = ceil((h+d)/(lineheight+linedepth))
1429 currentcells = usedcells
1430 dataset.currentcells = usedcells
1431 end
1432 setwhd(b,widths[currentcolumn],lineheight,linedepth)
1433 local column = cells[currentcolumn]
1434 for i=currentrow,currentrow+currentcells-2 do
1435 if not column[i] then
1436 column[i] = true
1437 end
1438 end
1439 local anchor = currentrow + currentcells - 1
1440 if anchor > 0 then
1441 column[anchor] = b or true
1442 dataset.currentrow = anchor + 1
1443 if balance then
1444 dataset.currentrow = dataset.nofrows + 1
1445 local c, r, n = findgap(dataset,true)
1446 if n then
1447 dataset.currentcolumn, dataset.currentrow, dataset.currentcells = c, r, n
1448 end
1449 else
1450 columnsets.gotoslot(name,slot)
1451 end
1452 end
1453 end
1454
1455 function columnsets.submvl(name,box,slot)
1456 columnsets.addmvl(name,box,slot,true)
1457 end
1458
1459 function columnsets.submvlbalance(name,box,slot)
1460 columnsets.addmvl(name,box,slot,true,true)
1461 end
1462
1463 function columnsets.gotoslot(name,slot)
1464 local dataset = data[name]
1465 if not dataset.present then
1466 dataset.present = 1
1467 setfirstcells(dataset)
1468 end
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486 local deadcycles = 1000
1487 while deadcycles > 0 do
1488 local c, r, n = findgap(dataset,true)
1489 if n then
1490 dataset.currentcolumn = c
1491 dataset.currentrow = r
1492 dataset.currentcells = n
1493 else
1494 dataset.currentcolumn = 1
1495 dataset.currentrow = 1
1496 dataset.currentcells = n
1497 n = 0
1498 end
1499 if n > 0 then
1500 break
1501 else
1502 dataset.present = dataset.present + 1
1503 dataset.cells = dataset.future[dataset.present] or emptycells(dataset)
1504 dataset.future[dataset.present] = dataset.cells
1505 end
1506 deadcycles = deadcycles - 1
1507 end
1508 if deadcycles == 0 then
1509 report("fatal error: mvl %i mismatch",dataset.currentmvl or 1)
1510 os.exit()
1511 end
1512 end
1513
1514 function columnsets.setpresent(name,n)
1515 local dataset = data[name]
1516 if dataset then
1517
1518 dataset.present = n
1519 dataset.cells = dataset.future[n] or dataset.cells
1520
1521 end
1522 end
1523
1524 function columnsets.setmvl(name,n)
1525 local dataset = data[name]
1526 if dataset then
1527 setfirstcells(dataset)
1528 dataset.currentcolumn = dataset.firstcolumn
1529 dataset.currentrow = 1
1530 dataset.present = 1
1531 dataset.currentmvl = n
1532 end
1533 end
1534
1535 function columnsets.definesub(t)
1536 local dataset = data[t.name]
1537 if dataset then
1538 local lastmvl = dataset.lastmvl or 1
1539 local done = false
1540 local mvls = dataset.mvls
1541 local nofcolumns = dataset.nofleft + dataset.nofright
1542 local columns = t.columns
1543 if columns == "*" or columns == v_all then
1544 columns = { }
1545 for i=1,nofcolumns do
1546 columns[i] = i
1547 end
1548 else
1549 columns = utilities.parsers.settings_to_array(columns or "")
1550 end
1551 for i=1,#columns do
1552 local n = tonumber(columns[i]) or 1
1553 columns[i] = n
1554
1555 if not done then
1556 lastmvl = lastmvl + 1
1557 done = true
1558 end
1559 mvls[n] = lastmvl
1560 end
1561 dataset.mvlc[t.subname] = columns
1562 dataset.lastmvl = lastmvl
1563 return lastmvl
1564 else
1565 return 1
1566 end
1567 end
1568
1569 implement {
1570 name = "definesubcolumnset",
1571 actions = function(t)
1572 return integer_value, columnsets.definesub(t)
1573 end,
1574 usage = "value",
1575 arguments = { {
1576 { "name", "string" },
1577 { "subname", "string" },
1578 { "columns", "string" },
1579 } }
1580 }
1581
1582 implement {
1583 name = "columnsetlastmvl",
1584 arguments = "argument",
1585 usage = "value",
1586 public = true,
1587 actions = function(name)
1588 local dataset = data[name]
1589 return integer_value, dataset and dataset.lastmvl or 1
1590 end
1591 }
1592
1593 implement {
1594 name = "addtocolumnsetmvl",
1595 actions = columnsets.addmvl,
1596 arguments = { "argument", "integer", "integer" },
1597 }
1598
1599 implement {
1600 name = "subtocolumnsetmvl",
1601 actions = columnsets.submvl,
1602 arguments = { "argument", "integer", "integer" },
1603 }
1604
1605 implement {
1606 name = "subtocolumnsetmvlbalance",
1607 actions = columnsets.submvlbalance,
1608 arguments = { "argument", "integer", "integer" },
1609 }
1610
1611 implement {
1612 name = "setcolumnsetpresent",
1613 actions = columnsets.setpresent,
1614 arguments = { "argument", "integer" },
1615 }
1616
1617 implement {
1618 name = "setcolumnsetmvl",
1619 actions = columnsets.setmvl,
1620 arguments = { "argument", "integer" },
1621 }
1622
1623 implement {
1624 name = "gotocolumnsetslot",
1625 actions = columnsets.gotoslot,
1626 arguments = "argument",
1627 }
1628
1629end
1630
1631do
1632
1633
1634
1635
1636 local followup = nil
1637 local splitter = lpeg.splitter("*",tonumber)
1638
1639 columnsets["noto"] = function(t)
1640 return followup()
1641 end
1642
1643 columnsets["goto"] = function(name,target)
1644 local dataset = data[name]
1645 local nofcolumns = dataset.nofcolumns
1646 if target == v_yes or target == "" then
1647 local currentcolumn = dataset.currentcolumn
1648 followup = function()
1649 context(dataset.currentcolumn == currentcolumn and 1 or 0)
1650 end
1651 return followup()
1652 end
1653 if target == v_first then
1654 if dataset.currentcolumn > 1 then
1655 target = v_page
1656 else
1657 return context(0)
1658 end
1659 end
1660 if target == v_page then
1661 if dataset.currentcolumn == 1 and dataset.currentrow == 1 then
1662 return context(0)
1663 else
1664 local sheet = dataset.sheet
1665 followup = function()
1666 context(dataset.sheet == sheet and 1 or 0)
1667 end
1668 return followup()
1669 end
1670 end
1671 if target == v_last then
1672 target = dataset.nofcolumns
1673 if dataset.currentcolumn ~= target then
1674 followup = function()
1675 context(dataset.currentcolumn ~= target and 1 or 0)
1676 end
1677 return followup()
1678 end
1679 return
1680 end
1681 local targetpage = tonumber(target)
1682 if targetpage then
1683 followup = function()
1684 context(dataset.currentcolumn ~= targetpage and 1 or 0)
1685 end
1686 return followup()
1687 end
1688 local targetcolumn, targetrow = lpeg.match(splitter,target)
1689 if targetcolumn and targetrow then
1690 if dataset.currentcolumn ~= targetcolumn and dataset.currentrow ~= targetrow then
1691 followup = function()
1692 if dataset.currentcolumn ~= targetcolumn then
1693 context(1)
1694 return
1695 end
1696 dataset.currentrow = targetrow
1697 context(0)
1698 end
1699 return followup()
1700 end
1701 end
1702 end
1703
1704 implement {
1705 name = "columnsetgoto",
1706 actions = columnsets["goto"],
1707 arguments = "2 strings",
1708 }
1709
1710 implement {
1711 name = "columnsetnoto",
1712 actions = columnsets["noto"],
1713 }
1714
1715end
1716
1717do
1718
1719 function columnsets.sethsize(name)
1720 local dataset = data[name]
1721 texsetdimen(d_page_mvl_column_width,dataset.width)
1722 end
1723
1724 function columnsets.sethspan(name,span)
1725
1726
1727 local dataset = data[name]
1728 local column = dataset.currentcolumn
1729 local available = dataset.lastcolumn - column + 1
1730 if span > available then
1731 span = available
1732 end
1733 local width = dataset.spans[column][span]
1734 texsetdimen(d_page_mvl_span_width,width)
1735 end
1736
1737
1738
1739
1740
1741
1742 implement {
1743 name = "columnsethspan",
1744 arguments = { "argument", "integer", "integer" },
1745 usage = "value",
1746 actions = function(name,column,span)
1747 local dataset = data[name]
1748 local total = 0
1749 if dataset then
1750 local dataset = data[name]
1751 local nofcolumns = dataset.nofleft + dataset.nofright
1752 local spanned = dataset.spans[column]
1753 if spanned then
1754 total = spanned[span] or 0
1755 end
1756 end
1757 return dimension_value, total
1758 end,
1759 }
1760
1761 implement {
1762 name = "columnsetcolumnwidth",
1763 arguments = { "argument", "argument" },
1764 usage = "value",
1765 actions = function(name,subname)
1766 local dataset = data[name]
1767 local width = 0
1768 if dataset then
1769 local columns = dataset.mvlc[subname]
1770 width = dataset.widths[columns and columns[1] or 1]
1771 end
1772 return dimension_value, width
1773 end,
1774 }
1775
1776 implement {
1777 name = "setvsizecolumnset",
1778 arguments = "argument",
1779 actions = function() end
1780 }
1781
1782 implement {
1783 name = "sethsizecolumnset",
1784 arguments = "argument",
1785 actions = columnsets.sethsize,
1786 }
1787
1788 implement {
1789 name = "sethsizecolumnspan",
1790 arguments = { "argument" ,"integer" },
1791 actions = columnsets.sethspan,
1792 }
1793
1794end
1795
1796
1797
1798do
1799
1800 local areas = { }
1801
1802 function columnsets.registerarea(t)
1803
1804 areas[#areas+1] = t
1805 end
1806
1807
1808
1809
1810 local ctx_page_mvl_set_area = context.page_mvl_set_area
1811
1812 function columnsets.flushareas(name)
1813 local nofareas = #areas
1814 if nofareas == 0 then
1815 return
1816 end
1817 local dataset = data[name]
1818 local setsheet = dataset.sheet
1819
1820 if odd(setsheet) then
1821
1822 local kept = { }
1823 for i=1,nofareas do
1824 local area = areas[i]
1825 local kind = area.kind
1826 local sheet = area.sheet or 0
1827 if sheet == 1 or sheet == setsheet or sheet == (setsheet + 1) then
1828 local okay = false
1829 local nofleft = dataset.nofleft
1830 local nofcolumns = area.nc
1831 local nofrows = area.nr
1832 local column = area.c
1833 local row = area.r
1834
1835 if kind == v_sheet then
1836 if odd(sheet) then
1837
1838 elseif column > nofleft then
1839
1840 else
1841 column = column + nofleft
1842 end
1843 end
1844 columnsets.block {
1845 name = name,
1846 c = column,
1847 r = row,
1848 nc = nofcolumns,
1849 nr = nofrows,
1850 }
1851 local left = 0
1852 local start = nofleft + 1
1853 local overflow = (column + nofcolumns - 1) - nofleft
1854 local height = nofrows * (dataset.lineheight + dataset.linedepth)
1855 local width = dataset.spreads[column][nofcolumns]
1856
1857 if overflow > 0 then
1858 local used = nofcolumns - overflow
1859 local sofar = dataset.spreads[column][used]
1860 if sofar then
1861 left = sofar + texgetdimen(d_backspace)
1862 else
1863
1864 end
1865 end
1866
1867 ctx_page_mvl_set_area(name,area.name,column,row,width,height,start,left)
1868 if area.state ~= v_repeat then
1869 area = nil
1870 end
1871 if area then
1872 kept[#kept+1] = area
1873 end
1874 else
1875 kept[#kept+1] = area
1876 end
1877 end
1878 areas = kept
1879 end
1880 end
1881
1882 function columnsets.setarea(t)
1883 local dataset = data[t.name]
1884 local cells = dataset.cells
1885 local box = takebox(t.box)
1886 local column = t.c
1887 local row = t.r
1888 if column and row then
1889 setwhd(box,dataset.widths[column],dataset.lineheight,dataset.linedepth)
1890 cells[column][row] = box
1891 end
1892 end
1893
1894 implement {
1895 name = "registercolumnsetarea",
1896 actions = columnsets.registerarea,
1897 arguments = { {
1898 { "name", "string" },
1899 { "kind", "string" },
1900 { "page", "integer" },
1901 { "sheet", "integer" },
1902 { "state", "string" },
1903 { "c", "integer" },
1904 { "r", "integer" },
1905 { "nc", "integer" },
1906 { "nr", "integer" },
1907 } }
1908 }
1909
1910 implement {
1911 name = "flushcolumnsetareas",
1912 actions = columnsets.flushareas,
1913 arguments = "argument",
1914 }
1915
1916 implement {
1917 name = "setcolumnsetarea",
1918 actions = columnsets.setarea,
1919 arguments = { {
1920 { "name", "string" },
1921 { "c", "integer" },
1922 { "r", "integer" },
1923 { "method", "string" },
1924 { "box", "integer" },
1925 } }
1926 }
1927
1928end
1929
1930
1931
1932do
1933
1934
1935
1936 function columnsets.state(name,variable)
1937 local dataset = data[name]
1938 if variable == "column" then
1939 variable = "currentcolumn"
1940 end
1941 return dataset and dataset[variable] or 0
1942 end
1943
1944 implement {
1945 name = "columnsetstate",
1946 actions = function(name,variable)
1947 return integer_value, columnsets.state(name,variable)
1948 end,
1949 public = true,
1950 usage = "value",
1951 arguments = "2 arguments",
1952 }
1953
1954
1955
1956 local function hascontent(cells,nofleft,nofright)
1957 local result = 0
1958 if cells then
1959 local function found(c)
1960 for i=1,#c do
1961 if c[i] then
1962 return true
1963 end
1964 end
1965 return false
1966 end
1967 for i=1,nofleft do
1968 if found(cells[i]) then
1969 result = result + 1
1970 break
1971 end
1972 end
1973 for i=nofleft+1,nofleft+nofright do
1974 if found(cells[i]) then
1975 result = result + 2
1976 break
1977 end
1978 end
1979 end
1980 return result
1981 end
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998 implement {
1999 name = "columnsethascontent",
2000 arguments = { "argument", "integer" },
2001 usage = "value",
2002 public = true,
2003 actions = function(name,index)
2004 local dataset = data[name]
2005 if not dataset then
2006 return integer_value, 0
2007 elseif index < dataset.lastcontent then
2008 return integer_value, 3
2009 else
2010 return integer_value, dataset.lastresult
2011 end
2012 end
2013 }
2014
2015
2016 implement {
2017 name = "columnsetlastfuture",
2018 arguments = "argument",
2019 usage = "value",
2020 public = true,
2021 actions = function(name)
2022 local dataset = data[name]
2023 if dataset then
2024 local future = dataset.future
2025 local nofleft = dataset.nofleft
2026 local nofright = dataset.nofright
2027 for i=#future,1,-1 do
2028 local lastcontent = hascontent(future[i],nofleft,nofright)
2029 if lastcontent > 0 then
2030 dataset.lastcontent = i
2031 dataset.lastresult = lastcontent
2032 return integer_value, i
2033 end
2034 end
2035 dataset.lastcontent = 0
2036 dataset.lastresult = 0
2037 end
2038 return integer_value, 0
2039 end
2040 }
2041
2042end
2043
2044do
2045
2046 local s_page_mvl_temp <const> = "page:mvl:temp"
2047
2048 function columnsets.registerspreadsheets(name,spreadsheets)
2049 local dataset = data[name]
2050 if dataset then
2051 local s = buffers.raw(s_page_mvl_temp)
2052 stepper(spreadsheets,1,function(n)
2053 local d = dataset.spreadsheets[n]
2054 d[#d+1] = s
2055 report("spreadsheets %i, registering entry %i",n,#d)
2056 if n > dataset.nofspreadsheets then
2057 dataset.nofspreadsheets = n
2058 end
2059 end)
2060 buffers.erase(s_page_mvl_temp)
2061 end
2062 end
2063
2064 function columnsets.presetspreadsheets(name)
2065 local dataset = data[name]
2066 if dataset then
2067 local saved = savedstate(dataset)
2068 local spreadsheets = dataset.spreadsheets
2069setstate(dataset,false)
2070dataset.byspread = true
2071dataset.spread = 1
2072dataset.count = 1
2073dataset.sheet = 1
2074 for n=1,dataset.nofspreadsheets do
2075 dataset.cells = futurecells(dataset)
2076 local list = rawget(spreadsheets,n)
2077 if list then
2078 if trace_flush then
2079 report("presetting: spreadsheets %i, %i entries",n,#list)
2080 end
2081 texsetcount(c_page_mvl_current_spreadsheet,n)
2082 for i=1,#list do
2083 buffers.assign(s_page_mvl_temp,list[i])
2084 expandmacro("page_mvl_process_spread")
2085 end
2086 spreadsheets[n] = nil
2087 else
2088 if trace_flush then
2089 report("presetting: spreadsheets %i, no entries",n)
2090 end
2091 end
2092
2093
2094
2095 dataset.spread = dataset.spread + 1
2096 dataset.count = dataset.spread * 2 - 1
2097 dataset.sheet = dataset.spread * 2 - 1
2098 dataset.state = "left"
2099 dataset.firstcolumn = 1
2100 dataset.lastcolumn = dataset.firstcolumn + dataset.nofright
2101 dataset.currentcolumn = dataset.firstcolumn
2102 dataset.currentrow = 1
2103
2104 end
2105dataset.byspread = false
2106 texsetcount(c_page_mvl_current_spreadsheet,0)
2107 restorestate(dataset,saved)
2108 setstate(dataset,false)
2109 setfirstcells(dataset)
2110 check(dataset)
2111 end
2112 end
2113
2114 function columnsets.registersheet(name,sheet,option)
2115 local dataset = data[name]
2116 if dataset then
2117 local s = buffers.raw(s_page_mvl_temp)
2118 if option == v_page then
2119 stepper(sheet,1,function(n)
2120 local d = dataset.delayed[n]
2121 d[#d+1] = s
2122 report("sheet %i, registering entry %i, delayed",n,#d)
2123 if n > dataset.nofdelayed then
2124 dataset.nofdelayed = n
2125 end
2126 end)
2127 else
2128 stepper(sheet,1,function(n)
2129 local d = dataset.sheets[n]
2130 d[#d+1] = s
2131 report("sheet %i, registering entry %i, immediate",n,#d)
2132 if n > dataset.nofsheets then
2133 dataset.nofsheets = n
2134 end
2135 end)
2136 end
2137 buffers.erase(s_page_mvl_temp)
2138 end
2139 end
2140
2141 function columnsets.presetsheets(name)
2142 local dataset = data[name]
2143 if dataset then
2144 local saved = savedstate(dataset)
2145 local sheets = dataset.sheets
2146 setstate(dataset,false)
2147 for sheet=1,dataset.nofsheets do
2148 dataset.cells = futurecells(dataset)
2149 check(dataset)
2150 local list = rawget(sheets,sheet)
2151 if list then
2152 if trace_flush then
2153 report("presetting: spread %i, count %i, sheet %i, %i entries",
2154 dataset.spread,dataset.count,dataset.sheet,#list)
2155 end
2156 if dataset.sheet ~= sheet then
2157 report("error: sheets are out of sync")
2158 end
2159 texsetcount(c_page_mvl_current_sheet,sheet)
2160 for i=1,#list do
2161 buffers.assign(s_page_mvl_temp,list[i])
2162 expandmacro("page_mvl_process_sheet")
2163 end
2164 sheets[sheet] = nil
2165 else
2166 if trace_flush then
2167 report("presetting: spread %i, count %i, sheet %i, no entries",
2168 dataset.spread,dataset.count,dataset.sheet)
2169 end
2170 end
2171 setstate(dataset,true)
2172 end
2173 texsetcount(c_page_mvl_current_sheet,0)
2174 restorestate(dataset,saved)
2175 setstate(dataset,false)
2176 setfirstcells(dataset)
2177 check(dataset)
2178 end
2179 end
2180
2181 function columnsets.delayedsheet(name)
2182 local dataset = data[name]
2183 if dataset then
2184 local delayed = dataset.delayed
2185 local sheet = dataset.sheet
2186 local list = rawget(delayed,sheet)
2187 if list then
2188 if trace_flush then
2189 report("presetting: delayed sheet %i, %i entries",n,#list)
2190 end
2191 for i=1,#list do
2192 buffers.assign(s_page_mvl_temp,list[i])
2193 expandmacro("page_mvl_process_delayed")
2194 end
2195 delayed[sheet] = nil
2196 end
2197 end
2198 end
2199
2200 implement {
2201 name = "registercolumnsetspreadsheets",
2202 arguments = "2 strings",
2203 actions = columnsets.registerspreadsheets,
2204 }
2205
2206 implement {
2207 name = "presetcolumnsetspreadsheets",
2208 arguments = "argument",
2209 actions = columnsets.presetspreadsheets,
2210 }
2211
2212 implement {
2213 name = "registercolumnsetsheet",
2214 arguments = "3 strings",
2215 actions = columnsets.registersheet,
2216 }
2217
2218 implement {
2219 name = "delayedcolumnsetsheet",
2220 arguments = "string",
2221 actions = columnsets.delayedsheet,
2222 }
2223
2224 implement {
2225 name = "presetcolumnsetsheets",
2226 arguments = "argument",
2227 actions = columnsets.presetsheets,
2228 }
2229
2230end
2231
2232do
2233
2234 local setbalanceshape = tex.setbalanceshape
2235
2236 function columnsets.shape(name)
2237 local dataset = data[name]
2238 local future = dataset.future
2239 local disabled = dataset.disabled
2240 local page = 0
2241 local height = dataset.lineheight
2242 local depth = dataset.linedepth
2243 local total = height + depth
2244 local toggle = dataset.nofleft + 1
2245 local nofspreads = #future
2246 local nofcolumns = dataset.nofleft + dataset.nofright
2247 local worstcase = nofcolumns
2248 local first = dataset.initial == "right" and toggle or 1
2249 local shape = {
2250 identifier = nums[name],
2251 worstcase = worstcase,
2252
2253
2254 }
2255
2256 local slack = 0
2257
2258 local function add(spread,page,column,n,row,creator)
2259 n = n - slack
2260 if n == 0 then
2261 n = 1
2262 end
2263 if n > 0 then
2264 local index = #shape + 1;
2265 shape[index] = {
2266 creator = creator,
2267 index = index,
2268 spread = spread,
2269 page = page,
2270 column = column,
2271 vsize = n * total,
2272 topskip = height,
2273
2274 lines = n,
2275 row = row - n + 1,
2276 }
2277 end
2278 end
2279
2280 local mvls = dataset.mvls
2281 local currentmvl = dataset.currentmvl or 1
2282
2283 for spread=1,nofspreads do
2284 local cells = future[spread]
2285 local worst = #shape
2286
2287 for c=first,#cells do
2288 if (mvls[c] == currentmvl or currentmvl == 1) and not disabled[c] then
2289 local column = cells[c]
2290 local lines = 0
2291 local slot = 0
2292 local noflines = #column
2293 if c == toggle or c == 1 then
2294 page = page + 1
2295 end
2296 for r=1,noflines do
2297 if not column[r] then
2298 lines = lines + 1
2299 elseif lines > 0 then
2300 add(spread,page,c,lines,r,"specific shape")
2301 lines = 0
2302 end
2303 end
2304 if lines > 0 then
2305 add(spread,page,c,lines,noflines,"specific shape")
2306 end
2307 end
2308 end
2309 first = 1
2310 worst = #shape - worst + 1
2311 if worst > worstcase then
2312 worstcase = worst
2313 end
2314 end
2315 local nofrows = dataset.nofrows
2316 add(nofspreads,0,0,nofrows,nofrows,"generic shape")
2317 shape.worstcase = worstcase
2318 dataset.shape = shape
2319 dataset.lastshape = copy(shape[#shape])
2320
2321
2322
2323local cnt = #shape
2324local nor = dataset.nofrows
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334for i=1,cnt do
2335 local s = shape[i]
2336 local r = s.row
2337 if r == 1 then
2338
2339 s.options = 1
2340 end
2341
2342
2343
2344end
2345
2346shape[cnt].options = 1
2347
2348 setbalanceshape(shape)
2349 end
2350
2351 function columnsets.limit(name)
2352 local dataset = data[name]
2353 if dataset and not dataset.limited then
2354 local future = dataset.future
2355 local limit = dataset.limit or 1
2356 for i=1,#future do
2357 local cells = future[i]
2358 for c=1,#cells do
2359 local column = cells[c]
2360 local lines = 0
2361 local first = 0
2362 local noflines = #column
2363 for r=1,noflines do
2364 if not column[r] then
2365 if first == 0 then
2366
2367 first = r
2368 lines = 1
2369 else
2370
2371 lines = lines + 1
2372 end
2373 elseif first > 0 then
2374 if lines < limit then
2375
2376 for i=first,first+lines-1 do
2377 column[i] = true
2378 end
2379 else
2380
2381 end
2382 lines = 0
2383 first = 0
2384 else
2385 end
2386 end
2387 if first > 0 and lines < limit then
2388 for i=first,first+lines-1 do
2389 column[i] = true
2390 end
2391 end
2392 end
2393 end
2394 dataset.limited = true
2395 end
2396 end
2397
2398 function columnsets.reshape(name)
2399 local dataset = data[name]
2400 if dataset then
2401 local shape = dataset.shape
2402 if shape then
2403 setbalanceshape(shape)
2404 end
2405 end
2406 end
2407
2408 implement {
2409 name = "columnsetshape",
2410 arguments = "argument",
2411 actions = columnsets.shape,
2412 }
2413
2414 implement {
2415 name = "columnsetlimit",
2416 arguments = "argument",
2417 actions = columnsets.limit,
2418 }
2419
2420 implement {
2421 name = "columnsetreshape",
2422 arguments = "argument",
2423 actions = columnsets.reshape,
2424 }
2425
2426 function columnsets.setextra(name,index,reset,justadd)
2427 local dataset = data[name]
2428 if dataset then
2429 local shape = dataset.shape
2430 if shape then
2431 local nofshapes = #shape
2432 local s = index < nofshapes and shape[index]
2433 if not s then
2434 local template = shape[nofshapes-1]
2435 local lastshape = dataset.lastshape
2436 local threshold = dataset.nofleft + 1
2437 local nofcolumns = dataset.nofleft + dataset.nofright
2438 local lines = lastshape.lines
2439 local vsize = lastshape.vsize
2440 local topskip = lastshape.topskip
2441 local bottomskip = lastshape.bottomskip
2442 local spread = lastshape.spread + 1
2443 local spread = template.spread + 1
2444 local page = template.page + 1
2445 while nofshapes <= index do
2446 for j=1,nofcolumns do
2447 if j == threshold then
2448 page = page + 1
2449 end
2450 shape[nofshapes] = {
2451 creator = "intermediate extra",
2452 column = j,
2453 extra = 0,
2454 extralines = 0,
2455 vsize = vsize,
2456 index = nofshapes,
2457 lines = lines,
2458 page = page,
2459 row = 1,
2460 spread = spread,
2461 topskip = topskip,
2462 bottomskip = bottomskip,
2463 }
2464 nofshapes = nofshapes + 1
2465 end
2466 spread = spread + 1
2467 page = page + 1
2468 end
2469 if shape[#shape].creator ~= "generic shape" then
2470 nofshapes = #shape + 1
2471 local last = copy(lastshape)
2472 last.spread = spread
2473 last.extra = 0
2474 last.extralines = 0
2475 last.index = nofshapes
2476 last.creator = "final extra"
2477 shape[nofshapes] = last
2478 end
2479 s = shape[index] or shape[#shape]
2480 end
2481 if justadd then
2482 return
2483 else
2484 local extralines = (s.extralines or 0) - (reset and -1 or 1)
2485 if -extralines >= s.lines + 1 then
2486
2487 return integer_value, 0
2488 else
2489
2490 s.extralines = extralines
2491 s.extra = extralines * (dataset.lineheight + dataset.linedepth)
2492 return integer_value, 1
2493 end
2494 end
2495 end
2496 end
2497 if justadd then
2498 return integer_value, 0
2499 end
2500 end
2501
2502 implement {
2503 name = "columnsetsetshapeextra",
2504 arguments = { "argument", "integer", false },
2505 usage = "value",
2506 actions = columnsets.setextra
2507 }
2508
2509 implement {
2510 name = "columnsetresetshapeextra",
2511 arguments = { "argument", "integer", true },
2512 usage = "value",
2513 actions = columnsets.setextra
2514 }
2515
2516 implement {
2517 name = "balanceshapeextra",
2518 arguments = { "argument", "integer" },
2519 usage = "value",
2520 public = true,
2521 protected = true,
2522 actions = function(name,index)
2523 local dataset = data[name]
2524 if dataset then
2525 local shape = dataset.shape
2526 if shape then
2527 return dimension_value, (shape[index] or shape[#shape]).extra
2528 end
2529 end
2530 return dimension_value, 0
2531 end
2532 }
2533
2534 implement {
2535 name = "columnsetshapevsize",
2536 arguments = { "argument", "integer" },
2537 usage = "value",
2538 public = true,
2539 actions = function(name,index)
2540 local dataset = data[name]
2541 local vsize = 0
2542 if dataset then
2543 local shape = dataset.shape
2544 if shape then
2545 local s = shape[index] or shape[#shape]
2546 vsize = s.vsize
2547 end
2548 end
2549 return dimension_value, vsize
2550 end
2551 }
2552
2553 implement {
2554 name = "columnsetshapeindex",
2555 arguments = { "argument", "integer" },
2556 usage = "value",
2557 public = true,
2558 actions = function(name,index)
2559 local dataset = data[name]
2560 local index = 0
2561 if dataset then
2562 local shape = dataset.shape
2563 if shape then
2564 index = (shape[index] or shape[#shape]).page
2565 end
2566 end
2567 return integer_value, index
2568 end
2569 }
2570
2571 implement {
2572 name = "columnsetshapecount",
2573 arguments = "argument",
2574 usage = "value",
2575 public = true,
2576 actions = function(name,index)
2577 local dataset = data[name]
2578 local count = 0
2579 if dataset then
2580 local shape = dataset.shape
2581 if shape then
2582 count = #shape
2583 end
2584 end
2585 return integer_value, count
2586 end
2587 }
2588
2589 implement {
2590 name = "columnsetshapeworst",
2591 arguments = "argument",
2592 usage = "value",
2593 public = true,
2594 actions = function(name,index)
2595 local dataset = data[name]
2596 local worst = 0
2597 if dataset then
2598 local shape = dataset.shape
2599 if shape then
2600 worst = shape.worst
2601 end
2602 if worst == 0 then
2603 worst = dataset.nofleft + dataset.nofright
2604 end
2605 end
2606 return integer_value, worst
2607 end
2608 }
2609
2610end
2611
2612
2613
2614do
2615
2616 local trace = false
2617
2618 trackers.register("columnsets.showgrid",function(v)
2619 trace = v
2620 end)
2621
2622 local function showgrid(dataset,where,cells)
2623 local nofcolumns = dataset.nofcolumns
2624 local nofrows = dataset.nofrows
2625 local row = { [0] = "0" }
2626 local sheet = { }
2627 if not cells then
2628 cells = dataset.cells
2629 end
2630 for r=1,nofrows do
2631 row[0] = format("% 3i",r)
2632 for c=1,nofcolumns do
2633 local v = cells[c][r]
2634
2635 row[c] = v == true and "!" or v and "+" or "-"
2636 end
2637 sheet[#sheet+1] = concat(row," ",0,nofcolumns)
2638 end
2639 report("%s :\n%s",where or "show",concat(sheet,"\n"))
2640 end
2641
2642 local function show(name,range)
2643 local dataset = data[name]
2644 if dataset then
2645 local future = dataset.future
2646 if future then
2647 stepper(range or "1:*",#future,function(n)
2648 showgrid(dataset,"spread " .. n,future[n])
2649 end)
2650 end
2651 end
2652 end
2653
2654 implement {
2655 name = "columnsetshowtracedgrid",
2656 arguments = "argument",
2657 actions = function(name)
2658 if type(trace) == "string" then
2659 show(name,trace)
2660 end
2661 end,
2662 }
2663
2664 implement {
2665 name = "columnsetshowgrid",
2666 arguments = "2 arguments",
2667 public = true,
2668 protected = true,
2669 actions = show,
2670 }
2671
2672 function columnsets.showshape(name)
2673 local dataset = data[name]
2674 if dataset then
2675 local shape = dataset.shape
2676 report("")
2677 if shape then
2678 for i=1,#shape do
2679 local s = shape[i]
2680 report(
2681 "%04i: s %03i p %03i c %01i l %02i e %02i v %p c %a",
2682 i, s.spread, s.page, s.column, s.lines, s.extralines or 0, s.vsize, s.creator
2683 )
2684 end
2685 else
2686 report("no shape")
2687 end
2688 report("")
2689 end
2690 end
2691
2692 implement {
2693 name = "columnsetshowshape",
2694 arguments = "argument",
2695 public = true,
2696 protected = true,
2697 actions = columnsets.showshape
2698 }
2699
2700 function columnsets.status(name)
2701 local dataset = data[name]
2702 if dataset then
2703 local shape = dataset.shape
2704 local sheet = dataset.sheet
2705 local lines = { "[" }
2706 if shape then
2707 for i=1,#shape do
2708 local s = shape[i]
2709 if s and s.page == sheet then
2710 local l = s.lines
2711 local e = s.extralines
2712 local s = tostring(l)
2713 if e and e ~= 0 then
2714 if e > 0 then
2715 s = s .. "+"
2716 end
2717 s = s .. tostring(e)
2718 end
2719 lines[#lines+1] = s
2720 end
2721 end
2722 if #lines == 1 then
2723 lines[#lines+1] = "-"
2724 end
2725 end
2726 lines[#lines+1] = "]"
2727 return
2728 "[ page " .. texgetcount(c_realpageno) ..
2729 ", spread " .. dataset.spread ..
2730 ", sheet " .. sheet ..
2731 ", nofcolumns " .. dataset.nofcolumns ..
2732 ", first " .. dataset.firstcolumn ..
2733 ", last " .. dataset.lastcolumn ..
2734 ", nofleft " .. dataset.nofleft ..
2735 ", nofright " .. dataset.nofright ..
2736 ", nofrows " .. dataset.nofrows ..
2737 " ] " .. concat(lines," ")
2738 end
2739 end
2740
2741 implement {
2742 name = "columnsetstatus",
2743 arguments = "argument",
2744 actions = { columnsets.status, context },
2745 }
2746
2747end
2748
2749
2750
2751do
2752
2753 local div, mod = math.div, math.mod
2754
2755 local actions = { "spread", "page", "column", "slot", "lines", "amount" }
2756 local trace = false
2757 local eject <const> = tex.magicconstants.ejectpenalty
2758
2759 trackers.register("columnsets.breaks", function(v)
2760 trace = v
2761 end)
2762
2763
2764
2765
2766
2767
2768
2769
2770 local trybreak_code <const> = tex.balancecallbackcodes.trybreak
2771 local skipzeros_code <const> = tex.balancecallbackcodes.skipzeros
2772
2773 callback.register("balance_boundary", function(what,count,identifier,slot)
2774 local name = nums[identifier]
2775 local action = actions[what] or "unknown"
2776 local result = skipzeros_code
2777 local penalty = 0
2778 local extra = 0
2779 if name and action then
2780 local dataset = data[name]
2781 if dataset then
2782 local shape = dataset.shape
2783 if shape then
2784 local spread, page, column
2785 local quit = dataset.quit
2786 local nofleft = dataset.nofleft
2787 local nofright = dataset.nofright
2788 local nofcolumns = nofleft + nofright
2789 local current = slot < #shape and shape[slot]
2790 local shaped = current and current.column ~= 0
2791 if shaped then
2792 spread = current.spread
2793 page = current.page
2794 column = current.column
2795 else
2796 local nofslots = #shape - 1
2797 local overflow = slot - nofslots
2798 spread = shape[#shape].spread
2799 column = overflow
2800 while column > nofcolumns do
2801 spread = spread + 1
2802 column = column - nofcolumns
2803 end
2804 page = spread*2
2805 if column <= nofleft then
2806 page = page - 1
2807 end
2808 if column == 0 then
2809 count = 0
2810 end
2811 end
2812 if action == "spread" then
2813 if count > 0 then
2814 if not shaped then
2815 quit = #shape + (spread-shape[#shape].spread+1) * nofcolumns - column + 1
2816
2817 elseif slot == #shape then
2818 quit = #shape
2819 else
2820 quit = slot
2821 for i=slot+1,#shape do
2822 local s = shape[i].spread
2823 quit = quit + 1
2824 if s > spread then
2825 break
2826 end
2827 end
2828 end
2829 dataset.quit = quit
2830 end
2831 if slot < quit then
2832 result = trybreak_code
2833 penalty = eject
2834 end
2835 elseif action == "page" then
2836 if count > 0 then
2837 if not shaped then
2838 quit = slot + nofcolumns - column + 1
2839 if odd(page) then
2840 quit = quit - nofright
2841 end
2842 elseif slot == #shape then
2843 quit = #shape
2844 else
2845 quit = slot
2846 for i=slot+1,#shape-1 do
2847 local p = shape[i].page
2848 quit = quit + 1
2849 if p > page then
2850 break
2851 end
2852 end
2853 end
2854 dataset.quit = quit
2855 end
2856 if slot < quit then
2857 result = trybreak_code
2858 penalty = eject
2859 end
2860 elseif action == "column" then
2861 if count > 0 then
2862 if not shaped then
2863 quit = slot + 1
2864 elseif slot == #shape then
2865 quit = #shape
2866 else
2867 quit = slot
2868 for i=slot+1,#shape-1 do
2869 local c = shape[i].column
2870 quit = quit + 1
2871 if column == nofcolumns and c == 1 then
2872 break
2873 elseif c > column then
2874 break
2875 end
2876 end
2877 end
2878 dataset.quit = quit
2879 end
2880 if slot < quit then
2881 result = trybreak_code
2882 penalty = eject
2883 end
2884 elseif action == "slot" then
2885 if count > 0 then
2886 quit = slot + count
2887 dataset.quit = quit
2888 end
2889 if slot < quit then
2890 result = trybreak_code
2891 penalty = eject
2892 end
2893 elseif action == "lines" or action == "amount" then
2894 if count > 0 then
2895 if action == "lines" then
2896 extra = count * (dataset.lineheight + dataset.linedepth)
2897 else
2898 extra = count
2899 end
2900 result = trybreak_code
2901 if trace then
2902 report(
2903 "name %a, action %s, count %i, slot %i, spread %i, page %i, column %i, extra %p",
2904 name, action, count, slot, spread, page, column, shaped, extra
2905 )
2906 end
2907 end
2908 goto TODO
2909 end
2910 if trace then
2911 report(
2912 "name %a, action %s, count %i, slot %i, spread %i, page %i, column %i, shaped %l, quit %i",
2913 name, action, count, slot, spread, page, column, shaped, quit
2914 )
2915 end
2916 ::TODO::
2917 end
2918 end
2919 end
2920 if trace then
2921 if result == skipzeros_code then
2922 report("action %s, ignore rest",action or "quit")
2923 else
2924 report("action %s, eject",action)
2925 end
2926 end
2927 return result, penalty, extra
2928 end)
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940 local function analyse(name,slot)
2941 local dataset = data[name]
2942 local spread = 0
2943 local page = 0
2944 local column = 0
2945 if dataset then
2946 local shape = dataset.shape
2947 if shape then
2948 columnsets.setextra(name,slot,false,true)
2949 local nofleft = dataset.nofleft
2950 local nofright = dataset.nofright
2951 local nofcolumns = nofleft + nofright
2952 local current = slot < #shape and shape[slot]
2953 local shaped = current and current.column ~= 0
2954 if shaped then
2955 spread = current.spread
2956 page = current.page
2957 column = current.column
2958 else
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971 end
2972 end
2973 end
2974 return spread, page, column, slot
2975 end
2976
2977
2978
2979 implement {
2980 name = "columnsetshapebalanceextend",
2981 arguments = { "argument", "integer" },
2982 actions = function(name,index)
2983 local dataset = data[name]
2984 if dataset then
2985 columnsets.setextra(name,index,false,true)
2986 end
2987 end
2988 }
2989
2990 implement {
2991 name = "columnsetshapebalancecheck",
2992 arguments = { "argument", "integer" },
2993 usage = "value",
2994 actions = function(name,index)
2995 local dataset = data[name]
2996 if dataset then
2997 local shape = dataset.shape[index]
2998 if shape then
2999 local spread = shape.spread
3000 local future = dataset.future[spread]
3001 if future then
3002 local column = shape.column
3003 cells = future[column]
3004 if cells then
3005 local first = false
3006 local last = false
3007 local found = false
3008
3009 for r=1,#cells do
3010 if not cells[r] then
3011 if not first then
3012 first = r
3013 end
3014 last = r
3015 end
3016 end
3017 local lastcolumn = column > dataset.nofleft and #future or dataset.nofleft
3018 local function okay(r)
3019 for c=column,lastcolumn do
3020 if future[c][r] then
3021 return false
3022 end
3023 end
3024 return true
3025 end
3026 for r=first,last do
3027 if not okay(r) then
3028 report("prepare found range %i..%i with occupied row %i on spread %i",first,last,r,spread)
3029 return integer_value, 0
3030 end
3031 end
3032
3033 return integer_value, 1
3034 end
3035 else
3036
3037 return integer_value, 1
3038 end
3039 end
3040 end
3041 return integer_value, 0
3042 end
3043 }
3044
3045
3046 implement {
3047 name = "columnsetshapesetbalancerange",
3048 arguments = { "argument", "integer" },
3049 usage = "value",
3050 actions = function(name,index)
3051 local dataset = data[name]
3052 if dataset then
3053 local shape = dataset.shape
3054 local where = shape[index]
3055 local first = 0
3056 local last = 0
3057 local page = 0
3058 if where then
3059 first = index
3060 last = index
3061 page = where.page
3062 for i=index-1,1,-1 do
3063 if shape[i].page == page then
3064 first = i
3065 else
3066 break
3067 end
3068 end
3069 for i=index+1,#shape-1 do
3070 if shape[i].page == page then
3071 last = i
3072 else
3073 break
3074 end
3075 end
3076 end
3077 dataset.balancefirst = first
3078 dataset.balancelast = last
3079 dataset.balancepage = page
3080 dataset.balanceindex = index
3081 end
3082 end
3083 }
3084
3085 implement {
3086 name = "columnsetbalancepage",
3087 arguments = "argument",
3088 usage = "value",
3089 actions = function(name)
3090 local dataset = data[name]
3091 return integer_value, dataset and dataset.balancepage or 0
3092 end
3093 }
3094
3095 implement {
3096 name = "columnsetbalancefirst",
3097 arguments = "argument",
3098 usage = "value",
3099 actions = function(name)
3100 local dataset = data[name]
3101 return integer_value, dataset and dataset.balancefirst or 0
3102 end
3103 }
3104
3105 implement {
3106 name = "columnsetbalancelast",
3107 arguments = "argument",
3108 usage = "value",
3109 actions = function(name)
3110 local dataset = data[name]
3111 return integer_value, dataset and dataset.balancelast or 0
3112 end
3113 }
3114
3115 implement {
3116 name = "columnsetshapespread",
3117 arguments = { "argument", "integer" },
3118 usage = "value",
3119 public = true,
3120 actions = function(name,index)
3121 local spread, page, column = analyse(name,index)
3122 return integer_value, spread
3123 end
3124 }
3125
3126 implement {
3127 name = "columnsetshapepage",
3128 arguments = { "argument", "integer" },
3129 usage = "value",
3130 public = true,
3131 actions = function(name,index)
3132 local spread, page, column = analyse(name,index)
3133 return integer_value, page
3134 end
3135 }
3136
3137 implement {
3138 name = "columnsetshapecolumn",
3139 arguments = { "argument", "integer" },
3140 usage = "value",
3141 public = true,
3142 actions = function(name,index)
3143 local spread, page, column = analyse(name,index)
3144 return integer_value, column
3145 end
3146 }
3147
3148 implement {
3149 name = "columnsetshapeslots",
3150 arguments = "argument",
3151 usage = "value",
3152 public = true,
3153 actions = function(name,index)
3154 local dataset = data[name]
3155 if dataset then
3156 return integer_value, #dataset.shape
3157 else
3158 return integer_value, 0
3159 end
3160 end
3161 }
3162
3163end
3164
3165do
3166
3167
3168
3169 function columnsets.blockrows(name,what,n)
3170 local dataset = data[name]
3171 if dataset then
3172 local future = dataset.future
3173 local nofrows = dataset.nofrows
3174 local nofleft = dataset.nofleft
3175 local nofright = dataset.nofright
3176 local nofcolumns = nofleft + nofright
3177
3178 local function check(f,r,first,last)
3179 local okay = false
3180 for c=first,last do
3181 if f[c][r] then
3182 okay = true
3183 end
3184 end
3185 if okay then
3186 for c=first,last do
3187 local fc = f[c]
3188 if not fc[r] then
3189 fc[r] = true
3190 end
3191 end
3192 end
3193 end
3194
3195 for i=1,n do
3196 local f = future[i]
3197 if what == v_spread then
3198 for r=1,nofrows do
3199 check(f,r,1,nofcolumns)
3200 end
3201 else
3202 for r=1,nofrows do
3203 check(f,r,1,nofleft)
3204 check(f,r,nofleft+1,nofcolumns)
3205 end
3206 end
3207 end
3208
3209 dataset.currentrow = 1
3210 dataset.currentcolumn = 1
3211 end
3212 end
3213
3214 implement {
3215 name = "columnsetsblockrows",
3216 arguments = { "argument", "argument", "integer" },
3217 actions = columnsets.blockrows,
3218 }
3219
3220 implement {
3221 name = "columnsetdisablewide",
3222 arguments = "argument",
3223 actions = function(name)
3224 local dataset = data[name]
3225 if dataset then
3226 local disabled = dataset.disabled
3227 for i=1,dataset.nofcolumns do
3228 disabled[i] = false
3229 end
3230 end
3231 end
3232 }
3233
3234 implement {
3235 name = "columnsetenablewide",
3236 arguments = "argument",
3237 actions = function(name)
3238 local dataset = data[name]
3239 if dataset then
3240 local disabled = dataset.disabled
3241 for i=1,dataset.nofcolumns do
3242 disabled[i] = true
3243 end
3244 disabled[1] = false
3245 disabled[dataset.nofleft+1] = false
3246 end
3247 end
3248 }
3249
3250end
3251 |