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 addtoinfo("ConTeXt.Url","www.pragma-ade.com")
250 addtoinfo("ConTeXt.Support","contextgarden.net")
251 addtoinfo("TeX.Support","tug.org")
252
253 done = true
254 else
255
256 end
257end
258
259lpdf.registerpagefinalizer(setupidentity,"identity")
260
261
262
263
264
265local function flushjavascripts()
266 local t = interactions.javascripts.flushpreambles()
267 if #t > 0 then
268 local a = pdfarray()
269 local pdf_javascript = pdfconstant("JavaScript")
270 for i=1,#t do
271 local ti = t[i]
272 local name = ti[1]
273 local script = ti[2]
274 local j = pdfdictionary {
275 S = pdf_javascript,
276 JS = pdfreference(pdfflushstreamobject(script)),
277 }
278 a[#a+1] = pdfstring(name)
279 a[#a+1] = pdfreference(pdfflushobject(j))
280 end
281 addtonames("JavaScript",pdfreference(pdfflushobject(pdfdictionary{ Names = a })))
282 end
283end
284
285lpdf.registerdocumentfinalizer(flushjavascripts,"javascripts")
286
287
288
289local plusspecs = {
290 [v_max] = {
291 mode = "FullScreen",
292 },
293 [v_bookmark] = {
294 mode = "UseOutlines",
295 },
296 [v_attachment] = {
297 mode = "UseAttachments",
298 },
299 [v_layer] = {
300 mode = "UseOC",
301 },
302 [v_fit] = {
303 fit = true,
304 },
305 [v_doublesided] = {
306 layout = "TwoColumnRight",
307 },
308 [v_fixed] = {
309 fixed = true,
310 },
311 [v_landscape] = {
312 duplex = "DuplexFlipShortEdge",
313 },
314 [v_portrait] = {
315 duplex = "DuplexFlipLongEdge",
316 },
317 [v_page] = {
318 duplex = "Simplex" ,
319 },
320 [v_paper] = {
321 paper = true,
322 },
323 [v_title] ={
324 title = true,
325 },
326 [v_lefttoright] ={
327 direction = "L2R",
328 },
329 [v_righttoleft] ={
330 direction = "R2L",
331 },
332 [v_nomenubar] ={
333 nomenubar = true,
334 },
335}
336
337local pagespecs = {
338
339 [v_max] = plusspecs[v_max],
340 [v_bookmark] = plusspecs[v_bookmark],
341 [v_attachment] = plusspecs[v_attachment],
342 [v_layer] = plusspecs[v_layer],
343 [v_lefttoright] = plusspecs[v_lefttoright],
344 [v_righttoleft] = plusspecs[v_righttoleft],
345 [v_title] = plusspecs[v_title],
346
347 [v_none] = {
348 },
349 [v_fit] = {
350 mode = "UseNone",
351 fit = true,
352 },
353 [v_doublesided] = {
354 mode = "UseNone",
355 layout = "TwoColumnRight",
356 fit = true,
357 },
358 [v_singlesided] = {
359 mode = "UseNone"
360 },
361 [v_default] = {
362 mode = "UseNone",
363 layout = "auto",
364 },
365 [v_auto] = {
366 mode = "UseNone",
367 layout = "auto",
368 },
369 [v_fixed] = {
370 mode = "UseNone",
371 layout = "auto",
372 fixed = true,
373 },
374 [v_landscape] = {
375 mode = "UseNone",
376 layout = "auto",
377 fixed = true,
378 duplex = "DuplexFlipShortEdge",
379 },
380 [v_portrait] = {
381 mode = "UseNone",
382 layout = "auto",
383 fixed = true,
384 duplex = "DuplexFlipLongEdge",
385 },
386 [v_page] = {
387 mode = "UseNone",
388 layout = "auto",
389 fixed = true,
390 duplex = "Simplex",
391 },
392 [v_paper] = {
393 mode = "UseNone",
394 layout = "auto",
395 fixed = true,
396 duplex = "Simplex",
397 paper = true,
398 },
399 [v_nomenubar] = {
400 mode = "UseNone",
401 layout = "auto",
402 nomenubar = true,
403 },
404}
405
406local pagespec, topoffset, leftoffset, height, width, doublesided = "default", 0, 0, 0, 0, false
407local cropoffset, bleedoffset, trimoffset, artoffset = 0, 0, 0, 0
408local marked = false
409local copies = false
410
411local getpagedimensions getpagedimensions = function()
412 getpagedimensions = backends.codeinjections.getpagedimensions
413 return getpagedimensions()
414end
415
416function codeinjections.setupcanvas(specification)
417 local paperheight = specification.paperheight
418 local paperwidth = specification.paperwidth
419 local paperdouble = specification.doublesided
420
421 paperwidth, paperheight = codeinjections.setpagedimensions(paperwidth,paperheight)
422
423 pagespec = specification.mode or pagespec
424 topoffset = specification.topoffset or 0
425 leftoffset = specification.leftoffset or 0
426 height = specification.height or paperheight
427 width = specification.width or paperwidth
428 marked = specification.print
429
430 copies = specification.copies
431 if copies and copies < 2 then
432 copies = false
433 end
434
435 cropoffset = specification.cropoffset or 0
436 trimoffset = cropoffset - (specification.trimoffset or 0)
437 bleedoffset = trimoffset - (specification.bleedoffset or 0)
438 artoffset = bleedoffset - (specification.artoffset or 0)
439
440 if paperdouble ~= nil then
441 doublesided = paperdouble
442 end
443end
444
445local function documentspecification()
446 if not pagespec or pagespec == "" then
447 pagespec = v_default
448 end
449 local settings = settings_to_array(pagespec)
450
451 local first = settings[1]
452 local defaults = pagespecs[first]
453 local spec = defaults or pagespecs[v_default]
454
455 if spec.layout == "auto" then
456 if doublesided then
457 local s = pagespecs[v_doublesided]
458 for k, v in next, s do
459 spec[k] = v
460 end
461 else
462 spec.layout = false
463 end
464 end
465
466 for i=defaults and 2 or 1,#settings do
467 local s = plusspecs[settings[i]]
468 if s then
469 for k, v in next, s do
470 spec[k] = v
471 end
472 end
473 end
474
475 local layout = spec.layout
476 local mode = spec.mode
477 local fit = spec.fit
478 local fixed = spec.fixed
479 local duplex = spec.duplex
480 local paper = spec.paper
481 local title = spec.title
482 local direction = spec.direction
483 local nomenubar = spec.nomenubar
484 if layout then
485 addtocatalog("PageLayout",pdfconstant(layout))
486 end
487 if mode then
488 addtocatalog("PageMode",pdfconstant(mode))
489 end
490 local prints = nil
491 if marked then
492 local pages = structures.pages
493 local marked = pages.allmarked(marked)
494 local nofmarked = marked and #marked or 0
495 if nofmarked > 0 then
496
497
498 for i=1,#marked do marked[i] = marked[i] - 1 end
499 prints = pdfarray(flattened(pages.toranges(marked)))
500 end
501 end
502 if fit or fixed or duplex or copies or paper or prints or title or direction or nomenubar then
503 addtocatalog("ViewerPreferences",pdfdictionary {
504 FitWindow = fit and true or nil,
505 PrintScaling = fixed and pdfconstant("None") or nil,
506 Duplex = duplex and pdfconstant(duplex) or nil,
507 NumCopies = copies and copies or nil,
508 PickTrayByPDFSize = paper and true or nil,
509 PrintPageRange = prints or nil,
510 DisplayDocTitle = title and true or nil,
511 Direction = direction and pdfconstant(direction) or nil,
512 HideMenubar = nomenubar and true or nil,
513 })
514 end
515 addtoinfo ("Trapped", pdfconstant("False"))
516 addtocatalog("Version", pdfconstant(format("1.%s",pdfminorversion())))
517 addtocatalog("Lang", pdfstring(tokens.getters.macro("currentmainlanguage")))
518end
519
520
521
522local factor = number.dimenfactors.bp
523local f_value = formatters["%.6N"]
524
525local function boxvalue(n)
526 return pdfverbose(f_value(factor * n))
527end
528
529local function pagespecification()
530 local paperwidth, paperheight = codeinjections.getpagedimensions()
531 local llx = leftoffset
532 local lly = paperheight + topoffset - height
533 local urx = width - leftoffset
534 local ury = paperheight - topoffset
535
536 local function extrabox(WhatBox,offset,always)
537 if offset ~= 0 or always then
538 addtopageattributes(WhatBox, pdfarray {
539 boxvalue(llx + offset),
540 boxvalue(lly + offset),
541 boxvalue(urx - offset),
542 boxvalue(ury - offset),
543 })
544 end
545 end
546 if omitextraboxes then
547
548 else
549 extrabox("CropBox",cropoffset,true)
550 extrabox("TrimBox",trimoffset,true)
551 extrabox("BleedBox",bleedoffset)
552
553 end
554end
555
556lpdf.registerpagefinalizer(pagespecification,"page specification")
557lpdf.registerdocumentfinalizer(documentspecification,"document specification")
558
559
560
561
562
563
564
565
566local map = {
567 numbers = "D",
568 Romannumerals = "R",
569 romannumerals = "r",
570 Characters = "A",
571 characters = "a",
572}
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
604local function featurecreep()
605 local pages = structures.pages.tobesaved
606 local list = pdfarray()
607 local getset = structures.sets.get
608 local stopped = false
609 local oldlabel = nil
610 local olconversion = nil
611 for i=1,#pages do
612 local p = pages[i]
613 if not p then
614 return
615 end
616 local label = p.viewerprefix or ""
617 if p.status == v_stop then
618 if not stopped then
619 list[#list+1] = i - 1
620 list[#list+1] = pdfdictionary {
621 P = pdfunicode(label),
622 }
623 stopped = true
624 end
625 oldlabel = nil
626 oldconversion = nil
627 stopped = false
628 else
629 local numberdata = p.numberdata
630 local conversion = nil
631 local number = p.number
632 if numberdata then
633 local conversionset = numberdata.conversionset
634 if conversionset then
635 conversion = getset("structure:conversions",p.block,conversionset,1,"numbers")
636 end
637 end
638 conversion = conversion and map[conversion] or map.numbers
639 if number == 1 or oldlabel ~= label or oldconversion ~= conversion then
640 list[#list+1] = i - 1
641 list[#list+1] = pdfdictionary {
642 S = pdfconstant(conversion),
643 St = number,
644 P = label ~= "" and pdfunicode(label) or nil,
645 }
646 end
647 oldlabel = label
648 oldconversion = conversion
649 stopped = false
650 end
651 end
652 addtocatalog("PageLabels", pdfdictionary { Nums = list })
653end
654
655lpdf.registerdocumentfinalizer(featurecreep,"featurecreep")
656 |