1if not modules then modules = { } end modules ['back-out'] = {
2 version = 1.001,
3 comment = "companion to back-ini.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
9local type, unpack, rawset = type, unpack, type
10local loadstring = loadstring
11local sind, cosd, abs = math.sind, math.cosd, math.abs
12local insert, remove = table.insert, table.remove
13
14local context = context
15local implement = interfaces.implement
16
17local allocate = utilities.storage.allocate
18
19local formatters = string.formatters
20
21local get = tokens.accessors.index
22
23local scanners = tokens.scanners
24local scaninteger = scanners.integer
25local scanstring = scanners.string
26local scankeyword = scanners.keyword
27local scantokenlist = scanners.tokenlist
28local scannumber = scanners.number
29
30local nuts = nodes.nuts
31local tonode = nuts.tonode
32local copynut = nuts.copy
33local nutpool = nuts.pool
34local nodepool = nodes.pool
35
36local nodeproperties = nodes.properties.data
37
38local register = nutpool.register
39local newnut = nuts.new
40local tonode = nodes.tonode
41
42
43
44
45
46backends = backends or { }
47local backends = backends
48
49local whatsitcodes = allocate { }
50nodes.whatsitcodes = whatsitcodes
51local lastwhatsit = 0
52
53local whatsit_code <const> = nodes.nodecodes.whatsit
54
55nodes.subtypes.whatsit = whatsitcodes
56nodes.subtypes[whatsit_code] = whatsitcodes
57
58local function registerwhatsit(name)
59 lastwhatsit = lastwhatsit + 1
60 whatsitcodes[lastwhatsit] = name
61 whatsitcodes[name] = lastwhatsit
62 return lastwhatsit
63end
64
65local function registerwhatsitnode(name)
66 return register(newnut(whatsit_code,registerwhatsit(name)))
67end
68
69
70
71do
72
73 local literalvalues = allocate { }
74 nodes.literalvalues = literalvalues
75 local lastliteral = 0
76 local literalnode = registerwhatsitnode("literal")
77
78 local function registerliteral(name,alias)
79 lastliteral = lastliteral + 1
80 literalvalues[lastliteral] = name
81 literalvalues[name] = lastliteral
82 if alias then
83 literalvalues[alias] = lastliteral
84 end
85 return lastliteral
86 end
87
88 local originliteral_code = registerliteral("origin")
89 local pageliteral_code = registerliteral("page")
90 local directliteral_code = registerliteral("always","direct")
91 local rawliteral_code = registerliteral("raw")
92 local textliteral_code = registerliteral("text")
93 local fontliteral_code = registerliteral("font")
94
95 function nutpool.originliteral(str) local t = copynut(literalnode) nodeproperties[t] = { data = str, mode = originliteral_code } return t end
96 function nutpool.pageliteral (str) local t = copynut(literalnode) nodeproperties[t] = { data = str, mode = pageliteral_code } return t end
97 function nutpool.directliteral(str) local t = copynut(literalnode) nodeproperties[t] = { data = str, mode = directliteral_code } return t end
98 function nutpool.rawliteral (str) local t = copynut(literalnode) nodeproperties[t] = { data = str, mode = rawliteral_code } return t end
99
100 local pdfliterals = {
101 [originliteral_code] = originliteral_code, [literalvalues[originliteral_code]] = originliteral_code,
102 [pageliteral_code] = pageliteral_code, [literalvalues[pageliteral_code]] = pageliteral_code,
103 [directliteral_code] = directliteral_code, [literalvalues[directliteral_code]] = directliteral_code,
104 [rawliteral_code] = rawliteral_code, [literalvalues[rawliteral_code]] = rawliteral_code,
105 }
106
107 function nutpool.literal(mode,str)
108 local t = copynut(literalnode)
109 if str then
110 nodeproperties[t] = { data = str, mode = pdfliterals[mode] or pageliteral_code }
111 else
112 nodeproperties[t] = { data = mode, mode = pageliteral_code }
113 end
114 return t
115 end
116
117end
118
119
120
121
122do
123
124 local getdata = nuts.getdata
125 local serialize = token.serialize
126
127 local lateluanode = registerwhatsitnode("latelua")
128 local noflatelua = 0
129
130 function nutpool.latelua(code)
131 local n = copynut(lateluanode)
132 nodeproperties[n] = { data = code }
133 return n
134 end
135
136 function backends.latelua(current,pos_h,pos_v)
137 local prop = nodeproperties[current]
138 local data = prop and prop.data or getdata(current)
139 local kind = type(data)
140 noflatelua = noflatelua + 1
141 if kind == "table" then
142 data.action(data.specification or data,current,pos_h,pos_v)
143 elseif kind == "function" then
144 data(current,pos_h,pos_v)
145 else
146 if kind ~= "string" then
147 data = serialize(data)
148 end
149 if data and #data ~= "" then
150 local code = loadstring(data)
151 if code then
152 code(current,pos_h,pos_v)
153 end
154 end
155 end
156 end
157
158 function backends.getcallbackstate()
159 return { count = noflatelua }
160 end
161
162 implement {
163 name = "latelua",
164 public = true,
165 protected = true,
166 untraced = true,
167 actions = function()
168 local node = copynut(lateluanode)
169 local name = "latelua"
170 if scankeyword("name") then
171 name = scanstring()
172 end
173 local data = scantokenlist()
174 nodeproperties[node] = { name = name, data = data }
175 return context(tonode(node))
176 end,
177 }
178
179end
180
181
182
183
184do
185
186 local usernode = registerwhatsitnode("userdefined")
187
188 local userids = allocate()
189 local lastid = 0
190
191 setmetatable(userids, {
192 __index = function(t,k)
193 if type(k) == "string" then
194 lastid = lastid + 1
195 rawset(userids,lastid,k)
196 rawset(userids,k,lastid)
197 return lastid
198 else
199 rawset(userids,k,k)
200 return k
201 end
202 end,
203 __call = function(t,k)
204 return t[k]
205 end
206 } )
207
208 function nutpool.userdefined(id,data)
209 local n = copynut(usernode)
210 nodeproperties[n] = { id = id, data = data }
211 return n
212 end
213
214 nutpool .usernode = nutpool.userdefined
215 nutpool .userids = userids
216 nodepool.userids = userids
217
218end
219
220
221
222do
223
224 local saveposnode = registerwhatsitnode("savepos")
225
226 function nutpool.savepos()
227 return copynut(saveposnode)
228 end
229
230end
231
232do
233
234 local savenode = registerwhatsitnode("save")
235 local restorenode = registerwhatsitnode("restore")
236 local setmatrixnode = registerwhatsitnode("setmatrix")
237
238 local stack = { }
239 local restore = true
240
241
242
243 function nutpool.save()
244 return copynut(savenode)
245 end
246
247 function nutpool.restore()
248 return copynut(restorenode)
249 end
250
251 function nutpool.setmatrix(rx,sx,sy,ry,tx,ty)
252 local t = copynut(setmatrixnode)
253 nodeproperties[t] = { matrix = { rx, sx, sy, ry, tx, ty } }
254 return t
255 end
256
257 implement {
258 name = "startgraphicgroup",
259 actions = function()
260 context(tonode(copynut(savenode)))
261 end,
262 }
263
264 implement {
265 name = "stopgraphicgroup",
266 actions = function()
267 context(tonode(copynut(restorenode)))
268 end,
269 }
270
271
272
273 local function stopcommon(n)
274 local top = remove(stack)
275 if top == false then
276 return
277 elseif top == true then
278 return copynut(n)
279 elseif top then
280 local t = copynut(n)
281 nodeproperties[t] = { matrix = { unpack(top) } }
282 return t
283 else
284 return
285 end
286 end
287
288
289
290 local startmatrixnode = registerwhatsitnode("startmatrix")
291 local stopmatrixnode = registerwhatsitnode("stopmatrix")
292
293 local function startmatrix(rx, sx, sy, ry)
294 if rx == 1 and sx == 0 and sy == 0 and ry == 1 then
295 insert(stack,false)
296 else
297 local t = copynut(startmatrixnode)
298 nodeproperties[t] = { matrix = { rx, sx, sy, ry } }
299 insert(stack,store and { -rx, -sx, -sy, -ry } or true)
300 return t
301 end
302 end
303
304 local function stopmatrix()
305 return stopcommon(stopmatrixnode)
306 end
307
308 implement {
309 name = "startmatrix",
310 actions = function()
311 local rx, sx, sy, ry = 1, 0, 0, 1
312 while true do
313 if scankeyword("rx") then rx = scannumber()
314 elseif scankeyword("ry") then ry = scannumber()
315 elseif scankeyword("sx") then sx = scannumber()
316 elseif scankeyword("sy") then sy = scannumber()
317 else break end
318 end
319 local t = startmatrix(rx,sx,sy,ry)
320 if t then
321 context(tonode(t))
322 end
323 end,
324 }
325
326 implement {
327 name = "stopmatrix",
328 actions = function()
329 local t = stopmatrix(n)
330 if t then
331 context(tonode(t))
332 end
333 end,
334 }
335
336
337
338 local startscalingnode = registerwhatsitnode("startscaling")
339 local stopscalingnode = registerwhatsitnode("stopscaling")
340
341 local function startscaling(rx,ry)
342 if rx == 1 and ry == 1 then
343 insert(stack,false)
344 else
345 if rx == 0 then
346 rx = 0.0001
347 end
348 if ry == 0 then
349 ry = 0.0001
350 end
351 local t = copynut(startscalingnode)
352 nodeproperties[t] = { matrix = { rx, 0, 0, ry } }
353 insert(stack,restore and { 1/rx, 0, 0, 1/ry } or true)
354 return t
355 end
356 end
357
358 local function stopscaling()
359 return stopcommon(stopscalingnode)
360 end
361
362 nutpool.startscaling = startscaling
363 nutpool.stopscaling = stopscaling
364
365 implement {
366 name = "startscaling",
367 actions = function()
368 local rx, ry = 1, 1
369 while true do
370 if scankeyword("rx") then
371 rx = scannumber()
372 elseif scankeyword("ry") then
373 ry = scannumber()
374 else
375 break
376 end
377 end
378 local t = startscaling(rx,ry)
379 if t then
380 context(tonode(t))
381 end
382 end,
383 }
384
385 implement {
386 name = "stopscaling",
387 actions = function()
388 local t = stopscaling(n)
389 if t then
390 context(tonode(t))
391 end
392 end,
393 }
394
395
396
397 local startrotationnode = registerwhatsitnode("startrotation")
398 local stoprotationnode = registerwhatsitnode("stoprotation")
399
400 local function startrotation(a)
401 if a == 0 then
402 insert(stack,false)
403 else
404 local s, c = sind(a), cosd(a)
405 if abs(s) < 0.000001 then
406 s = 0
407 end
408 if abs(c) < 0.000001 then
409 c = 0
410 end
411 local t = copynut(startrotationnode)
412 nodeproperties[t] = { matrix = { c, s, -s, c } }
413 insert(stack,restore and { c, -s, s, c } or true)
414 return t
415 end
416 end
417
418 local function stoprotation()
419 return stopcommon(stoprotationnode)
420 end
421
422 nutpool.startrotation = startrotation
423 nutpool.stoprotation = stoprotation
424
425 implement {
426 name = "startrotation",
427 actions = function()
428 local n = scannumber()
429 local t = startrotation(n)
430 if t then
431 context(tonode(t))
432 end
433 end,
434 }
435
436 implement {
437 name = "stoprotation",
438 actions = function()
439 local t = stoprotation()
440 if t then
441 context(tonode(t))
442 end
443 end,
444 }
445
446
447
448 local startmirroringnode = registerwhatsitnode("startmirroring")
449 local stopmirroringnode = registerwhatsitnode("stopmirroring")
450
451 local function startmirroring()
452 local t = copynut(startmirroringnode)
453 nodeproperties[t] = { matrix = { -1, 0, 0, 1 } }
454 return t
455 end
456
457 local function stopmirroring()
458 local t = copynut(stopmirroringnode)
459 nodeproperties[t] = { matrix = { -1, 0, 0, 1 } }
460 return t
461 end
462
463 implement {
464 name = "startmirroring",
465 actions = function()
466 context(tonode(startmirroring()))
467 end,
468 }
469
470 implement {
471 name = "stopmirroring",
472 actions = function()
473 context(tonode(stopmirroring()))
474 end,
475 }
476
477
478
479 local startclippingnode = registerwhatsitnode("startclipping")
480 local stopclippingnode = registerwhatsitnode("stopclipping")
481
482 local function startclipping(path)
483 local t = copynut(startclippingnode)
484 nodeproperties[t] = { path = path }
485 return t
486 end
487
488 local function stopclipping()
489 return copynut(stopclippingnode)
490 end
491
492 implement {
493 name = "startclipping",
494 actions = function()
495 context(tonode(startclipping(scanstring())))
496 end
497 }
498
499 implement {
500 name = "stopclipping",
501 actions = function()
502 context(tonode(stopclipping()))
503 end,
504 }
505
506end
507
508
509
510
511do
512
513 local logwriter = logs.writer
514 local openfile = io.open
515 local flushio = io.flush
516 local serialize = token.serialize
517
518 local opennode = registerwhatsitnode("open")
519 local writenode = registerwhatsitnode("write")
520 local closenode = registerwhatsitnode("close")
521 local channels = { }
522
523 local immediate_code <const> = tex.flagcodes.immediate
524
525 function backends.openout(n)
526 local p = nodeproperties[n]
527 if p then
528 local handle = openfile(p.filename,"wb") or false
529 if handle then
530 channels[p.channel] = handle
531 else
532
533 end
534 end
535 end
536
537 function backends.writeout(n)
538 local p = nodeproperties[n]
539 if p then
540 local handle = channels[p.channel]
541 local content = serialize(p.data)
542 if handle then
543 handle:write(content,"\n")
544 else
545 logwriter(content,"\n")
546 end
547 end
548 end
549
550 function backends.closeout(n)
551 local p = nodeproperties[n]
552 if p then
553 local channel = p.channel
554 local handle = channels[channel]
555 if handle then
556 handle:close()
557 channels[channel] = false
558 flushio()
559 else
560
561 end
562 end
563 end
564
565 local function immediately(prefix)
566 return prefix and (prefix & immediate_code) ~= 0
567 end
568
569 implement {
570 name = "openout",
571 public = true,
572 usage = "value",
573 actions = function(prefix)
574 local channel = scaninteger()
575 scankeyword("=")
576 local filename = scanstring()
577 if not immediately(prefix) then
578 local n = copynut(opennode)
579 nodeproperties[n] = { channel = channel, filename = filename }
580 return context(tonode(n))
581 elseif not channels[channel] then
582 local handle = openfile(filename,"wb") or false
583 if handle then
584 channels[channel] = handle
585 else
586
587 end
588 end
589 end,
590 }
591
592 implement {
593 name = "write",
594 public = true,
595 usage = "value",
596 actions = function(prefix)
597 local channel = scaninteger()
598 if not immediately(prefix) then
599 local t = scantokenlist()
600 local n = copynut(writenode)
601 nodeproperties[n] = { channel = channel, data = t }
602 return context(tonode(n))
603 else
604 local content = scanstring()
605 local handle = channels[channel]
606 if handle then
607 handle:write(content,"\n")
608 else
609 logwriter(content,"\n")
610 end
611 end
612 end,
613 }
614
615 implement {
616 name = "closeout",
617 public = true,
618 usage = "value",
619 actions = function(prefix)
620 local channel = scaninteger()
621 if not immediately(prefix) then
622 local n = copynut(closenode)
623 nodeproperties[n] = { channel = channel }
624 return context(tonode(n))
625 else
626 local handle = channels[channel]
627 if handle then
628 handle:close()
629 channels[channel] = false
630 flushio()
631 else
632
633 end
634 end
635 end,
636 }
637
638
639
640 local open_command = get(token.create("openout"))
641 local write_command = get(token.create("write"))
642 local close_command = get(token.create("closeout"))
643
644end
645
646
647
648do
649
650 local setstatenode = registerwhatsitnode("setstate")
651
652 function nutpool.setstate(data)
653 local n = copynut(setstatenode)
654 nodeproperties[n] = { data = data }
655 return n
656 end
657
658end
659
660
661
662do
663 implement {
664 name = "special",
665 actions = scanstring,
666 public = true,
667 protected = true,
668 }
669end
670 |