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