1if not modules then modules = { } end modules ['lpdf-epa'] = {
2 version = 1.001,
3 comment = "companion to lpdf-epa.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
13local type, tonumber, next = type, tonumber, next
14local format, gsub, lower, find, match = string.format, string.gsub, string.lower, string.find, string.match
15local formatters = string.formatters
16local concat, merged = table.concat, table.merged
17local abs = math.abs
18local expandname = file.expandname
19local allocate = utilities.storage.allocate
20local isfile = lfs.isfile
21
22local trace_links = false trackers.register("figures.links", function(v) trace_links = v end)
23local trace_comments = false trackers.register("figures.comments", function(v) trace_comments = v end)
24local trace_fields = false trackers.register("figures.fields", function(v) trace_fields = v end)
25local trace_outlines = false trackers.register("figures.outlines", function(v) trace_outlines = v end)
26
27local report_link = logs.reporter("backend","link")
28local report_comment = logs.reporter("backend","comment")
29local report_field = logs.reporter("backend","field")
30local report_outline = logs.reporter("backend","outline")
31
32local context = context
33
34local pdfbackend = backends.registered.pdf
35local nodeinjections = pdfbackend.nodeinjections
36local codeinjections = pdfbackend.codeinjections
37
38
39local setmacro = tokens.setters.macro
40
41local lpdf = lpdf
42local pdfarray = lpdf.array
43local pdfdictionary = lpdf.dictionary
44local pdfconstant = lpdf.constant
45local pdfreference = lpdf.reference
46local pdfreserveobject = lpdf.reserveobject
47local pdfgetpos = lpdf.getpos
48local pdfcopyboolean = lpdf.copyboolean
49local pdfcopyunicode = lpdf.copyunicode
50local pdfcopyarray = lpdf.copyarray
51local pdfcopydictionary = lpdf.copydictionary
52local pdfcopynumber = lpdf.copynumber
53local pdfcopyinteger = lpdf.copyinteger
54local pdfcopystring = lpdf.copystring
55local pdfcopyconstant = lpdf.copyconstant
56
57local createimage = images.create
58local embedimage = images.embed
59
60local hpack_node = nodes.hpack
61
62local loadpdffile = lpdf.epdf.load
63
64local nameonly = file.nameonly
65
66local variables = interfaces.variables
67
68
69local escapetex = characters.filters.utf.private.escape
70
71local bookmarks = structures.bookmarks
72
73local maxdimen <const> = tex.magicconstants.maxdimen
74local bpfactor <const> = number.dimenfactors.bp
75
76local layerspec = {
77 "epdfcontent"
78}
79
80local getpos = function() getpos = codeinjections.getpos return getpos() end
81
82local collected = allocate()
83local tobesaved = allocate()
84
85local jobembedded = {
86 collected = collected,
87 tobesaved = tobesaved,
88}
89
90job.embedded = jobembedded
91
92local function initializer()
93 tobesaved = jobembedded.tobesaved
94 collected = jobembedded.collected
95end
96
97job.register('job.embedded.collected',tobesaved,initializer)
98
99local function validdocument(specification)
100 if figures and not specification then
101 specification = figures and figures.current()
102 specification = specification and specification.status
103 end
104 if specification then
105 local fullname = specification.fullname
106 local expanded = lower(expandname(fullname))
107
108 tobesaved[expanded] = true
109
110 return specification, fullname, loadpdffile(fullname)
111 end
112end
113
114local function getmediasize(specification,pagedata)
115 local xscale = specification.xscale or 1
116 local yscale = specification.yscale or 1
117
118 local mediabox = pagedata.MediaBox
119 local llx = mediabox[1]
120 local lly = mediabox[2]
121 local urx = mediabox[3]
122 local ury = mediabox[4]
123 local width = xscale * (urx - llx)
124 local height = yscale * (ury - lly)
125 return llx, lly, urx, ury, width, height, xscale, yscale
126end
127
128local function getdimensions(annotation,llx,lly,xscale,yscale,width,height,report,minheight)
129 local rectangle = annotation.Rect
130 local a_llx = rectangle[1]
131 local a_lly = rectangle[2]
132 local a_urx = rectangle[3]
133 local a_ury = rectangle[4]
134 local x = xscale * (a_llx - llx)
135 local y = yscale * (a_lly - lly)
136 local w = xscale * (a_urx - a_llx)
137 local h = yscale * (a_ury - a_lly)
138 if w > width or h > height or w < 0 or h < 0 or abs(x) > (maxdimen/2) or abs(y) > (maxdimen/2) then
139 report("broken rectangle [%.6F %.6F %.6F %.6F] (max: %.6F)",a_llx,a_lly,a_urx,a_ury,maxdimen/2)
140 return
141 end
142 if minheight and minheight ~= 0 then
143 local dh = minheight - h
144 if dh > 0 then
145 y = y - dh/2
146 h = h + dh
147 end
148 end
149 return x, y, w, h, a_llx, a_lly, a_urx, a_ury
150end
151
152local layerused = false
153
154
155
156
157
158
159
160
161local function initializelayer(height,width)
162
163 context.setuplayer(layerspec, { height = height .. "bp", width = width .. "bp" })
164 layerused = true
165
166end
167
168function codeinjections.flushmergelayer()
169 if layerused then
170 context.flushlayer(layerspec)
171 layerused = false
172 end
173end
174
175local f_namespace = formatters["lpdf-epa-%s-"]
176
177local function makenamespace(filename)
178 filename = gsub(lower(nameonly(filename)),"[^%a%d]+","-")
179 return f_namespace(filename)
180end
181
182local function add_link(x,y,w,h,destination,what)
183 x = x .. "bp"
184 y = y .. "bp"
185 w = w .. "bp"
186 h = h .. "bp"
187 if trace_links then
188 report_link("destination %a, type %a, dx %s, dy %s, wd %s, ht %s",destination,what,x,y,w,h)
189 end
190 local locationspec = {
191 x = x,
192 y = y,
193 preset = "leftbottom",
194 }
195 local buttonspec = {
196 width = w,
197 height = h,
198 offset = variables.overlay,
199 frame = trace_links and variables.on or variables.off,
200 }
201 context.setlayer (
202 layerspec,
203 locationspec,
204 function() context.button ( buttonspec, "", { destination } ) end
205
206 )
207end
208
209local function link_goto(x,y,w,h,document,annotation,pagedata,namespace,pageoffset)
210 local a = annotation.A
211 if a then
212 local destination = a.D
213 local what = "page"
214 if type(destination) == "string" then
215 local destinations = document.destinations
216 local wanted = destinations[destination]
217 destination = wanted and wanted.D
218 if destination then what = "named" end
219 end
220 local pagedata = destination and destination[1]
221 if pagedata then
222 local destinationpage = pagedata.number
223 if destinationpage then
224 if pageoffset then
225 destinationpage = "page(" .. (destinationpage+pageoffset) .. ")"
226 else
227 destinationpage = namespace .. destinationpage
228 end
229 add_link(x,y,w,h,destinationpage,what)
230 end
231 end
232 end
233end
234
235local function link_uri(x,y,w,h,document,annotation)
236 local url = annotation.A.URI
237 if url then
238
239
240 url = escapetex(url)
241 add_link(x,y,w,h,formatters["url(%s)"](url),"url")
242 end
243end
244
245
246
247
248
249
250
251
252
253
254
255
256local embedded = false directives.register("figures.embedded", function(v) embedded = v end)
257local reported = { }
258
259local function link_file(x,y,w,h,document,annotation)
260 local a = annotation.A
261 if a then
262 local filename = a.F
263 if type(filename) == "table" then
264 filename = filename.F
265 end
266 if filename then
267 filename = escapetex(filename)
268 local destination = a.D
269 if not destination then
270 add_link(x,y,w,h,formatters["file(%s)"](filename),"file")
271 elseif type(destination) == "string" then
272 add_link(x,y,w,h,formatters["%s::%s"](filename,destination),"file (named)")
273 else
274
275 destination = tonumber(destination[1])
276 if destination then
277 destination = destination + 1
278 local loaded = collected[lower(expandname(filename))]
279 if embedded and loaded then
280 add_link(x,y,w,h,makenamespace(filename) .. destination,what)
281 else
282 if loaded and not reported[filename] then
283 report_link("reference to an also loaded file %a, consider using directive: figures.embedded",filename)
284 reported[filename] = true
285 end
286 add_link(x,y,w,h,formatters["%s::page(%s)"](filename,destination),"file (page)")
287 end
288 else
289 add_link(x,y,w,h,formatters["file(%s)"](filename),"file")
290 end
291 end
292 end
293 end
294end
295
296
297
298
299
300local function getpageoffset(options)
301 if options then
302 for k in next, options do
303 local p = tonumber(match(k,"pageoffset:([%+%-]*%d+)"))
304 if p then
305 return p
306 end
307 end
308 end
309end
310
311local function getminheight(options)
312 if options then
313 for k in next, options do
314 local p = tonumber(match(k,"minheight:([%+%-]*%d+)"))
315 if p then
316 return p
317 end
318 end
319 end
320end
321
322function codeinjections.mergereferences(specification,options)
323 local specification, fullname, document = validdocument(specification)
324 if not document then
325 return ""
326 end
327 local pagenumber = specification.page or 1
328 local pagedata = document.pages[pagenumber]
329 local annotations = pagedata and pagedata.Annots
330 local namespace = makenamespace(fullname)
331 local reference = namespace .. pagenumber
332 local pageoffset = getpageoffset(options)
333 local minheight = getminheight(options)
334 if annotations and #annotations > 0 then
335 local llx, lly, urx, ury, width, height, xscale, yscale = getmediasize(specification,pagedata,xscale,yscale)
336 initializelayer(height,width)
337 for i=1,#annotations do
338 local annotation = annotations[i]
339 if annotation then
340 if annotation.Subtype == "Link" then
341 local a = annotation.A
342 if not a then
343 local d = annotation.Dest
344 if d then
345 a = { S = "GoTo", D = d }
346 annotation.A = a
347 end
348 end
349 if not a then
350 report_link("missing link annotation")
351 else
352 local x, y, w, h = getdimensions(annotation,llx,lly,xscale,yscale,width,height,report_link,minheight)
353 if x then
354 local linktype = a.S
355 if linktype == "GoTo" then
356 link_goto(x,y,w,h,document,annotation,pagedata,namespace,pageoffset)
357 elseif linktype == "GoToR" then
358 link_file(x,y,w,h,document,annotation)
359 elseif linktype == "URI" then
360 link_uri(x,y,w,h,document,annotation)
361 elseif trace_links then
362 report_link("unsupported link annotation %a",linktype)
363 end
364 end
365 end
366 end
367 elseif trace_links then
368 report_link("broken annotation, index %a",i)
369 end
370 end
371 end
372
373 setmacro("mergedfigurereference",reference,"global")
374 if trace_links then
375 report_link("setting figure reference to %a",reference)
376 end
377 specification.reference = reference
378 return namespace
379end
380
381function codeinjections.mergeviewerlayers(specification)
382
383 if true then
384 return
385 end
386 local specification, fullname, document = validdocument(specification)
387 if not document then
388 return ""
389 end
390 local namespace = makenamespace(fullname)
391 local layers = document.layers
392 if layers then
393 for i=1,#layers do
394 local layer = layers[i]
395 if layer then
396 local tag = namespace .. gsub(layer," ",":")
397 local title = tag
398 if trace_links then
399 report_link("using layer %a",tag)
400 end
401 attributes.viewerlayers.define {
402 tag = tag,
403 title = title,
404 visible = variables.start,
405 editable = variables.yes,
406 printable = variables.yes,
407 }
408 codeinjections.useviewerlayer(tag)
409 elseif trace_links then
410 report_link("broken layer, index %a",i)
411 end
412 end
413 end
414end
415
416
417
418
419
420
421
422
423
424
425
426local commentlike = {
427 Text = "text",
428 FreeText = "freetext",
429 Line = "line",
430 Square = "shape",
431 Circle = "shape",
432 Polygon = "poly",
433 PolyLine = "poly",
434 Highlight = "markup",
435 Underline = "markup",
436 Squiggly = "markup",
437 StrikeOut = "markup",
438 Caret = "text",
439 Stamp = "stamp",
440 Ink = "ink",
441 Popup = "popup",
442}
443
444local function copyBS(v)
445 if v then
446
447
448
449
450
451
452 return copypdfdictionary(v)
453 end
454end
455
456local function copyBE(v)
457 if v then
458
459
460
461
462 return copypdfdictionary(v)
463 end
464end
465
466local function copyBorder(v)
467 if v then
468
469 return copypdfarray(v)
470 end
471end
472
473local function copyPopup(v,references)
474 if v then
475 local p = references[v]
476 if p then
477 return pdfreference(p)
478 end
479 end
480end
481
482local function copyParent(v,references)
483 if v then
484 local p = references[v]
485 if p then
486 return pdfreference(p)
487 end
488 end
489end
490
491local function copyIRT(v,references)
492 if v then
493 local p = references[v]
494 if p then
495 return pdfreference(p)
496 end
497 end
498end
499
500local function copyC(v)
501 if v then
502
503 return pdfcopyarray(v)
504 end
505end
506
507local function finalizer(d,xscale,yscale,a_llx,a_ury)
508 local q = d.QuadPoints or d.Vertices or d.CL
509 if q then
510 return function()
511 local h, v = pdfgetpos()
512 for i=1,#q,2 do
513 q[i] = xscale * q[i] + (h*bpfactor - xscale * a_llx)
514 q[i+1] = yscale * q[i+1] + (v*bpfactor - yscale * a_ury)
515 end
516 return d()
517 end
518 end
519 q = d.InkList or d.Path
520 if q then
521 return function()
522 local h, v = pdfgetpos()
523 for i=1,#q do
524 local q = q[i]
525 for i=1,#q,2 do
526 q[i] = xscale * q[i] + (h*bpfactor - xscale * a_llx)
527 q[i+1] = yscale * q[i+1] + (v*bpfactor - yscale * a_ury)
528 end
529 end
530 return d()
531 end
532 end
533 return d()
534end
535
536local validstamps = {
537 Approved = true,
538 Experimental = true,
539 NotApproved = true,
540 AsIs = true,
541 Expired = true,
542 NotForPublicRelease = true,
543 Confidential = true,
544 Final = true,
545 Sold = true,
546 Departmental = true,
547 ForComment = true,
548 TopSecret = true,
549 Draft = true,
550 ForPublicRelease = true,
551}
552
553
554
555local function validStamp(v)
556 local name = "Stamped"
557 if v then
558 local ok = validstamps[v]
559 if ok then
560 name = ok
561 else
562 for k in next, validstamps do
563 if find(v,k.."$") then
564 name = k
565 validstamps[v] = k
566 break
567 end
568 end
569 end
570 end
571
572 context.predefinesymbol { name }
573 context.step()
574
575 return pdfconstant(name), codeinjections.analyzenormalsymbol(name)
576end
577
578local annotationflags = lpdf.flags.annotations
579
580local function copyF(v,lock)
581 if lock then
582 v = (v or 0) | (annotationflags.ReadOnly + annotationflags.Locked + annotationflags.LockedContents)
583 end
584 if v then
585 return pdfcopyinteger(v)
586 end
587end
588
589
590
591
592function codeinjections.mergecomments(specification)
593 local specification, fullname, document = validdocument(specification)
594 if not document then
595 return ""
596 end
597 local pagenumber = specification.page or 1
598 local pagedata = document.pages[pagenumber]
599 local annotations = pagedata and pagedata.Annots
600 if annotations and #annotations > 0 then
601 local llx, lly, urx, ury, width, height, xscale, yscale = getmediasize(specification,pagedata,xscale,yscale)
602 initializelayer(height,width)
603
604 local lockflags = specification.lock
605 local references = { }
606 local usedpopups = { }
607 for i=1,#annotations do
608 local annotation = annotations[i]
609 if annotation then
610 local subtype = annotation.Subtype
611 if commentlike[subtype] then
612 references[annotation] = pdfreserveobject()
613 local p = annotation.Popup
614 if p then
615 usedpopups[p] = true
616 end
617 end
618 end
619 end
620
621 for i=1,#annotations do
622
623 local annotation = annotations[i]
624 if annotation then
625 local reference = references[annotation]
626 if reference then
627 local subtype = annotation.Subtype
628 local kind = commentlike[subtype]
629 if kind ~= "popup" or usedpopups[annotation] then
630 local x, y, w, h, a_llx, a_lly, a_urx, a_ury = getdimensions(annotation,llx,lly,xscale,yscale,width,height,report_comment)
631 if x then
632 local voffset = h
633 local dictionary = pdfdictionary {
634 Subtype = pdfconstant (subtype),
635
636 Contents = pdfcopyunicode(annotation.Contents),
637 NM = pdfcopystring (annotation.NM),
638 M = pdfcopystring (annotation.M),
639 F = copyF (annotation.F,lockflags),
640 C = copyC (annotation.C),
641 ca = pdfcopynumber (annotation.ca),
642 CA = pdfcopynumber (annotation.CA),
643 Lang = pdfcopystring (annotation.Lang),
644
645 CreationDate = pdfcopystring (annotation.CreationDate),
646 T = pdfcopyunicode(annotation.T),
647 Subj = pdfcopyunicode(annotation.Subj),
648
649 Border = pdfcopyarray (annotation.Border),
650 BS = copyBS (annotation.BS),
651 BE = copyBE (annotation.BE),
652
653 Popup = copyPopup (annotation.Popup,references),
654 RC = pdfcopyunicode(annotation.RC)
655 }
656 if kind == "markup" then
657 dictionary.IRT = copyIRT (annotation.IRT,references)
658 dictionary.RT = pdfconstant (annotation.RT)
659 dictionary.IT = pdfcopyconstant (annotation.IT)
660 dictionary.QuadPoints = pdfcopyarray (annotation.QuadPoints)
661
662 elseif kind == "text" then
663
664 dictionary.F = nil
665 dictionary.Open = pdfcopyboolean (annotation.Open)
666 dictionary.Name = pdfcopyunicode (annotation.Name)
667 dictionary.State = pdfcopystring (annotation.State)
668 dictionary.StateModel = pdfcopystring (annotation.StateModel)
669 dictionary.IT = pdfcopyconstant (annotation.IT)
670 dictionary.QuadPoints = pdfcopyarray (annotation.QuadPoints)
671 dictionary.RD = pdfcopyarray (annotation.RD)
672 dictionary.Sy = pdfcopyconstant (annotation.Sy)
673 voffset = 0
674 elseif kind == "freetext" then
675 dictionary.DA = pdfcopystring (annotation.DA)
676 dictionary.Q = pdfcopyinteger (annotation.Q)
677 dictionary.DS = pdfcopystring (annotation.DS)
678 dictionary.CL = pdfcopyarray (annotation.CL)
679 dictionary.IT = pdfcopyconstant (annotation.IT)
680 dictionary.LE = pdfcopyconstant (annotation.LE)
681
682 elseif kind == "line" then
683 dictionary.LE = pdfcopyarray (annotation.LE)
684 dictionary.IC = pdfcopyarray (annotation.IC)
685 dictionary.LL = pdfcopynumber (annotation.LL)
686 dictionary.LLE = pdfcopynumber (annotation.LLE)
687 dictionary.Cap = pdfcopyboolean (annotation.Cap)
688 dictionary.IT = pdfcopyconstant (annotation.IT)
689 dictionary.LLO = pdfcopynumber (annotation.LLO)
690 dictionary.CP = pdfcopyconstant (annotation.CP)
691 dictionary.Measure = pdfcopydictionary(annotation.Measure)
692 dictionary.CO = pdfcopyarray (annotation.CO)
693 voffset = 0
694 elseif kind == "shape" then
695 dictionary.IC = pdfcopyarray (annotation.IC)
696
697 voffset = 0
698 elseif kind == "stamp" then
699 local name, appearance = validStamp(annotation.Name)
700 dictionary.Name = name
701 dictionary.AP = appearance
702 voffset = 0
703 elseif kind == "ink" then
704 dictionary.InkList = pdfcopyarray (annotation.InkList)
705 elseif kind == "poly" then
706 dictionary.Vertices = pdfcopyarray (annotation.Vertices)
707
708 dictionary.IC = pdfcopyarray (annotation.IC)
709 dictionary.IT = pdfcopyconstant (annotation.IT)
710 dictionary.Measure = pdfcopydictionary(annotation.Measure)
711 dictionary.Path = pdfcopyarray (annotation.Path)
712
713 elseif kind == "popup" then
714 dictionary.Open = pdfcopyboolean (annotation.Open)
715 dictionary.Parent = copyParent (annotation.Parent,references)
716 voffset = 0
717 end
718 if dictionary then
719 local locationspec = {
720 x = x .. "bp",
721 y = y .. "bp",
722 voffset = voffset .. "bp",
723 preset = "leftbottom",
724 }
725 local finalize = finalizer(dictionary,xscale,yscale,a_llx,a_ury)
726 context.setlayer(layerspec,locationspec,function()
727 context(hpack_node(nodeinjections.annotation(w/bpfactor,h/bpfactor,0,finalize,reference)))
728 end)
729 end
730 end
731 else
732
733 end
734 end
735 elseif trace_comments then
736 report_comment("broken annotation, index %a",i)
737 end
738 end
739 end
740 return namespace
741end
742
743local widgetflags = lpdf.flags.widgets
744
745local function flagstoset(flag,flags)
746 local t = { }
747 if flags then
748 for k, v in next, flags do
749 if (flag & v) ~= 0 then
750 t[k] = true
751 end
752 end
753 end
754 return t
755end
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776function codeinjections.mergefields(specification,dataonly)
777 local specification, fullname, document = validdocument(specification)
778 if not document then
779 return ""
780 end
781 local pagenumber = specification.page or 1
782 local pagedata = document.pages[pagenumber]
783 local annotations = pagedata and pagedata.Annots
784 if annotations and #annotations > 0 then
785 local llx, lly, urx, ury, width, height, xscale, yscale = getmediasize(specification,pagedata,xscale,yscale)
786 initializelayer(height,width)
787
788 for i=1,#annotations do
789
790 local annotation = annotations[i]
791 if annotation then
792 local subtype = annotation.Subtype
793 if subtype == "Widget" then
794 local parent = annotation.Parent or { }
795 local name = annotation.T or parent.T
796 local what = annotation.FT or parent.FT
797 if name and what then
798 local x, y, w, h, a_llx, a_lly, a_urx, a_ury = getdimensions(annotation,llx,lly,xscale,yscale,width,height,report_field)
799 if x then
800 x = x .. "bp"
801 y = y .. "bp"
802 local W, H = w, h
803 w = w .. "bp"
804 h = h .. "bp"
805 if trace_fields then
806 report_field("field %a, type %a, dx %s, dy %s, wd %s, ht %s",name,what,x,y,w,h)
807 end
808 local locationspec = {
809 x = x,
810 y = y,
811 preset = "leftbottom",
812 }
813
814 local aflags = flagstoset(annotation.F or parent.F or 0, annotationflags)
815 local wflags = flagstoset(annotation.Ff or parent.Ff or 0, widgetflags)
816 if what == "Tx" then
817
818 if wflags.MultiLine then
819 wflags.MultiLine = nil
820 what = "text"
821 else
822 what = "line"
823 end
824
825
826 local frame = trace_links and variables.on or variables.off
827 local default = annotation.V or ""
828 if default == "" then
829 local parent = annotation.Parent
830 if parent then
831 default = parent.V or ""
832 end
833 end
834 if dataonly then
835 if type(default) == "string" and default ~= "" then
836 local data = lpdf.epdf.parsecontent(tostring(annotation.DA or "") or "") or { }
837 local font = "Normal"
838 local size = "10bp"
839 for i=1,#data do
840 local d = data[i]
841 if d[3] == "Tf" then
842 font = d[1][2]
843 size = d[2][2] .. "bp"
844 break
845 end
846 end
847 local fieldspec = {
848 width = w,
849 height = h,
850 frame = frame,
851
852 ["epdf:font"] = font,
853 ["epdf:size"] = size,
854 }
855report_field("field %a, type %a, data %a",name,what,default)
856 context.setlayer (layerspec,locationspec,function()
857 context["epdf"..what.."field"](fieldspec,default)
858 end)
859 end
860 else
861 local fieldspec = {
862 width = w,
863 height = h,
864 offset = variables.overlay,
865 frame = frame,
866 n = annotation.MaxLen or (parent and parent.MaxLen),
867 type = what,
868 option = concat(merged(aflags,wflags),","),
869 default = default,
870 }
871 context.setlayer (layerspec,locationspec,function()
872 context.definefieldbody ( { name } , fieldspec )
873 context.fieldbody ( { name } )
874 end)
875 end
876 elseif what == "Btn" then
877 if wflags.Radio or wflags.RadiosInUnison then
878
879 wflags.Radio = nil
880 wflags.RadiosInUnison = nil
881 what = "radio"
882 elseif wflags.PushButton then
883
884
885
886
887
888 wflags.PushButton = nil
889 what = "push"
890 else
891
892 what = "check"
893
894 if dataonly then
895 report_field("field %a, type %a, todo: render value",name,what)
896 else
897 local AP = annotation.AP or (parent and parent.AP)
898 if AP then
899 local a = document.__xrefs__[AP]
900 if a and pdfe.copyappearance then
901 local o = pdfe.copyappearance(document,a)
902 if o then
903 AP = pdfreference(o)
904 end
905 end
906 end
907 local dictionary = pdfdictionary {
908 Subtype = pdfconstant("Widget"),
909 FT = pdfconstant("Btn"),
910 T = pdfcopyunicode(annotation.T or parent.T),
911 F = pdfcopyinteger(annotation.F or parent.F),
912 Ff = pdfcopyinteger(annotation.Ff or parent.Ff),
913 AS = pdfcopyconstant(annotation.AS or (parent and parent.AS)),
914 AP = AP and pdfreference(AP),
915
916 }
917 local finalize = dictionary()
918 context.setlayer(layerspec,locationspec,function()
919 context(hpack_node(nodeinjections.annotation(W/bpfactor,H/bpfactor,0,finalize)))
920 end)
921 end
922 end
923 elseif what == "Ch" then
924
925 if wflags.PopUp then
926 wflags.PopUp = nil
927 if wflags.Edit then
928 wflags.Edit = nil
929 what = "combo"
930 else
931 what = "popup"
932 end
933 else
934 what = "choice"
935 end
936 elseif what == "Sig" then
937 what = "signature"
938 else
939 what = nil
940 end
941 end
942 end
943 end
944 end
945 end
946 end
947end
948
949
950
951function codeinjections.mergerenditions(specification)
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974end
975
976
977
978
979
980function codeinjections.getbookmarks(filename)
981
982
983
984
985 local list = bookmarks.extras.get(filename)
986
987 if list then
988 return list
989 else
990 list = { }
991 end
992
993 local document = nil
994
995 if isfile(filename) then
996 document = loadpdffile(filename)
997 else
998 report_outline("unknown file %a",filename)
999 bookmarks.extras.register(filename,list)
1000 return list
1001 end
1002
1003 local outlines = document.Catalog.Outlines
1004 local pages = document.pages
1005 local nofpages = document.nofpages
1006 local destinations = document.destinations
1007
1008
1009
1010
1011 local function setdestination(current,entry)
1012 local destination = nil
1013 local action = current.A
1014 if action then
1015 local subtype = action.S
1016 if subtype == "GoTo" then
1017 destination = action.D
1018 local kind = type(destination)
1019 if kind == "string" then
1020 entry.destination = destination
1021 destination = destinations[destination]
1022 local pagedata = destination and destination[1]
1023 if pagedata then
1024 entry.realpage = pagedata.number
1025 end
1026 elseif kind == "table" then
1027 local pageref = #destination
1028 if pageref then
1029 local pagedata = pages[pageref]
1030 if pagedata then
1031 entry.realpage = pagedata.number
1032 end
1033 end
1034 end
1035
1036
1037 end
1038 else
1039 local destination = current.Dest
1040 if destination then
1041 if type(destination) == "string" then
1042 local wanted = destinations[destination]
1043 destination = wanted and wanted.D
1044 if destination then
1045 entry.destination = destination
1046 end
1047 else
1048 local pagedata = destination and destination[1]
1049 if pagedata and pagedata.Type == "Page" then
1050 entry.realpage = pagedata.number
1051
1052
1053 end
1054 end
1055 end
1056 end
1057 end
1058
1059 local function traverse(current,depth)
1060 while current do
1061
1062 local title = current("Title")
1063 if title then
1064 if type(title) == "table" then
1065 title = lpdf.frombytes(title[2],title[3])
1066 end
1067 local entry = {
1068 level = depth,
1069 title = title,
1070 }
1071 list[#list+1] = entry
1072 setdestination(current,entry)
1073 if trace_outlines then
1074 report_outline("%w%s",2*depth,title)
1075 end
1076 end
1077 local first = current.First
1078 if first then
1079 local current = first
1080 while current do
1081 local title = current.Title
1082 if type(title) == "table" then
1083 title = lpdf.frombytes(title[2],title[3])
1084 end
1085 if title and trace_outlines then
1086 report_outline("%w%s",2*depth,title)
1087 end
1088 local entry = {
1089 level = depth,
1090 title = title,
1091 }
1092 setdestination(current,entry)
1093 list[#list+1] = entry
1094 traverse(current.First,depth+1)
1095 current = current.Next
1096 end
1097 end
1098 current = current.Next
1099 end
1100 end
1101
1102 if outlines then
1103 if trace_outlines then
1104 report_outline("outline of %a:",document.filename)
1105 report_outline()
1106 end
1107 traverse(outlines,0)
1108 if trace_outlines then
1109 report_outline()
1110 end
1111 elseif trace_outlines then
1112 report_outline("no outline in %a",document.filename)
1113 end
1114
1115 bookmarks.extras.register(filename,list)
1116
1117 return list
1118
1119end
1120
1121function codeinjections.mergebookmarks(specification)
1122
1123 if not specification then
1124 specification = figures and figures.current()
1125 specification = specification and specification.status
1126 end
1127 if specification then
1128 local fullname = specification.fullname
1129 local bookmarks = codeinjections.getbookmarks(fullname)
1130 local realpage = tonumber(specification.page) or 1
1131 for i=1,#bookmarks do
1132 local b = bookmarks[i]
1133 if not b.usedpage then
1134 if b.realpage == realpage then
1135 if trace_options then
1136 report_outline("using %a at page %a of file %a",b.title,realpage,fullname)
1137 end
1138 b.usedpage = true
1139 b.section = structures.sections.currentsectionindex()
1140 b.pageindex = specification.pageindex
1141 end
1142 end
1143 end
1144 end
1145end
1146
1147
1148
1149
1150
1151
1152
1153
1154function codeinjections.getinfo(specification)
1155 if type(specification) == "string" then
1156 specification = { filename = specification }
1157 end
1158 local filename = specification.filename
1159 if type(filename) == "string" and isfile(filename) then
1160 local pdffile = loadpdffile(filename)
1161 if pdffile then
1162 local pagenumber = specification.page or 1
1163 local metadata = specification.metadata
1164 local catalog = pdffile.Catalog
1165 local info = pdffile.Info
1166 local pages = pdffile.pages
1167 local nofpages = pdffile.nofpages
1168 if metadata then
1169 local m = catalog.Metadata
1170 if m then
1171 m = m()
1172 if metadata == "xml" then
1173 metadata = xml.convert(m)
1174 else
1175 metadata = m
1176 end
1177 else
1178 metadata = nil
1179 end
1180 else
1181 metadata = nil
1182 end
1183 if pagenumber > nofpages then
1184 pagenumber = nofpages
1185 end
1186 local nobox = { 0, 0, 0, 0 }
1187 local crop = nobox
1188 local media = nobox
1189 local page = pages[pagenumber]
1190 if page then
1191 crop = page.CropBox or nobox
1192 media = page.MediaBox or crop or nobox
1193 end
1194 local bbox = crop or media or nobox
1195 return {
1196 filename = filename,
1197 pdfversion = tonumber(catalog.Version),
1198 nofpages = nofpages,
1199 title = info.Title,
1200 creator = info.Creator,
1201 producer = info.Producer,
1202 creationdate = info.CreationDate,
1203 modification = info.ModDate,
1204 metadata = metadata,
1205 width = bbox[4] - bbox[2],
1206 height = bbox[3] - bbox[1],
1207 cropbox = { crop[1], crop[2], crop[3], crop[4] },
1208 mediabox = { media[1], media[2], media[3], media[4] } ,
1209 }
1210 end
1211 end
1212end
1213 |