lpdf-epa.lua /size: 43 Kb    last modification: 2020-07-01 14:35
1
if
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
-- Links can also have quadpoint
10 11
-- embedded files ... not bound to a page
12 13
local
type
,
tonumber
,
next
=
type
,
tonumber
,
next
14
local
format
,
gsub
,
lower
,
find
=
string
.
format
,
string
.
gsub
,
string
.
lower
,
string
.
find
15
local
formatters
=
string
.
formatters
16
local
concat
,
merged
=
table
.
concat
,
table
.
merged
17
local
abs
=
math
.
abs
18
local
expandname
=
file
.
expandname
19
local
allocate
=
utilities
.
storage
.
allocate
20
local
bor
,
band
=
bit32
.
bor
,
bit32
.
band
21
local
isfile
=
lfs
.
isfile
22 23
local
trace_links
=
false
trackers
.
register
(
"
figures.links
"
,
function
(
v
)
trace_links
=
v
end
)
24
local
trace_comments
=
false
trackers
.
register
(
"
figures.comments
"
,
function
(
v
)
trace_comments
=
v
end
)
25
local
trace_fields
=
false
trackers
.
register
(
"
figures.fields
"
,
function
(
v
)
trace_fields
=
v
end
)
26
local
trace_outlines
=
false
trackers
.
register
(
"
figures.outlines
"
,
function
(
v
)
trace_outlines
=
v
end
)
27 28
local
report_link
=
logs
.
reporter
(
"
backend
"
,
"
link
"
)
29
local
report_comment
=
logs
.
reporter
(
"
backend
"
,
"
comment
"
)
30
local
report_field
=
logs
.
reporter
(
"
backend
"
,
"
field
"
)
31
local
report_outline
=
logs
.
reporter
(
"
backend
"
,
"
outline
"
)
32 33
local
lpdf
=
lpdf
34
local
backends
=
backends
35
local
context
=
context
36 37
local
nodeinjections
=
backends
.
pdf
.
nodeinjections
38 39
local
pdfarray
=
lpdf
.
array
40
local
pdfdictionary
=
lpdf
.
dictionary
41
local
pdfconstant
=
lpdf
.
constant
42
local
pdfreserveobject
=
lpdf
.
reserveobject
43
local
pdfreference
=
lpdf
.
reference
44 45
local
pdfcopyboolean
=
lpdf
.
copyboolean
46
local
pdfcopyunicode
=
lpdf
.
copyunicode
47
local
pdfcopyarray
=
lpdf
.
copyarray
48
local
pdfcopydictionary
=
lpdf
.
copydictionary
49
local
pdfcopynumber
=
lpdf
.
copynumber
50
local
pdfcopyinteger
=
lpdf
.
copyinteger
51
local
pdfcopystring
=
lpdf
.
copystring
52
local
pdfcopyconstant
=
lpdf
.
copyconstant
53 54
local
createimage
=
images
.
create
55
local
embedimage
=
images
.
embed
56 57
local
hpack_node
=
nodes
.
hpack
58 59
local
loadpdffile
=
lpdf
.
epdf
.
load
60 61
local
nameonly
=
file
.
nameonly
62 63
local
variables
=
interfaces
.
variables
64
local
codeinjections
=
backends
.
pdf
.
codeinjections
65
----- urlescaper = lpegpatterns.urlescaper
66
----- utftohigh = lpegpatterns.utftohigh
67
local
escapetex
=
characters
.
filters
.
utf
.
private
.
escape
68 69
local
bookmarks
=
structures
.
bookmarks
70 71
local
maxdimen
=
0x3FFFFFFF
-- 2^30-1
72 73
local
bpfactor
=
number
.
dimenfactors
.
bp
74 75
local
layerspec
=
{
76
"
epdfcontent
"
77
}
78 79
local
getpos
=
function
(
)
getpos
=
backends
.
codeinjections
.
getpos
return
getpos
(
)
end
80 81
local
collected
=
allocate
(
)
82
local
tobesaved
=
allocate
(
)
83 84
local
jobembedded
=
{
85
collected
=
collected
,
86
tobesaved
=
tobesaved
,
87
}
88 89
job
.
embedded
=
jobembedded
90 91
local
function
initializer
(
)
92
tobesaved
=
jobembedded
.
tobesaved
93
collected
=
jobembedded
.
collected
94
end
95 96
job
.
register
(
'
job.embedded.collected
'
,
tobesaved
,
initializer
)
97 98
local
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
-- we could add a check for duplicate page insertion
107
tobesaved
[
expanded
]
=
true
108
--- but that is messy anyway so we forget about it
109
return
specification
,
fullname
,
loadpdffile
(
fullname
)
-- costs time
110
end
111
end
112 113
local
function
getmediasize
(
specification
,
pagedata
)
114
local
xscale
=
specification
.
xscale
or
1
115
local
yscale
=
specification
.
yscale
or
1
116
----- size = specification.size or "crop" -- todo
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
)
-- \\overlaywidth, \\overlayheight
123
local
height
=
yscale
*
(
ury
-
lly
)
-- \\overlaywidth, \\overlayheight
124
return
llx
,
lly
,
urx
,
ury
,
width
,
height
,
xscale
,
yscale
125
end
126 127
local
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
142
end
143 144
local
layerused
=
false
145 146
-- local function initializelayer(height,width)
147
-- if not layerused then
148
-- context.definelayer(layerspec, { height = height .. "bp", width = width .. "bp" })
149
-- layerused = true
150
-- end
151
-- end
152 153
local
function
initializelayer
(
height
,
width
)
154
-- if not layerused then
155
context
.
setuplayer
(
layerspec
,
{
height
=
height
.
.
"
bp
"
,
width
=
width
.
.
"
bp
"
}
)
156
layerused
=
true
157
-- end
158
end
159 160
function
codeinjections
.
flushmergelayer
(
)
161
if
layerused
then
162
context
.
flushlayer
(
layerspec
)
163
layerused
=
false
164
end
165
end
166 167
local
f_namespace
=
formatters
[
"
lpdf-epa-%s-
"
]
168 169
local
function
makenamespace
(
filename
)
170
filename
=
gsub
(
lower
(
nameonly
(
filename
)
)
,
"
[^%a%d]+
"
,
"
-
"
)
171
return
f_namespace
(
filename
)
172
end
173 174
local
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
=
{
-- predefining saves time
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
-- context.nested.button(buttonspec, "", { destination }) -- time this
198
)
199
end
200 201
local
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
-- [ 18 0 R /Fit ]
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
-- is this ok? isn't it destination already a string?
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
220
end
221 222
local
function
link_uri
(
x
,
y
,
w
,
h
,
document
,
annotation
)
223
local
url
=
annotation
.
A
.
URI
224
if
url
then
225
-- url = lpegmatch(urlescaper,url)
226
-- url = lpegmatch(utftohigh,url)
227
url
=
escapetex
(
url
)
228
add_link
(
x
,
y
,
w
,
h
,
formatters
[
"
url(%s)
"
]
(
url
)
,
"
url
"
)
229
end
230
end
231 232
-- The rules in PDF on what a 'file specification' is, is in fact quite elaborate
233
-- (see section 3.10 in the 1.7 reference) so we need to test for string as well
234
-- as a table. TH/20140916
235 236
-- When embedded is set then files need to have page references which is seldom the
237
-- case but you can generate them with context:
238
--
239
-- \setupinteraction[state=start,page={page,page}]
240
--
241
-- see tests/mkiv/interaction/cross[1|2|3].tex for an example
242 243
local
embedded
=
false
directives
.
register
(
"
figures.embedded
"
,
function
(
v
)
embedded
=
v
end
)
244
local
reported
=
{
}
245 246
local
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
-- hm, zero offset so maybe: destination + 1
262
destination
=
tonumber
(
destination
[
1
]
)
-- array
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
281
end
282 283
-- maybe handler per subtype and then one loop but then what about order ...
284 285
function
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
}
-- no need for a dict
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
-- moved outside previous test
333
context
.
setgvalue
(
"
figurereference
"
,
reference
)
-- global, todo: setmacro
334
if
trace_links
then
335
report_link
(
"
setting figure reference to %a
"
,
reference
)
336
end
337
specification
.
reference
=
reference
338
return
namespace
339
end
340 341
function
codeinjections
.
mergeviewerlayers
(
specification
)
342
-- todo: parse included page for layers .. or only for whole document inclusion
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
{
-- also does some cleaning
362
tag
=
tag
,
-- todo: #3A or so
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
374
end
375 376
-- It took a bit of puzzling and playing around to come to the following
377
-- implementation. In the end it looks simple but as usual it takes a while
378
-- to see what the specification (and implementation) boils down to. Lots of
379
-- shared properties and such. The scaling took some trial and error as
380
-- viewers differ. I had to extend some low level helpers to make it more
381
-- comfortable. Hm, the specification is somewhat incomplete as some fields
382
-- are permitted even if not mentioned so in the end we can share more code.
383
--
384
-- If all works ok, we can get rid of some copies which saves time and space.
385 386
local
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 404
local
function
copyBS
(
v
)
-- dict can be shared
405
if
v
then
406
-- return pdfdictionary {
407
-- Type = copypdfconstant(V.Type),
408
-- W = copypdfnumber (V.W),
409
-- S = copypdfstring (V.S),
410
-- D = copypdfarray (V.D),
411
-- }
412
return
copypdfdictionary
(
v
)
413
end
414
end
415 416
local
function
copyBE
(
v
)
-- dict can be shared
417
if
v
then
418
-- return pdfdictionary {
419
-- S = copypdfstring(V.S),
420
-- I = copypdfnumber(V.I),
421
-- }
422
return
copypdfdictionary
(
v
)
423
end
424
end
425 426
local
function
copyBorder
(
v
)
-- dict can be shared
427
if
v
then
428
-- todo
429
return
copypdfarray
(
v
)
430
end
431
end
432 433
local
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
440
end
441 442
local
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
449
end
450 451
local
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
458
end
459 460
local
function
copyC
(
v
)
461
if
v
then
462
-- todo: check color space
463
return
pdfcopyarray
(
v
)
464
end
465
end
466 467
local
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
(
)
-- already scaled
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
(
)
-- already scaled
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
(
)
494
end
495 496
local
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
-- todo: we can use runtoks instead of steps
514 515
local
function
validStamp
(
v
)
516
local
name
=
"
Stamped
"
-- fallback
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
-- we temporary return to \TEX:
532
context
.
predefinesymbol
{
name
}
533
context
.
step
(
)
534
-- beware, an error is not reported
535
return
pdfconstant
(
name
)
,
codeinjections
.
analyzenormalsymbol
(
name
)
536
end
537 538
local
annotationflags
=
lpdf
.
flags
.
annotations
539 540
local
function
copyF
(
v
,
lock
)
-- todo: bxor 24
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
547
end
548 549
-- Speed is not really an issue so we don't optimize this code too much. In the end (after
550
-- testing we end up with less code that we started with.
551 552
function
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
-- todo: proper parameter
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
-- we keep the order
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
-- common (skipped: P AP AS OC AF BM StructParent)
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
-- also common
605
CreationDate
=
pdfcopystring
(
annotation
.
CreationDate
)
,
606
T
=
pdfcopyunicode
(
annotation
.
T
)
,
607
Subj
=
pdfcopyunicode
(
annotation
.
Subj
)
,
608
-- border
609
Border
=
pdfcopyarray
(
annotation
.
Border
)
,
610
BS
=
copyBS
(
annotation
.
BS
)
,
611
BE
=
copyBE
(
annotation
.
BE
)
,
612
-- sort of common
613
Popup
=
copyPopup
(
annotation
.
Popup
,
references
)
,
614
RC
=
pdfcopyunicode
(
annotation
.
RC
)
-- string or stream
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
-- dictionary.RD = pdfcopyarray (annotation.RD)
622
elseif
kind
=
=
"
text
"
then
623
-- somehow F fails to view : /F 24 : bit4=nozoom bit5=norotate
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
)
-- caret
632
dictionary
.
Sy
=
pdfcopyconstant
(
annotation
.
Sy
)
-- caret
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
-- dictionary.RC = pdfcopystring (annotation.RC)
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
)
-- names
652
dictionary
.
CO
=
pdfcopyarray
(
annotation
.
CO
)
653
voffset
=
0
654
elseif
kind
=
=
"
shape
"
then
655
dictionary
.
IC
=
pdfcopyarray
(
annotation
.
IC
)
656
-- dictionary.RD = pdfcopyarray (annotation.RD)
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
-- dictionary.LE = pdfcopyarray (annotation.LE) -- todo: names in array
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
-- dictionary.RD = pdfcopyarray (annotation.RD)
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
-- report_comment("skipping annotation, index %a",i)
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
701
end
702 703
local
widgetflags
=
lpdf
.
flags
.
widgets
704 705
local
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
715
end
716 717
-- BS : border style dict
718
-- R : rotation 0 90 180 270
719
-- BG : background array
720
-- CA : caption string
721
-- RC : roll over caption
722
-- AC : down caption
723
-- I/RI/IX : icon streams
724
-- IF : fit dictionary
725
-- TP : text position number
726 727
-- Opt : array of texts
728
-- TI : top index
729 730
-- V : value
731
-- DV : default value
732
-- DS : default string
733
-- RV : rich
734
-- Q : quadding (0=left 1=middle 2=right)
735 736
function
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
-- we keep the order
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
-- DA DV F FT MaxLen MK Q T V | AA OC
778
if
wflags
.
MultiLine
then
779
wflags
.
MultiLine
=
nil
780
what
=
"
text
"
781
else
782
what
=
"
line
"
783
end
784
-- via context
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
-- AP AS DA F Ff FT H MK T V | AA OC
802
wflags
.
Radio
=
nil
803
wflags
.
RadiosInUnison
=
nil
804
what
=
"
radio
"
805
elseif
wflags
.
PushButton
then
806
-- AP DA F Ff FT H MK T | AA OC
807
--
808
-- Push buttons only have an appearance and some associated
809
-- actions so they are not worth copying.
810
--
811
wflags
.
PushButton
=
nil
812
what
=
"
push
"
813
else
814
-- AP AS DA F Ff FT H MK T V | OC AA
815
what
=
"
check
"
816
-- direct
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
-- F Ff FT Opt T | AA OC (rest follows)
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
866
end
867 868
-- Beware, bookmarks can be in pdfdoc encoding or in unicode. However, in mkiv we
869
-- write out the strings in unicode (hex). When we read them in, we check for a bom
870
-- and convert to utf.
871 872
function
codeinjections
.
getbookmarks
(
filename
)
873 874
-- The first version built a nested tree and flattened that afterwards ... but I decided
875
-- to keep it simple and flat.
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
-- I need to check this destination analyzer with the one in annotations .. best share
901
-- code (and not it's inconsistent). On the todo list ...
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
-- elseif subtype then
928
-- report("unsupported bookmark action %a",subtype)
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
-- else
944
-- report("unsupported bookmark destination (no page)")
945
end
946
end
947
end
948
end
949
end
950 951
local
function
traverse
(
current
,
depth
)
952
while
current
do
953
-- local title = current.Title
954
local
title
=
current
(
"
Title
"
)
-- can be pdfdoc or unicode
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 1005
end
1006 1007
function
codeinjections
.
mergebookmarks
(
specification
)
1008
-- codeinjections.getbookmarks(document)
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
1031
end
1032 1033
-- A bit more than a placeholder but in the same perspective as
1034
-- inclusion of comments and fields:
1035
--
1036
-- getinfo{ filename = "tt.pdf", metadata = true }
1037
-- getinfo{ filename = "tt.pdf", page = 1, metadata = "xml" }
1038
-- getinfo("tt.pdf")
1039 1040
function
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
]
}
,
-- we need access
1094
mediabox
=
{
media
[
1
]
,
media
[
2
]
,
media
[
3
]
,
media
[
4
]
}
,
-- we need access
1095
}
1096
end
1097
end
1098
end
1099