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