1if not modules then modules = { } end modules ['page-flt'] = {
2 version = 1.001,
3 comment = "companion to page-flt.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12local next = next
13local tostring = tostring
14local insert, remove = table.insert, table.remove
15local find, topattern = string.find, string.topattern
16local abs = math.abs
17
18local trace_floats = false trackers.register("floats.caching", function(v) trace_floats = v end)
19local trace_collecting = false trackers.register("floats.collecting", function(v) trace_collecting = v end)
20
21local report_floats = logs.reporter("floats","caching")
22local report_collecting = logs.reporter("floats","collecting")
23
24local C, Cc, S, P, lpegmatch = lpeg.C, lpeg.Cc, lpeg.S, lpeg.P, lpeg.match
25
26
27
28
29local setdimen = tex.setdimen
30local getdimen = tex.getdimen
31local setcount = tex.setcount
32local texsetbox = tex.setbox
33local textakebox = nodes.takebox
34
35floats = floats or { }
36local floats = floats
37
38local context = context
39local commands = commands
40local interfaces = interfaces
41local showmessage = interfaces.showmessage
42local implement = interfaces.implement
43local setmacro = interfaces.setmacro
44
45local noffloats = 0
46local last = nil
47local default = "text"
48local pushed = { }
49
50local function initialize()
51 return {
52 text = { },
53 page = { },
54 top = { },
55 bottom = { },
56 leftpage = { },
57 rightpage = { },
58 somewhere = { },
59 }
60end
61
62local stacks = initialize()
63
64
65
66function floats.stacked(which)
67 return #stacks[which or default]
68end
69
70function floats.push()
71 insert(pushed,stacks)
72 stacks = initialize()
73 setcount("global","savednoffloats",0)
74end
75
76function floats.pop()
77 local popped = remove(pushed)
78 if popped then
79 for which, stack in next, stacks do
80 for i=1,#stack do
81 insert(popped[which],stack[i])
82 end
83 end
84 stacks = popped
85 setcount("global","savednoffloats",#stacks[default])
86 end
87end
88
89local function setdimensions(t,b)
90 local bw, bh, bd = 0, 0, 0
91 local nw, nh, nd = 0, 0, 0
92 local cw, ch, cd = 0, 0, 0
93 if b then
94 bw = b.width
95 bh = b.height
96 bd = b.depth
97 cw = b.cwidth
98 ch = b.cheight
99 cd = b.cdepth
100 end
101 if t then
102 nw = t.width or bw
103 nh = t.height or bh
104 nd = t.depth or bd
105 cw = t.cwidth or cw
106 ch = t.cheight or ch
107 cd = t.cdepth or cd
108 end
109 setdimen("global","floatwidth", bw)
110 setdimen("global","floatheight", bh+bd)
111 setdimen("global","naturalfloatwd", nw)
112 setdimen("global","naturalfloatht", nh)
113 setdimen("global","naturalfloatdp", nd)
114 setdimen("global","floatcaptionwd", cw)
115 setdimen("global","floatcaptionht", ch)
116 setdimen("global","floatcaptiondp", cd)
117 return bw, bh, bd, nw, nh, dp, cw, xh, xp
118end
119
120local function get(stack,n,bylabel)
121 if bylabel then
122 for i=1,#stack do
123 local s = stack[i]
124 local n = topattern(tostring(n))
125 if find(s.data.label,n) then
126 return s, s.box, i
127 end
128 end
129 else
130 n = n or #stack
131 if n > 0 then
132 local t = stack[n]
133 if t then
134 return t, t.box, n
135 end
136 end
137 end
138end
139
140function floats.save(which,data)
141 which = which or default
142 local b = textakebox("floatbox")
143 if b then
144 local stack = stacks[which]
145 noffloats = noffloats + 1
146 local t = {
147 n = noffloats,
148 data = data or { },
149 width = getdimen("naturalfloatwd"),
150 height = getdimen("naturalfloatht"),
151 depth = getdimen("naturalfloatdp"),
152 cwidth = getdimen("floatcaptionwd"),
153 cheight = getdimen("floatcaptionht"),
154 cdepth = getdimen("floatcaptiondp"),
155 box = b,
156 }
157 insert(stack,t)
158
159 setcount("global","savednoffloats",#stacks[default])
160 if trace_floats then
161 report_floats("%s, category %a, number %a, slot %a, width %p, height %p, depth %p","saving",
162 which,noffloats,#stack,b.width,b.height,b.depth)
163 else
164 showmessage("floatblocks",2,noffloats)
165 end
166 else
167 report_floats("ignoring empty, category %a, number %a",which,noffloats)
168 end
169end
170
171function floats.resave(which)
172 if last then
173 which = which or default
174 local stack = stacks[which]
175 local b = textakebox("floatbox")
176 if not b then
177 report_floats("resaved float is empty")
178 end
179 last.box = b
180 insert(stack,1,last)
181 setcount("global","savednoffloats",#stacks[default])
182 if trace_floats then
183 report_floats("%s, category %a, number %a, slot %a width %p, height %p, depth %p","resaving",
184 which,noffloats,#stack,b.width,b.height,b.depth)
185 else
186 showmessage("floatblocks",2,noffloats)
187 end
188 else
189 report_floats("unable to resave float")
190 end
191end
192
193function floats.flush(which,n,bylabel)
194 which = which or default
195 local stack = stacks[which]
196 local t, b, n = get(stack,n or 1,bylabel)
197 if t then
198 if not b then
199 showmessage("floatblocks",1,t.n)
200 end
201 local w, h, d = setdimensions(t,b)
202 if trace_floats then
203 report_floats("%s, category %a, number %a, slot %a width %p, height %p, depth %p","flushing",
204 which,t.n,n,w,h,d)
205 else
206 showmessage("floatblocks",3,t.n)
207 end
208 texsetbox("floatbox",b)
209 last = remove(stack,n)
210 last.box = nil
211 setcount("global","savednoffloats",#stacks[which])
212 else
213 setdimensions()
214 end
215end
216
217function floats.consult(which,n)
218 which = which or default
219 local stack = stacks[which]
220 local t, b, n = get(stack,n)
221 if t then
222 local w, h, d = setdimensions(t,b)
223 if trace_floats then
224 report_floats("%s, category %a, number %a, slot %a width %p, height %p, depth %p","consulting",
225 which,t.n,n,w,h,d)
226 end
227 return t, b, n
228 else
229 if trace_floats then
230 report_floats("nothing to consult")
231 end
232 setdimensions()
233 end
234end
235
236function floats.collect(which,maxwidth,distance)
237 local usedwhich = which or default
238 local stack = stacks[usedwhich]
239 local stacksize = #stack
240 local collected = 0
241 local maxheight = 0
242 local maxdepth = 0
243
244 local function register()
245 collected = collected + 1
246 maxwidth = rest
247 if h > maxheight then
248 maxheight = h
249 end
250 if d > maxdepth then
251 maxdepth = d
252 end
253 end
254
255 for i=1,stacksize do
256 local t, b, n = get(stack,i)
257 if t then
258 local w, h, d, nw, nh, nd, cw, ch, cd = setdimensions(t,b)
259
260 if cw > nw then
261 w = cw
262 else
263 w = nw
264 end
265
266 local rest = maxwidth - w - distance
267 local fits = rest > -10
268 if trace_collecting then
269 report_collecting("%s, category %a, number %a, slot %a width %p, rest %p, fit %a","collecting",
270 usedwhich,t.n,n,w,rest,fits)
271 end
272 if fits then
273 collected = collected + 1
274 maxwidth = rest
275 if h > maxheight then
276 maxheight = h
277 end
278 if d > maxdepth then
279 maxdepth = d
280 end
281 else
282 break
283 end
284 else
285 break
286 end
287 end
288 setcount("global","nofcollectedfloats",collected)
289 setdimen("global","maxcollectedfloatstotal",maxheight+maxdepth)
290end
291
292function floats.getvariable(name,default)
293 local value = last and last.data[name] or default
294 return value ~= "" and value
295end
296
297function floats.checkedpagefloat(packed)
298 if structures.pages.is_odd() then
299 if #stacks.rightpage > 0 then
300 return "rightpage"
301 elseif #stacks.page > 0 then
302 return "page"
303 elseif #stacks.leftpage > 0 then
304 if packed then
305 return "leftpage"
306 end
307 end
308 else
309 if #stacks.leftpage > 0 then
310 return "leftpage"
311 elseif #stacks.page > 0 then
312 return "page"
313 elseif #stacks.rightpage > 0 then
314 if packed then
315 return "rightpage"
316 end
317 end
318 end
319end
320
321function floats.nofstacked(which)
322 local s = stacks[which or default]
323 return s and #s or 0
324end
325
326function floats.hasstacked(which)
327 local s = stacks[which or default]
328 return (s and #s or 0) > 0
329end
330
331
332
333local digits = lpeg.patterns.digits
334local nothing = Cc("")
335local method = C((1-S(", :"))^1)
336local position = P(":") * C(digits^1) * (P("*") * C(digits^1) + nothing)
337local label = P(":") * C((1-S(",*: "))^0)
338
339local pattern = method * (
340 label * position
341 + nothing * position
342 + label * nothing * nothing
343 + nothing * nothing * nothing
344) + nothing * nothing * nothing * nothing
345
346
347
348
349
350
351
352
353
354function floats.analysemethod(str)
355 return lpegmatch(pattern,str or "")
356end
357
358
359
360implement {
361 name = "flushfloat",
362 actions = floats.flush,
363 arguments = { "string", "integer" },
364}
365
366implement {
367 name = "flushlabeledfloat",
368 actions = floats.flush,
369 arguments = { "string", "string", true },
370}
371
372implement {
373 name = "savefloat",
374 actions = floats.save,
375 arguments = "string"
376}
377
378implement {
379 name = "savespecificfloat",
380 actions = floats.save,
381 arguments = {
382 "string",
383 {
384 { "specification" },
385 { "label" },
386 }
387 }
388}
389
390implement {
391 name = "resavefloat",
392 actions = floats.resave,
393 arguments = "string"
394}
395
396implement {
397 name = "pushfloat",
398 actions = floats.push
399}
400
401implement {
402 name = "popfloat",
403 actions = floats.pop
404}
405
406implement {
407 name = "consultfloat",
408 actions = floats.consult,
409 arguments = "string",
410}
411
412implement {
413 name = "collectfloat",
414 actions = floats.collect,
415 arguments = { "string", "dimen", "dimen" }
416}
417
418implement {
419 name = "getfloatvariable",
420 actions = { floats.getvariable, context },
421 arguments = "string"
422}
423
424implement {
425 name = "checkedpagefloat",
426 actions = { floats.checkedpagefloat, context },
427 arguments = "string"
428}
429
430implement {
431 name = "nofstackedfloats",
432 actions = { floats.nofstacked, context },
433 arguments = "string"
434}
435
436implement {
437 name = "doifelsestackedfloats",
438 actions = { floats.hasstacked, commands.doifelse },
439 arguments = "string"
440}
441
442implement {
443 name = "analysefloatmethod",
444 actions = function(str)
445 local method, label, column, row = floats.analysemethod(str)
446 setmacro("floatmethod",method or "")
447 setmacro("floatlabel", label or "")
448 setmacro("floatrow", row or "")
449 setmacro("floatcolumn",column or "")
450 end,
451 arguments = "string"
452}
453 |