1if not modules then modules = { } end modules ['lpdf-mis'] = {
2 version = 1.001,
3 comment = "companion to lpdf-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
9
10
11
12
13
14
15
16
17
18local next, tostring, type = next, tostring, type
19local format, gsub, formatters = string.format, string.gsub, string.formatters
20local concat, flattened = table.concat, table.flattened
21local settings_to_array = utilities.parsers.settings_to_array
22
23local backends, lpdf, nodes = backends, lpdf, nodes
24
25local nodeinjections = backends.pdf.nodeinjections
26local codeinjections = backends.pdf.codeinjections
27local registrations = backends.pdf.registrations
28
29local nuts = nodes.nuts
30local copy_node = nuts.copy
31
32local nodepool = nuts.pool
33local pageliteral = nodepool.pageliteral
34local register = nodepool.register
35
36local pdfdictionary = lpdf.dictionary
37local pdfarray = lpdf.array
38local pdfconstant = lpdf.constant
39local pdfreference = lpdf.reference
40local pdfunicode = lpdf.unicode
41local pdfverbose = lpdf.verbose
42local pdfstring = lpdf.string
43local pdfflushobject = lpdf.flushobject
44local pdfflushstreamobject = lpdf.flushstreamobject
45local pdfaction = lpdf.action
46local pdfminorversion = lpdf.minorversion
47
48local formattedtimestamp = lpdf.pdftimestamp
49local adddocumentextgstate = lpdf.adddocumentextgstate
50local addtocatalog = lpdf.addtocatalog
51local addtoinfo = lpdf.addtoinfo
52local addtopageattributes = lpdf.addtopageattributes
53local addtonames = lpdf.addtonames
54
55local pdfgetmetadata = lpdf.getmetadata
56
57local texset = tex.set
58
59local variables = interfaces.variables
60
61local v_stop = variables.stop
62local v_none = variables.none
63local v_max = variables.max
64local v_bookmark = variables.bookmark
65local v_fit = variables.fit
66local v_doublesided = variables.doublesided
67local v_singlesided = variables.singlesided
68local v_default = variables.default
69local v_auto = variables.auto
70local v_fixed = variables.fixed
71local v_landscape = variables.landscape
72local v_portrait = variables.portrait
73local v_page = variables.page
74local v_paper = variables.paper
75local v_attachment = variables.attachment
76local v_layer = variables.layer
77local v_lefttoright = variables.lefttoright
78local v_righttoleft = variables.righttoleft
79local v_title = variables.title
80local v_nomenubar = variables.nomenubar
81
82local positive = register(pageliteral("/GSpositive gs"))
83local negative = register(pageliteral("/GSnegative gs"))
84local overprint = register(pageliteral("/GSoverprint gs"))
85local knockout = register(pageliteral("/GSknockout gs"))
86
87local omitextraboxes = false
88
89directives.register("backend.omitextraboxes", function(v) omitextraboxes = v end)
90
91local function initializenegative()
92 local a = pdfarray { 0, 1 }
93 local g = pdfconstant("ExtGState")
94 local d = pdfdictionary {
95 FunctionType = 4,
96 Range = a,
97 Domain = a,
98 }
99 local negative = pdfdictionary { Type = g, TR = pdfreference(pdfflushstreamobject("{ 1 exch sub }",d)) }
100 local positive = pdfdictionary { Type = g, TR = pdfconstant("Identity") }
101 adddocumentextgstate("GSnegative", pdfreference(pdfflushobject(negative)))
102 adddocumentextgstate("GSpositive", pdfreference(pdfflushobject(positive)))
103 initializenegative = nil
104end
105
106local function initializeoverprint()
107 local g = pdfconstant("ExtGState")
108 local knockout = pdfdictionary { Type = g, OP = false, OPM = 0 }
109 local overprint = pdfdictionary { Type = g, OP = true, OPM = 1 }
110 adddocumentextgstate("GSknockout", pdfreference(pdfflushobject(knockout)))
111 adddocumentextgstate("GSoverprint", pdfreference(pdfflushobject(overprint)))
112 initializeoverprint = nil
113end
114
115function nodeinjections.overprint()
116 if initializeoverprint then initializeoverprint() end
117 return copy_node(overprint)
118end
119function nodeinjections.knockout ()
120 if initializeoverprint then initializeoverprint() end
121 return copy_node(knockout)
122end
123
124function nodeinjections.positive()
125 if initializenegative then initializenegative() end
126 return copy_node(positive)
127end
128function nodeinjections.negative()
129 if initializenegative then initializenegative() end
130 return copy_node(negative)
131end
132
133
134
135
136
137
138
139
140
141
142
143
144
145local openpage, closepage, opendocument, closedocument
146
147function codeinjections.registerdocumentopenaction(open)
148 opendocument = open
149end
150
151function codeinjections.registerdocumentcloseaction(close)
152 closedocument = close
153end
154
155function codeinjections.registerpageopenaction(open)
156 openpage = open
157end
158
159function codeinjections.registerpagecloseaction(close)
160 closepage = close
161end
162
163local function flushdocumentactions()
164 if opendocument then
165 addtocatalog("OpenAction",pdfaction(opendocument))
166 end
167 if closedocument then
168 addtocatalog("CloseAction",pdfaction(closedocument))
169 end
170end
171
172local function flushpageactions()
173 if openpage or closepage then
174 local d = pdfdictionary()
175 if openpage then
176 d.O = pdfaction(openpage)
177 end
178 if closepage then
179 d.C = pdfaction(closepage)
180 end
181 addtopageattributes("AA",d)
182 end
183end
184
185lpdf.registerpagefinalizer (flushpageactions, "page actions")
186lpdf.registerdocumentfinalizer(flushdocumentactions,"document actions")
187
188
189
190local identity = { }
191
192function codeinjections.setupidentity(specification)
193 for k, v in next, specification do
194 if v ~= "" then
195 identity[k] = v
196 end
197 end
198end
199
200function codeinjections.getidentityvariable(name)
201 return identity[name]
202end
203
204local done = false
205
206local function setupidentity()
207 if not done then
208 local metadata = pdfgetmetadata()
209 local creator = metadata.creator
210 local version = metadata.contextversion
211 local time = metadata.time
212 local jobname = environment.jobname or tex.jobname or "unknown"
213
214 local title = identity.title
215 if not title or title == "" then
216 title = tex.jobname
217 end
218 addtoinfo("Title", pdfunicode(title), title)
219 local subtitle = identity.subtitle or ""
220 if subtitle ~= "" then
221 addtoinfo("Subject", pdfunicode(subtitle), subtitle)
222 end
223 local author = identity.author or ""
224 if author ~= "" then
225 addtoinfo("Author", pdfunicode(author), author)
226 end
227 addtoinfo("Creator", pdfunicode(creator), creator)
228 addtoinfo("CreationDate", pdfstring(formattedtimestamp(time)))
229 local date = identity.date or ""
230 local pdfdate = date and formattedtimestamp(date)
231 if pdfdate then
232 addtoinfo("ModDate", pdfstring(pdfdate), date)
233 else
234
235
236 addtoinfo("ModDate", pdfstring(formattedtimestamp(time)),time)
237 end
238 local keywords = identity.keywords or ""
239 if keywords ~= "" then
240 keywords = concat(settings_to_array(keywords), " ")
241 addtoinfo("Keywords", pdfunicode(keywords), keywords)
242 end
243 local id = lpdf.id()
244 addtoinfo("ID", pdfstring(id), id)
245
246 addtoinfo("ConTeXt.Version",version)
247 addtoinfo("ConTeXt.Time",os.date("%Y-%m-%d %H:%M"))
248 addtoinfo("ConTeXt.Jobname",jobname)
249
250 addtoinfo("ConTeXt.Url","github.com/contextgarden/context")
251 addtoinfo("ConTeXt.Support","contextgarden.net")
252 addtoinfo("TeX.Support","tug.org")
253
254 done = true
255 else
256
257 end
258end
259
260lpdf.registerpagefinalizer(setupidentity,"identity")
261
262
263
264
265
266local function flushjavascripts()
267 local t = interactions.javascripts.flushpreambles()
268 if #t > 0 then
269 local a = pdfarray()
270 local pdf_javascript = pdfconstant("JavaScript")
271 for i=1,#t do
272 local ti = t[i]
273 local name = ti[1]
274 local script = ti[2]
275 local j = pdfdictionary {
276 S = pdf_javascript,
277 JS = pdfreference(pdfflushstreamobject(script)),
278 }
279 a[#a+1] = pdfstring(name)
280 a[#a+1] = pdfreference(pdfflushobject(j))
281 end
282 addtonames("JavaScript",pdfreference(pdfflushobject(pdfdictionary{ Names = a })))
283 end
284end
285
286lpdf.registerdocumentfinalizer(flushjavascripts,"javascripts")
287
288
289
290local plusspecs = {
291 [v_max] = {
292 mode = "FullScreen",
293 },
294 [v_bookmark] = {
295 mode = "UseOutlines",
296 },
297 [v_attachment] = {
298 mode = "UseAttachments",
299 },
300 [v_layer] = {
301 mode = "UseOC",
302 },
303 [v_fit] = {
304 fit = true,
305 },
306 [v_doublesided] = {
307 layout = "TwoColumnRight",
308 },
309 [v_fixed] = {
310 fixed = true,
311 },
312 [v_landscape] = {
313 duplex = "DuplexFlipShortEdge",
314 },
315 [v_portrait] = {
316 duplex = "DuplexFlipLongEdge",
317 },
318 [v_page] = {
319 duplex = "Simplex" ,
320 },
321 [v_paper] = {
322 paper = true,
323 },
324 [v_title] ={
325 title = true,
326 },
327 [v_lefttoright] ={
328 direction = "L2R",
329 },
330 [v_righttoleft] ={
331 direction = "R2L",
332 },
333 [v_nomenubar] ={
334 nomenubar = true,
335 },
336}
337
338local pagespecs = {
339
340 [v_max] = plusspecs[v_max],
341 [v_bookmark] = plusspecs[v_bookmark],
342 [v_attachment] = plusspecs[v_attachment],
343 [v_layer] = plusspecs[v_layer],
344 [v_lefttoright] = plusspecs[v_lefttoright],
345 [v_righttoleft] = plusspecs[v_righttoleft],
346 [v_title] = plusspecs[v_title],
347
348 [v_none] = {
349 },
350 [v_fit] = {
351 mode = "UseNone",
352 fit = true,
353 },
354 [v_doublesided] = {
355 mode = "UseNone",
356 layout = "TwoColumnRight",
357 fit = true,
358 },
359 [v_singlesided] = {
360 mode = "UseNone"
361 },
362 [v_default] = {
363 mode = "UseNone",
364 layout = "auto",
365 },
366 [v_auto] = {
367 mode = "UseNone",
368 layout = "auto",
369 },
370 [v_fixed] = {
371 mode = "UseNone",
372 layout = "auto",
373 fixed = true,
374 },
375 [v_landscape] = {
376 mode = "UseNone",
377 layout = "auto",
378 fixed = true,
379 duplex = "DuplexFlipShortEdge",
380 },
381 [v_portrait] = {
382 mode = "UseNone",
383 layout = "auto",
384 fixed = true,
385 duplex = "DuplexFlipLongEdge",
386 },
387 [v_page] = {
388 mode = "UseNone",
389 layout = "auto",
390 fixed = true,
391 duplex = "Simplex",
392 },
393 [v_paper] = {
394 mode = "UseNone",
395 layout = "auto",
396 fixed = true,
397 duplex = "Simplex",
398 paper = true,
399 },
400 [v_nomenubar] = {
401 mode = "UseNone",
402 layout = "auto",
403 nomenubar = true,
404 },
405}
406
407local pagespec, topoffset, leftoffset, height, width, doublesided = "default", 0, 0, 0, 0, false
408local cropoffset, bleedoffset, trimoffset, artoffset = 0, 0, 0, 0
409local marked = false
410local copies = false
411
412local getpagedimensions getpagedimensions = function()
413 getpagedimensions = backends.codeinjections.getpagedimensions
414 return getpagedimensions()
415end
416
417function codeinjections.setupcanvas(specification)
418 local paperheight = specification.paperheight
419 local paperwidth = specification.paperwidth
420 local paperdouble = specification.doublesided
421
422 paperwidth, paperheight = codeinjections.setpagedimensions(paperwidth,paperheight)
423
424 pagespec = specification.mode or pagespec
425 topoffset = specification.topoffset or 0
426 leftoffset = specification.leftoffset or 0
427 height = specification.height or paperheight
428 width = specification.width or paperwidth
429 marked = specification.print
430
431 copies = specification.copies
432 if copies and copies < 2 then
433 copies = false
434 end
435
436 cropoffset = specification.cropoffset or 0
437 trimoffset = cropoffset - (specification.trimoffset or 0)
438 bleedoffset = trimoffset - (specification.bleedoffset or 0)
439 artoffset = bleedoffset - (specification.artoffset or 0)
440
441 if paperdouble ~= nil then
442 doublesided = paperdouble
443 end
444end
445
446local function documentspecification()
447 if not pagespec or pagespec == "" then
448 pagespec = v_default
449 end
450 local settings = settings_to_array(pagespec)
451
452 local first = settings[1]
453 local defaults = pagespecs[first]
454 local spec = defaults or pagespecs[v_default]
455
456 if spec.layout == "auto" then
457 if doublesided then
458 local s = pagespecs[v_doublesided]
459 for k, v in next, s do
460 spec[k] = v
461 end
462 else
463 spec.layout = false
464 end
465 end
466
467 for i=defaults and 2 or 1,#settings do
468 local s = plusspecs[settings[i]]
469 if s then
470 for k, v in next, s do
471 spec[k] = v
472 end
473 end
474 end
475
476 local layout = spec.layout
477 local mode = spec.mode
478 local fit = spec.fit
479 local fixed = spec.fixed
480 local duplex = spec.duplex
481 local paper = spec.paper
482 local title = spec.title
483 local direction = spec.direction
484 local nomenubar = spec.nomenubar
485 if layout then
486 addtocatalog("PageLayout",pdfconstant(layout))
487 end
488 if mode then
489 addtocatalog("PageMode",pdfconstant(mode))
490 end
491 local prints = nil
492 if marked then
493 local pages = structures.pages
494 local marked = pages.allmarked(marked)
495 local nofmarked = marked and #marked or 0
496 if nofmarked > 0 then
497
498
499 for i=1,#marked do marked[i] = marked[i] - 1 end
500 prints = pdfarray(flattened(pages.toranges(marked)))
501 end
502 end
503 if fit or fixed or duplex or copies or paper or prints or title or direction or nomenubar then
504 addtocatalog("ViewerPreferences",pdfdictionary {
505 FitWindow = fit and true or nil,
506 PrintScaling = fixed and pdfconstant("None") or nil,
507 Duplex = duplex and pdfconstant(duplex) or nil,
508 NumCopies = copies and copies or nil,
509 PickTrayByPDFSize = paper and true or nil,
510 PrintPageRange = prints or nil,
511 DisplayDocTitle = title and true or nil,
512 Direction = direction and pdfconstant(direction) or nil,
513 HideMenubar = nomenubar and true or nil,
514 })
515 end
516
517 addtocatalog("Version", pdfconstant(format("1.%s",pdfminorversion())))
518 addtocatalog("Lang", pdfstring(tokens.getters.macro("currentmainlanguage")))
519end
520
521
522
523local factor = number.dimenfactors.bp
524local f_value = formatters["%.6N"]
525
526local function boxvalue(n)
527 return pdfverbose(f_value(factor * n))
528end
529
530local function pagespecification()
531 local paperwidth, paperheight = codeinjections.getpagedimensions()
532 local llx = leftoffset
533 local lly = paperheight + topoffset - height
534 local urx = width - leftoffset
535 local ury = paperheight - topoffset
536
537 local function extrabox(WhatBox,offset,always)
538 if offset ~= 0 or always then
539 addtopageattributes(WhatBox, pdfarray {
540 boxvalue(llx + offset),
541 boxvalue(lly + offset),
542 boxvalue(urx - offset),
543 boxvalue(ury - offset),
544 })
545 end
546 end
547 if omitextraboxes then
548
549 else
550 extrabox("CropBox",cropoffset,true)
551 extrabox("TrimBox",trimoffset,true)
552 extrabox("BleedBox",bleedoffset)
553
554 end
555end
556
557lpdf.registerpagefinalizer(pagespecification,"page specification")
558lpdf.registerdocumentfinalizer(documentspecification,"document specification")
559
560
561
562
563
564
565
566
567local map = {
568 numbers = "D",
569 Romannumerals = "R",
570 romannumerals = "r",
571 Characters = "A",
572 characters = "a",
573}
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605local function featurecreep()
606 local pages = structures.pages.tobesaved
607 local list = pdfarray()
608 local getset = structures.sets.get
609 local stopped = false
610 local oldlabel = nil
611 local olconversion = nil
612 for i=1,#pages do
613 local p = pages[i]
614 if not p then
615 return
616 end
617 local label = p.viewerprefix or ""
618 if p.status == v_stop then
619 if not stopped then
620 list[#list+1] = i - 1
621 list[#list+1] = pdfdictionary {
622 P = pdfunicode(label),
623 }
624 stopped = true
625 end
626 oldlabel = nil
627 oldconversion = nil
628 stopped = false
629 else
630 local numberdata = p.numberdata
631 local conversion = nil
632 local number = p.number
633 if numberdata then
634 local conversionset = numberdata.conversionset
635 if conversionset then
636 conversion = getset("structure:conversions",p.block,conversionset,1,"numbers")
637 end
638 end
639 conversion = conversion and map[conversion] or map.numbers
640 if number == 1 or oldlabel ~= label or oldconversion ~= conversion then
641 list[#list+1] = i - 1
642 list[#list+1] = pdfdictionary {
643 S = pdfconstant(conversion),
644 St = number,
645 P = label ~= "" and pdfunicode(label) or nil,
646 }
647 end
648 oldlabel = label
649 oldconversion = conversion
650 stopped = false
651 end
652 end
653 addtocatalog("PageLabels", pdfdictionary { Nums = list })
654end
655
656lpdf.registerdocumentfinalizer(featurecreep,"featurecreep")
657 |