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