grph-inc.lua /size: 73 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
grph-inc
'
]
=
{
2
version
=
1
.
001
,
3
comment
=
"
companion to grph-inc.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
-- todo: in pdfe: pdfe.copyappearance(document,objnum)
10
--
11
-- local im = createimage { filename = fullname }
12
-- local on = images.flushobject(im,document.__xrefs__[AP])
13 14
-- todo: files are sometimes located twice
15
-- todo: empty filename or only suffix always false (not found)
16
-- lowercase types
17
-- mps tex tmp svg
18
-- partly qualified
19
-- dimensions
20
-- use metatables
21
-- figures.boxnumber can go as we now can use names
22
-- avoid push
23
-- move some to command namespace
24 25
--[[ 26The ConTeXt figure inclusion mechanisms are among the oldest code 27in ConTeXt and evolved into a complex whole. One reason is that we 28deal with backend in an abstract way. What complicates matters is 29that we deal with internal graphics as well: TeX code, MetaPost code, 30etc. Later on figure databases were introduced, which resulted in 31a plug in model for locating images. On top of that runs a conversion 32mechanism (with caching) and resource logging. 33 34Porting that to Lua is not that trivial because quite some 35status information is kept between al these stages. Of course, image 36reuse also has some price, and so I decided to implement the graphics 37inclusion in several layers: detection, loading, inclusion, etc. 38 39Object sharing and scaling can happen at each stage, depending on the 40way the resource is dealt with. 41 42The TeX-Lua mix is suboptimal. This has to do with the fact that we cannot 43run TeX code from within Lua. Some more functionality will move to Lua. 44]]
--
45 46
-- todo: store loaded pages per pdf file someplace
47 48
local
tonumber
,
tostring
,
next
,
unpack
=
tonumber
,
tostring
,
next
,
unpack
49
local
format
,
lower
,
find
,
match
,
gsub
=
string
.
format
,
string
.
lower
,
string
.
find
,
string
.
match
,
string
.
gsub
50
local
longtostring
=
string
.
longtostring
51
local
contains
=
table
.
contains
52
local
sortedhash
,
sortedkeys
=
table
.
sortedhash
,
table
.
sortedkeys
53
local
concat
,
insert
,
remove
=
table
.
concat
,
table
.
insert
,
table
.
remove
54
local
todimen
=
string
.
todimen
55
local
collapsepath
=
file
.
collapsepath
56
local
formatters
=
string
.
formatters
57
local
odd
=
math
.
odd
58
local
isfile
,
isdir
,
modificationtime
=
lfs
.
isfile
,
lfs
.
isdir
,
lfs
.
modification
59 60
local
P
,
R
,
S
,
Cc
,
C
,
Cs
,
Ct
,
lpegmatch
=
lpeg
.
P
,
lpeg
.
R
,
lpeg
.
S
,
lpeg
.
Cc
,
lpeg
.
C
,
lpeg
.
Cs
,
lpeg
.
Ct
,
lpeg
.
match
61 62
local
settings_to_array
=
utilities
.
parsers
.
settings_to_array
63
local
settings_to_hash
=
utilities
.
parsers
.
settings_to_hash
64
local
allocate
=
utilities
.
storage
.
allocate
65
local
setmetatableindex
=
table
.
setmetatableindex
66
local
replacetemplate
=
utilities
.
templates
.
replace
67 68
local
bpfactor
=
number
.
dimenfactors
.
bp
69 70
images
=
images
or
{
}
71
local
images
=
images
72 73
local
hasscheme
=
url
.
hasscheme
74
local
urlhashed
=
url
.
hashed
75 76
local
resolveprefix
=
resolvers
.
resolve
77 78
local
texgetbox
=
tex
.
getbox
79
local
texsetbox
=
tex
.
setbox
80 81
local
hpack
=
node
.
hpack
82 83
local
new_latelua
=
nodes
.
pool
.
latelua
84 85
local
context
=
context
86 87
local
implement
=
interfaces
.
implement
88
local
variables
=
interfaces
.
variables
89 90
local
codeinjections
=
backends
.
codeinjections
91
local
nodeinjections
=
backends
.
nodeinjections
92 93
local
trace_figures
=
false
trackers
.
register
(
"
graphics.locating
"
,
function
(
v
)
trace_figures
=
v
end
)
94
local
trace_bases
=
false
trackers
.
register
(
"
graphics.bases
"
,
function
(
v
)
trace_bases
=
v
end
)
95
local
trace_programs
=
false
trackers
.
register
(
"
graphics.programs
"
,
function
(
v
)
trace_programs
=
v
end
)
96
local
trace_conversion
=
false
trackers
.
register
(
"
graphics.conversion
"
,
function
(
v
)
trace_conversion
=
v
end
)
97
local
trace_inclusion
=
false
trackers
.
register
(
"
graphics.inclusion
"
,
function
(
v
)
trace_inclusion
=
v
end
)
98
local
trace_usage
=
false
trackers
.
register
(
"
graphics.usage
"
,
function
(
v
)
trace_usage
=
v
end
)
99 100
local
extra_check
=
false
directives
.
register
(
"
graphics.extracheck
"
,
function
(
v
)
extra_check
=
v
end
)
101
local
auto_transform
=
true
directives
.
register
(
"
graphics.autotransform
"
,
function
(
v
)
auto_transform
=
v
end
)
102 103
local
report
=
logs
.
reporter
(
"
graphics
"
)
104
local
report_inclusion
=
logs
.
reporter
(
"
graphics
"
,
"
inclusion
"
)
105 106
local
f_hash_part
=
formatters
[
"
%s->%s->%s->%s
"
]
107
local
f_hash_full
=
formatters
[
"
%s->%s->%s->%s->%s->%s->%s->%s
"
]
108 109
local
v_yes
=
variables
.
yes
110
local
v_global
=
variables
[
"
global
"
]
111
local
v_local
=
variables
[
"
local
"
]
112
local
v_default
=
variables
.
default
113
local
v_auto
=
variables
.
auto
114 115
local
maxdimen
=
tex
.
magicconstants
.
maxdimen
-- 0x3FFFFFFF -- 2^30-1
116 117
local
ctx_doscalefigure
=
context
.
doscalefigure
118
local
ctx_relocateexternalfigure
=
context
.
relocateexternalfigure
119
local
ctx_startfoundexternalfigure
=
context
.
startfoundexternalfigure
120
local
ctx_stopfoundexternalfigure
=
context
.
stopfoundexternalfigure
121
local
ctx_dosetfigureobject
=
context
.
dosetfigureobject
122
local
ctx_doboxfigureobject
=
context
.
doboxfigureobject
123 124
-- extensions
125 126
function
checkimage
(
figure
)
127
if
figure
then
128
local
width
=
figure
.
width
or
0
129
local
height
=
figure
.
height
or
0
130
if
width
<
=
0
or
height
<
=
0
then
131
report_inclusion
(
"
image %a has bad dimensions (%p,%p), discarding
"
,
figure
.
filename
or
"
?
"
,
width
,
height
)
132
return
false
,
"
bad dimensions
"
133
end
134
-- local xres = figure.xres
135
-- local yres = figure.yres
136
local
changes
=
false
137
if
height
>
width
then
138
if
height
>
maxdimen
then
139
figure
.
height
=
maxdimen
140
figure
.
width
=
width
*
maxdimen
/
height
141
changed
=
true
142
end
143
elseif
width
>
maxdimen
then
144
figure
.
width
=
maxdimen
145
figure
.
height
=
height
*
maxdimen
/
width
146
changed
=
true
147
end
148
if
changed
then
149
report_inclusion
(
"
limiting natural dimensions of %a, old %p * %p, new %p * %p
"
,
150
figure
.
filename
,
width
,
height
,
figure
.
width
,
figure
.
height
)
151
end
152
if
width
>
=
maxdimen
or
height
>
=
maxdimen
then
153
report_inclusion
(
"
image %a is too large (%p,%p), discarding
"
,
154
figure
.
filename
,
width
,
height
)
155
return
false
,
"
dimensions too large
"
156
end
157
return
figure
158
end
159
end
160 161
-- This is a bit of abstraction. Fro a while we will follow the luatex image
162
-- model.
163 164
local
imagekeys
=
{
165
-- only relevant ones (permitted in luatex)
166
"
width
"
,
"
height
"
,
"
depth
"
,
"
bbox
"
,
167
"
colordepth
"
,
"
colorspace
"
,
168
"
filename
"
,
"
filepath
"
,
"
visiblefilename
"
,
169
"
imagetype
"
,
"
stream
"
,
170
"
index
"
,
"
objnum
"
,
171
"
pagebox
"
,
"
page
"
,
"
pages
"
,
172
"
rotation
"
,
"
transform
"
,
173
"
xsize
"
,
"
ysize
"
,
"
xres
"
,
"
yres
"
,
174
}
175 176
local
imagesizes
=
{
177
art
=
true
,
bleed
=
true
,
crop
=
true
,
178
media
=
true
,
none
=
true
,
trim
=
true
,
179
}
180 181
local
imagetypes
=
{
[
0
]
=
182
"
none
"
,
183
"
pdf
"
,
"
png
"
,
"
jpg
"
,
"
jp2
"
,
"
jbig2
"
,
184
"
stream
"
,
"
memstream
"
,
185
}
186 187
imagetypes
=
table
.
swapped
(
imagetypes
,
imagetypes
)
188 189
images
.
keys
=
imagekeys
190
images
.
types
=
imagetypes
191
images
.
sizes
=
imagesizes
192 193
local
function
createimage
(
specification
)
194
return
backends
.
codeinjections
.
newimage
(
specification
)
195
end
196 197
local
function
copyimage
(
specification
)
198
return
backends
.
codeinjections
.
copyimage
(
specification
)
199
end
200 201
local
function
scanimage
(
specification
)
202
return
backends
.
codeinjections
.
scanimage
(
specification
)
203
end
204 205
local
function
embedimage
(
specification
)
206
-- write the image to file
207
return
backends
.
codeinjections
.
embedimage
(
specification
)
208
end
209 210
local
function
wrapimage
(
specification
)
211
-- create an image rule
212
return
backends
.
codeinjections
.
wrapimage
(
specification
)
213
end
214 215
images
.
create
=
createimage
216
images
.
scan
=
scanimage
217
images
.
copy
=
copyimage
218
images
.
wrap
=
wrapimage
219
images
.
embed
=
embedimage
220 221
-- If really needed we can provide:
222
--
223
-- img = {
224
-- new = createimage,
225
-- scan = scanimage,
226
-- copy = copyimage,
227
-- node = wrapimage,
228
-- write = function(specification) context(wrapimage(specification)) end,
229
-- immediatewrite = embedimage,
230
-- immediatewriteobject = function() end, -- not upported, experimental anyway
231
-- boxes = function() return sortedkeys(imagesizes) end,
232
-- fields = function() return imagekeys end,
233
-- types = function() return { unpack(imagetypes,0,#imagetypes) } end,
234
-- }
235 236
-- end of copies / mapping
237 238
local
function
imagetotable
(
imgtable
)
239
if
type
(
imgtable
)
=
=
"
table
"
then
240
return
copy
(
imgtable
)
241
end
242
local
result
=
{
}
243
for
k
=
1
,
#
imagekeys
do
244
local
key
=
imagekeys
[
k
]
245
result
[
key
]
=
imgtable
[
key
]
246
end
247
return
result
248
end
249 250
function
images
.
serialize
(
i
,
...
)
251
return
table
.
serialize
(
imagetotable
(
i
)
,
...
)
252
end
253 254
function
images
.
print
(
i
,
...
)
255
return
table
.
print
(
imagetotable
(
i
)
,
...
)
256
end
257 258
local
function
checkimagesize
(
size
)
259
if
size
then
260
size
=
gsub
(
size
,
"
box
"
,
"
"
)
261
return
imagesizes
[
size
]
and
size
or
"
crop
"
262
else
263
return
"
crop
"
264
end
265
end
266 267
images
.
check
=
checkimage
268
images
.
checksize
=
checkimagesize
269
images
.
totable
=
imagetotable
270 271
-- local indexed = { }
272
--
273
-- function images.ofindex(n)
274
-- return indexed[n]
275
-- end
276 277
--- we can consider an grph-ini file
278 279
figures
=
figures
or
{
}
280
local
figures
=
figures
281 282
figures
.
images
=
images
283
figures
.
boxnumber
=
figures
.
boxnumber
or
0
284
figures
.
defaultsearch
=
true
285
figures
.
defaultwidth
=
0
286
figures
.
defaultheight
=
0
287
figures
.
defaultdepth
=
0
288
figures
.
nofprocessed
=
0
289
figures
.
nofmissing
=
0
290
figures
.
preferquality
=
true
-- quality over location
291 292
local
figures_loaded
=
allocate
(
)
figures
.
loaded
=
figures_loaded
293
local
figures_used
=
allocate
(
)
figures
.
used
=
figures_used
294
local
figures_found
=
allocate
(
)
figures
.
found
=
figures_found
295
local
figures_suffixes
=
allocate
(
)
figures
.
suffixes
=
figures_suffixes
296
local
figures_patterns
=
allocate
(
)
figures
.
patterns
=
figures_patterns
297
local
figures_resources
=
allocate
(
)
figures
.
resources
=
figures_resources
298 299
local
existers
=
allocate
(
)
figures
.
existers
=
existers
300
local
checkers
=
allocate
(
)
figures
.
checkers
=
checkers
301
local
includers
=
allocate
(
)
figures
.
includers
=
includers
302
local
remappers
=
allocate
(
)
figures
.
remappers
=
remappers
303
local
converters
=
allocate
(
)
figures
.
converters
=
converters
304
local
identifiers
=
allocate
(
)
figures
.
identifiers
=
identifiers
305
local
programs
=
allocate
(
)
figures
.
programs
=
programs
306 307
local
defaultformat
=
"
pdf
"
308
local
defaultprefix
=
"
m_k_i_v_
"
309 310
figures
.
localpaths
=
allocate
{
311
"
.
"
,
"
..
"
,
"
../..
"
312
}
313 314
figures
.
cachepaths
=
allocate
{
315
prefix
=
"
"
,
316
path
=
"
.
"
,
317
subpath
=
"
.
"
,
318
}
319 320
local
figure_paths
=
allocate
(
table
.
copy
(
figures
.
localpaths
)
)
321
figures
.
paths
=
figure_paths
322 323
local
figures_order
=
allocate
{
324
"
pdf
"
,
"
mps
"
,
"
jpg
"
,
"
png
"
,
"
jp2
"
,
"
jbig
"
,
"
svg
"
,
"
eps
"
,
"
tif
"
,
"
gif
"
,
"
mov
"
,
"
buffer
"
,
"
tex
"
,
"
cld
"
,
"
auto
"
,
325
}
326 327
local
figures_formats
=
allocate
{
-- magic and order will move here
328
[
"
pdf
"
]
=
{
list
=
{
"
pdf
"
}
}
,
329
[
"
mps
"
]
=
{
patterns
=
{
"
^mps$
"
,
"
^%d+$
"
}
}
,
-- we need to anchor
330
[
"
jpg
"
]
=
{
list
=
{
"
jpg
"
,
"
jpeg
"
}
}
,
331
[
"
png
"
]
=
{
list
=
{
"
png
"
}
}
,
332
[
"
jp2
"
]
=
{
list
=
{
"
jp2
"
}
}
,
333
[
"
jbig
"
]
=
{
list
=
{
"
jbig
"
,
"
jbig2
"
,
"
jb2
"
}
}
,
334
[
"
svg
"
]
=
{
list
=
{
"
svg
"
,
"
svgz
"
}
}
,
335
[
"
eps
"
]
=
{
list
=
{
"
eps
"
,
"
ai
"
}
}
,
336
[
"
gif
"
]
=
{
list
=
{
"
gif
"
}
}
,
337
[
"
tif
"
]
=
{
list
=
{
"
tif
"
,
"
tiff
"
}
}
,
338
[
"
mov
"
]
=
{
list
=
{
"
mov
"
,
"
flv
"
,
"
mp4
"
}
}
,
-- "avi" is not supported
339
[
"
buffer
"
]
=
{
list
=
{
"
tmp
"
,
"
buffer
"
,
"
buf
"
}
}
,
340
[
"
tex
"
]
=
{
list
=
{
"
tex
"
}
}
,
341
[
"
cld
"
]
=
{
list
=
{
"
cld
"
}
}
,
342
[
"
auto
"
]
=
{
list
=
{
"
auto
"
}
}
,
343
}
344 345
local
figures_magics
=
allocate
{
346
{
format
=
"
png
"
,
pattern
=
P
(
"
\137PNG\013\010\026\010
"
)
}
,
-- 89 50 4E 47 0D 0A 1A 0A,
347
{
format
=
"
jpg
"
,
pattern
=
P
(
"
\255\216\255
"
)
}
,
-- FF D8 FF
348
{
format
=
"
jp2
"
,
pattern
=
P
(
"
\000\000\000\012\106\080\032\032\013\010
"
)
,
}
,
-- 00 00 00 0C 6A 50 20 20 0D 0A },
349
{
format
=
"
gif
"
,
pattern
=
P
(
"
GIF
"
)
}
,
350
{
format
=
"
pdf
"
,
pattern
=
(
1
-
P
(
"
%PDF
"
)
)
^
0
*
P
(
"
%PDF
"
)
}
,
351
}
352 353
local
figures_native
=
allocate
{
354
pdf
=
true
,
355
jpg
=
true
,
356
jp2
=
true
,
357
png
=
true
,
358
}
359 360
figures
.
formats
=
figures_formats
-- frozen
361
figures
.
magics
=
figures_magics
-- frozen
362
figures
.
order
=
figures_order
-- frozen
363 364
-- name checker
365 366
local
okay
=
P
(
"
m_k_i_v_
"
)
367 368
local
pattern
=
(
R
(
"
az
"
,
"
AZ
"
)
*
P
(
"
:
"
)
)
^
-1
*
(
-- a-z : | A-Z :
369
(
okay
+
R
(
"
az
"
,
"
09
"
)
+
S
(
"
_/
"
)
-
P
(
"
_
"
)
^
2
)
^
1
*
(
P
(
"
.
"
)
*
R
(
"
az
"
)
^
1
)
^
0
*
P
(
-1
)
+
-- a-z | single _ | /
370
(
okay
+
R
(
"
az
"
,
"
09
"
)
+
S
(
"
-/
"
)
-
P
(
"
-
"
)
^
2
)
^
1
*
(
P
(
"
.
"
)
*
R
(
"
az
"
)
^
1
)
^
0
*
P
(
-1
)
+
-- a-z | single - | /
371
(
okay
+
R
(
"
AZ
"
,
"
09
"
)
+
S
(
"
_/
"
)
-
P
(
"
_
"
)
^
2
)
^
1
*
(
P
(
"
.
"
)
*
R
(
"
AZ
"
)
^
1
)
^
0
*
P
(
-1
)
+
-- A-Z | single _ | /
372
(
okay
+
R
(
"
AZ
"
,
"
09
"
)
+
S
(
"
-/
"
)
-
P
(
"
-
"
)
^
2
)
^
1
*
(
P
(
"
.
"
)
*
R
(
"
AZ
"
)
^
1
)
^
0
*
P
(
-1
)
-- A-Z | single - | /
373
)
*
Cc
(
false
)
+
Cc
(
true
)
374 375
function
figures
.
badname
(
name
)
376
if
not
name
then
377
-- bad anyway
378
elseif
not
hasscheme
(
name
)
then
379
return
lpegmatch
(
pattern
,
name
)
380
else
381
return
lpegmatch
(
pattern
,
file
.
basename
(
name
)
)
382
end
383
end
384 385
logs
.
registerfinalactions
(
function
(
)
386
local
done
=
false
387
if
trace_usage
and
figures
.
nofprocessed
>
0
then
388
logs
.
startfilelogging
(
report
,
"
names
"
)
389
for
_
,
data
in
sortedhash
(
figures_found
)
do
390
if
done
then
391
report
(
)
392
else
393
done
=
true
394
end
395
report
(
"
asked : %s
"
,
data
.
askedname
)
396
if
data
.
found
then
397
report
(
"
format : %s
"
,
data
.
format
)
398
report
(
"
found : %s
"
,
data
.
foundname
)
399
report
(
"
used : %s
"
,
data
.
fullname
)
400
if
data
.
badname
then
401
report
(
"
comment : %s
"
,
"
bad name
"
)
402
elseif
data
.
comment
then
403
report
(
"
comment : %s
"
,
data
.
comment
)
404
end
405
else
406
report
(
"
comment : %s
"
,
"
not found
"
)
407
end
408
end
409
logs
.
stopfilelogging
(
)
410
end
411
if
figures
.
nofmissing
>
0
and
logs
.
loggingerrors
(
)
then
412
logs
.
starterrorlogging
(
report
,
"
missing figures
"
)
413
for
_
,
data
in
sortedhash
(
figures_found
)
do
414
report
(
"
%w%s
"
,
6
,
data
.
askedname
)
415
end
416
logs
.
stoperrorlogging
(
)
417
end
418
end
)
419 420
-- We can set the order but only indirectly so that we can check for support.
421 422
function
figures
.
setorder
(
list
)
-- can be table or string
423
if
type
(
list
)
=
=
"
string
"
then
424
list
=
settings_to_array
(
list
)
425
end
426
if
list
and
#
list
>
0
then
427
figures_order
=
allocate
(
)
428
figures
.
order
=
figures_order
429
local
done
=
{
}
-- just to be sure in case the list is generated
430
for
i
=
1
,
#
list
do
431
local
l
=
lower
(
list
[
i
]
)
432
if
figures_formats
[
l
]
and
not
done
[
l
]
then
433
figures_order
[
#
figures_order
+
1
]
=
l
434
done
[
l
]
=
true
435
end
436
end
437
report_inclusion
(
"
lookup order % a
"
,
figures_order
)
438
else
439
-- invalid list
440
end
441
end
442 443
local
function
guessfromstring
(
str
)
444
if
str
then
445
for
i
=
1
,
#
figures_magics
do
446
local
pattern
=
figures_magics
[
i
]
447
if
lpegmatch
(
pattern
.
pattern
,
str
)
then
448
local
format
=
pattern
.
format
449
if
trace_figures
then
450
report_inclusion
(
"
file %a has format %a
"
,
filename
,
format
)
451
end
452
return
format
453
end
454
end
455
end
456
end
457 458
figures
.
guessfromstring
=
guessfromstring
459 460
function
figures
.
guess
(
filename
)
461
local
f
=
io
.
open
(
filename
,
'
rb
'
)
462
if
f
then
463
local
str
=
f
:
read
(
100
)
464
f
:
close
(
)
465
if
str
then
466
return
guessfromstring
(
str
)
467
end
468
end
469
end
470 471
local
function
setlookups
(
)
-- tobe redone .. just set locals
472
figures_suffixes
=
allocate
(
)
473
figures_patterns
=
allocate
(
)
474
for
_
,
format
in
next
,
figures_order
do
475
local
data
=
figures_formats
[
format
]
476
local
list
=
data
.
list
477
if
list
then
478
for
i
=
1
,
#
list
do
479
figures_suffixes
[
list
[
i
]
]
=
format
-- hash
480
end
481
else
482
figures_suffixes
[
format
]
=
format
483
end
484
local
patterns
=
data
.
patterns
485
if
patterns
then
486
for
i
=
1
,
#
patterns
do
487
figures_patterns
[
#
figures_patterns
+
1
]
=
{
patterns
[
i
]
,
format
}
-- array
488
end
489
end
490
end
491
figures
.
suffixes
=
figures_suffixes
492
figures
.
patterns
=
figures_patterns
493
end
494 495
setlookups
(
)
496 497
figures
.
setlookups
=
setlookups
498 499
function
figures
.
registerresource
(
t
)
500
local
n
=
#
figures_resources
+
1
501
figures_resources
[
n
]
=
t
502
return
n
503
end
504 505
local
function
register
(
tag
,
what
,
target
)
506
local
data
=
figures_formats
[
target
]
-- resolver etc
507
if
not
data
then
508
data
=
{
}
509
figures_formats
[
target
]
=
data
510
end
511
local
d
=
data
[
tag
]
-- list or pattern
512
if
d
and
not
contains
(
d
,
what
)
then
513
d
[
#
d
+
1
]
=
what
-- suffix or patternspec
514
else
515
data
[
tag
]
=
{
what
}
516
end
517
if
not
contains
(
figures_order
,
target
)
then
518
figures_order
[
#
figures_order
+
1
]
=
target
519
end
520
setlookups
(
)
521
end
522 523
function
figures
.
registersuffix
(
suffix
,
target
)
register
(
'
list
'
,
suffix
,
target
)
end
524
function
figures
.
registerpattern
(
pattern
,
target
)
register
(
'
pattern
'
,
pattern
,
target
)
end
525 526
implement
{
name
=
"
registerfiguresuffix
"
,
actions
=
register
,
arguments
=
{
"
'list'
"
,
"
string
"
,
"
string
"
}
}
527
implement
{
name
=
"
registerfigurepattern
"
,
actions
=
register
,
arguments
=
{
"
'pattern'
"
,
"
string
"
,
"
string
"
}
}
528 529
local
last_locationset
=
last_locationset
or
nil
530
local
last_pathlist
=
last_pathlist
or
nil
531 532
function
figures
.
setpaths
(
locationset
,
pathlist
)
533
if
last_locationset
=
=
locationset
and
last_pathlist
=
=
pathlist
then
534
-- this function can be called each graphic so we provide this optimization
535
return
536
end
537
local
t
,
h
=
figure_paths
,
settings_to_hash
(
locationset
)
538
if
last_locationset
~
=
locationset
then
539
-- change == reset (actually, a 'reset' would indeed reset
540
if
h
[
v_local
]
then
541
t
=
table
.
fastcopy
(
figures
.
localpaths
or
{
}
)
542
else
543
t
=
{
}
544
end
545
figures
.
defaultsearch
=
h
[
v_default
]
546
last_locationset
=
locationset
547
end
548
if
h
[
v_global
]
then
549
local
list
=
settings_to_array
(
pathlist
)
550
for
i
=
1
,
#
list
do
551
local
s
=
list
[
i
]
552
if
not
contains
(
t
,
s
)
then
553
t
[
#
t
+
1
]
=
s
554
end
555
end
556
end
557
figure_paths
=
t
558
last_pathlist
=
pathlist
559
figures
.
paths
=
figure_paths
560
if
trace_figures
then
561
report_inclusion
(
"
using locations %a
"
,
last_locationset
)
562
report_inclusion
(
"
using paths % a
"
,
figure_paths
)
563
end
564
end
565 566
implement
{
name
=
"
setfigurepaths
"
,
actions
=
figures
.
setpaths
,
arguments
=
"
2 strings
"
}
567 568
-- check conversions and handle it here
569 570
function
figures
.
hash
(
data
)
571
local
status
=
data
and
data
.
status
572
return
(
status
and
status
.
hash
or
tostring
(
status
.
private
)
)
or
"
nohash
"
-- the <img object>
573
end
574 575
-- interfacing to tex
576 577
local
function
new
(
)
-- we could use metatables status -> used -> request but it needs testing
578
local
request
=
{
579
name
=
false
,
580
label
=
false
,
581
format
=
false
,
582
page
=
false
,
583
width
=
false
,
584
height
=
false
,
585
preview
=
false
,
586
[
"
repeat
"
]
=
false
,
587
controls
=
false
,
588
display
=
false
,
589
mask
=
false
,
590
conversion
=
false
,
591
resolution
=
false
,
592
color
=
false
,
593
arguments
=
false
,
594
cache
=
false
,
595
prefix
=
false
,
596
size
=
false
,
597
}
598
local
used
=
{
599
fullname
=
false
,
600
format
=
false
,
601
name
=
false
,
602
path
=
false
,
603
suffix
=
false
,
604
width
=
false
,
605
height
=
false
,
606
}
607
local
status
=
{
608
status
=
0
,
609
converted
=
false
,
610
cached
=
false
,
611
fullname
=
false
,
612
format
=
false
,
613
}
614
-- this needs checking because we might check for nil, the test case
615
-- is getfiguredimensions which then should return ~= 0
616
-- setmetatableindex(status, used)
617
-- setmetatableindex(used, request)
618
return
{
619
request
=
request
,
620
used
=
used
,
621
status
=
status
,
622
}
623
end
624 625
-- use table.insert|remove
626 627
local
lastfiguredata
=
nil
-- will be topofstack or last so no { } (else problems with getfiguredimensions)
628
local
callstack
=
{
}
629 630
function
figures
.
initialize
(
request
)
631
local
figuredata
=
new
(
)
632
if
request
then
633
-- request.width/height are strings and are only used when no natural dimensions
634
-- can be determined; at some point the handlers might set them to numbers instead
635
local
w
=
tonumber
(
request
.
width
)
or
0
636
local
h
=
tonumber
(
request
.
height
)
or
0
637
local
p
=
tonumber
(
request
.
page
)
or
0
638
request
.
width
=
w
>
0
and
w
or
nil
639
request
.
height
=
h
>
0
and
h
or
nil
640
--
641
request
.
page
=
p
>
0
and
p
or
1
642
request
.
keepopen
=
p
>
0
643
request
.
size
=
checkimagesize
(
request
.
size
)
644
request
.
object
=
request
.
object
=
=
v_yes
645
request
[
"
repeat
"
]
=
request
[
"
repeat
"
]
=
=
v_yes
646
request
.
preview
=
request
.
preview
=
=
v_yes
647
request
.
cache
=
request
.
cache
~
=
"
"
and
request
.
cache
648
request
.
prefix
=
request
.
prefix
~
=
"
"
and
request
.
prefix
649
request
.
format
=
request
.
format
~
=
"
"
and
request
.
format
650
request
.
compact
=
request
.
compact
=
=
v_yes
651
table
.
merge
(
figuredata
.
request
,
request
)
652
end
653
return
figuredata
654
end
655 656
function
figures
.
push
(
request
)
657
statistics
.
starttiming
(
figures
)
658
local
figuredata
=
figures
.
initialize
(
request
)
-- we could use table.sparse but we set them later anyway
659
insert
(
callstack
,
figuredata
)
660
lastfiguredata
=
figuredata
661
return
figuredata
662
end
663 664
function
figures
.
pop
(
)
665
remove
(
callstack
)
666
lastfiguredata
=
callstack
[
#
callstack
]
or
lastfiguredata
667
statistics
.
stoptiming
(
figures
)
668
end
669 670
function
figures
.
current
(
)
671
return
callstack
[
#
callstack
]
or
lastfiguredata
672
end
673 674
local
function
get
(
category
,
tag
,
default
)
675
local
value
=
lastfiguredata
and
lastfiguredata
[
category
]
676
value
=
value
and
value
[
tag
]
677
if
not
value
or
value
=
=
"
"
or
value
=
=
true
then
678
return
default
or
"
"
679
else
680
return
value
681
end
682
end
683 684
local
function
setdimensions
(
box
)
685
local
status
=
lastfiguredata
and
lastfiguredata
.
status
686
local
used
=
lastfiguredata
and
lastfiguredata
.
used
687
if
status
and
used
then
688
local
b
=
texgetbox
(
box
)
689
local
w
=
b
.
width
690
local
h
=
b
.
height
+
b
.
depth
691
status
.
width
=
w
692
status
.
height
=
h
693
used
.
width
=
w
694
used
.
height
=
h
695
status
.
status
=
10
696
end
697
end
698 699
figures
.
get
=
get
700
figures
.
set
=
setdimensions
701 702
implement
{
name
=
"
figurestatus
"
,
actions
=
{
get
,
context
}
,
arguments
=
{
"
'status'
"
,
"
string
"
,
"
string
"
}
}
703
implement
{
name
=
"
figurerequest
"
,
actions
=
{
get
,
context
}
,
arguments
=
{
"
'request'
"
,
"
string
"
,
"
string
"
}
}
704
implement
{
name
=
"
figureused
"
,
actions
=
{
get
,
context
}
,
arguments
=
{
"
'used'
"
,
"
string
"
,
"
string
"
}
}
705 706
implement
{
name
=
"
figurefilepath
"
,
actions
=
{
get
,
file
.
dirname
,
context
}
,
arguments
=
{
"
'used'
"
,
"
'fullname'
"
}
}
707
implement
{
name
=
"
figurefilename
"
,
actions
=
{
get
,
file
.
nameonly
,
context
}
,
arguments
=
{
"
'used'
"
,
"
'fullname'
"
}
}
708
implement
{
name
=
"
figurefiletype
"
,
actions
=
{
get
,
file
.
extname
,
context
}
,
arguments
=
{
"
'used'
"
,
"
'fullname'
"
}
}
709 710
implement
{
name
=
"
figuresetdimensions
"
,
actions
=
setdimensions
,
arguments
=
"
integer
"
}
711 712
-- todo: local path or cache path
713 714
local
function
forbiddenname
(
filename
)
715
if
not
filename
or
filename
=
=
"
"
then
716
return
false
717
end
718
local
expandedfullname
=
collapsepath
(
filename
,
true
)
719
local
expandedinputname
=
collapsepath
(
file
.
addsuffix
(
environment
.
jobfilename
,
environment
.
jobfilesuffix
)
,
true
)
720
if
expandedfullname
=
=
expandedinputname
then
721
report_inclusion
(
"
skipping graphic with same name as input filename %a, enforce suffix
"
,
expandedinputname
)
722
return
true
723
end
724
local
expandedoutputname
=
collapsepath
(
codeinjections
.
getoutputfilename
(
)
,
true
)
725
if
expandedfullname
=
=
expandedoutputname
then
726
report_inclusion
(
"
skipping graphic with same name as output filename %a, enforce suffix
"
,
expandedoutputname
)
727
return
true
728
end
729
end
730 731
local
function
rejected
(
specification
)
732
if
extra_check
then
733
local
fullname
=
specification
.
fullname
734
if
fullname
and
figures_native
[
file
.
suffix
(
fullname
)
]
and
not
figures
.
guess
(
fullname
)
then
735
specification
.
comment
=
"
probably a bad file
"
736
specification
.
found
=
false
737
specification
.
error
=
true
738
report_inclusion
(
"
file %a looks bad
"
,
fullname
)
739
return
true
740
end
741
end
742
end
743 744
local
function
wipe
(
str
)
745
if
str
=
=
"
"
or
str
=
=
"
default
"
or
str
=
=
"
unknown
"
then
746
return
nil
747
else
748
return
str
749
end
750
end
751 752
local
function
register
(
askedname
,
specification
)
753
if
not
specification
then
754
specification
=
{
askedname
=
askedname
,
comment
=
"
invalid specification
"
}
755
elseif
forbiddenname
(
specification
.
fullname
)
then
756
specification
=
{
askedname
=
askedname
,
comment
=
"
forbidden name
"
}
757
elseif
specification
.
internal
then
758
-- no filecheck needed
759
specification
.
found
=
true
760
if
trace_figures
then
761
report_inclusion
(
"
format %a internally supported by engine
"
,
specification
.
format
)
762
end
763
elseif
not
rejected
(
specification
)
then
764
local
format
=
specification
.
format
765
if
format
then
766
local
conversion
=
wipe
(
specification
.
conversion
)
767
local
resolution
=
wipe
(
specification
.
resolution
)
768
local
arguments
=
wipe
(
specification
.
arguments
)
769
local
newformat
=
conversion
770
if
not
newformat
or
newformat
=
=
"
"
then
771
newformat
=
defaultformat
772
end
773
if
trace_conversion
then
774
report_inclusion
(
"
checking conversion of %a, fullname %a, old format %a, new format %a, conversion %a, resolution %a, arguments %a
"
,
775
askedname
,
776
specification
.
fullname
,
777
format
,
778
newformat
,
779
conversion
or
"
default
"
,
780
resolution
or
"
default
"
,
781
arguments
or
"
"
782
)
783
end
784
-- begin of quick hack
785
local
remapper
=
remappers
[
format
]
786
if
remapper
then
787
remapper
=
remapper
[
conversion
]
788
if
remapper
then
789
specification
=
remapper
(
specification
)
or
specification
790
format
=
specification
.
format
791
newformat
=
format
792
conversion
=
nil
793
end
794
end
795
-- end of quick hack
796
local
converter
=
(
not
remapper
)
and
(
newformat
~
=
format
or
resolution
or
arguments
)
and
converters
[
format
]
797
if
converter
then
798
local
okay
=
converter
[
newformat
]
799
if
okay
then
800
converter
=
okay
801
else
802
newformat
=
defaultformat
803
converter
=
converter
[
newformat
]
804
end
805
elseif
trace_conversion
then
806
report_inclusion
(
"
no converter for %a to %a
"
,
format
,
newformat
)
807
end
808
if
converter
then
809
-- todo: make this a function
810
--
811
-- todo: outline as helper function
812
--
813
local
oldname
=
specification
.
fullname
814
local
newpath
=
file
.
dirname
(
oldname
)
815
local
oldbase
=
file
.
basename
(
oldname
)
816
--
817
-- problem: we can have weird filenames, like a.b.c (no suffix) and a.b.c.gif
818
-- so we cannot safely remove a suffix (unless we do that for known suffixes)
819
--
820
-- local newbase = file.removesuffix(oldbase) -- assumes a known suffix
821
--
822
-- so we now have (also see *):
823
--
824
local
newbase
=
oldbase
825
--
826
local
fc
=
specification
.
cache
or
figures
.
cachepaths
.
path
827
if
fc
and
fc
~
=
"
"
and
fc
~
=
"
.
"
then
828
newpath
=
gsub
(
fc
,
"
%*
"
,
newpath
)
-- so cachedir can be "/data/cache/*"
829
else
830
newbase
=
defaultprefix
.
.
newbase
831
end
832
local
subpath
=
specification
.
subpath
or
figures
.
cachepaths
.
subpath
833
if
subpath
and
subpath
~
=
"
"
and
subpath
~
=
"
.
"
then
834
newpath
=
newpath
.
.
"
/
"
.
.
subpath
835
end
836
if
not
isdir
(
newpath
)
then
837
dir
.
makedirs
(
newpath
)
838
if
not
file
.
is_writable
(
newpath
)
then
839
if
trace_conversion
then
840
report_inclusion
(
"
path %a is not writable, forcing conversion path %a
"
,
newpath
,
"
.
"
)
841
end
842
newpath
=
"
.
"
843
end
844
end
845
local
prefix
=
specification
.
prefix
or
figures
.
cachepaths
.
prefix
846
if
prefix
and
prefix
~
=
"
"
then
847
newbase
=
prefix
.
.
newbase
848
end
849
local
hash
=
"
"
850
if
resolution
then
851
hash
=
hash
.
.
"
[r:
"
.
.
resolution
.
.
"
]
"
852
end
853
if
arguments
then
854
hash
=
hash
.
.
"
[a:
"
.
.
arguments
.
.
"
]
"
855
end
856
if
hash
~
=
"
"
then
857
newbase
=
newbase
.
.
"
_
"
.
.
md5
.
hex
(
hash
)
858
end
859
--
860
-- see *, we had:
861
--
862
-- local newbase = file.addsuffix(newbase,newformat)
863
--
864
-- but now have (result of Aditya's web image testing):
865
--
866
-- as a side effect we can now have multiple fetches with different
867
-- original figures_formats, not that it matters much (apart from older conversions
868
-- sticking around)
869
--
870
local
newbase
=
newbase
.
.
"
.
"
.
.
newformat
871
local
newname
=
file
.
join
(
newpath
,
newbase
)
872
oldname
=
collapsepath
(
oldname
)
873
newname
=
collapsepath
(
newname
)
874
local
oldtime
=
modificationtime
(
oldname
)
or
0
875
local
newtime
=
modificationtime
(
newname
)
or
0
876
if
newtime
=
=
0
or
oldtime
>
newtime
then
877
if
trace_conversion
then
878
report_inclusion
(
"
converting %a (%a) from %a to %a
"
,
askedname
,
oldname
,
format
,
newformat
)
879
end
880
converter
(
oldname
,
newname
,
resolution
or
"
"
,
arguments
or
"
"
)
881
else
882
if
trace_conversion
then
883
report_inclusion
(
"
no need to convert %a (%a) from %a to %a
"
,
askedname
,
oldname
,
format
,
newformat
)
884
end
885
end
886
if
io
.
exists
(
newname
)
and
io
.
size
(
newname
)
>
0
then
887
specification
.
foundname
=
oldname
888
specification
.
fullname
=
newname
889
specification
.
prefix
=
prefix
890
specification
.
subpath
=
subpath
891
specification
.
converted
=
true
892
format
=
newformat
893
if
not
figures_suffixes
[
format
]
then
894
-- maybe the new format is lowres.png (saves entry in suffixes)
895
-- so let's do this extra check
896
local
suffix
=
file
.
suffix
(
newformat
)
897
if
figures_suffixes
[
suffix
]
then
898
if
trace_figures
then
899
report_inclusion
(
"
using suffix %a as format for %a
"
,
suffix
,
format
)
900
end
901
format
=
suffix
902
end
903
end
904
specification
.
format
=
format
905
elseif
io
.
exists
(
oldname
)
then
906
report_inclusion
(
"
file %a is bugged
"
,
oldname
)
907
if
format
and
imagetypes
[
format
]
then
908
specification
.
fullname
=
oldname
909
end
910
specification
.
converted
=
false
911
specification
.
bugged
=
true
912
end
913
end
914
end
915
if
format
then
916
local
found
=
figures_suffixes
[
format
]
917
if
not
found
then
918
specification
.
found
=
false
919
if
trace_figures
then
920
report_inclusion
(
"
format %a is not supported
"
,
format
)
921
end
922
elseif
imagetypes
[
format
]
then
923
specification
.
found
=
true
924
if
trace_figures
then
925
report_inclusion
(
"
format %a natively supported by backend
"
,
format
)
926
end
927
else
928
specification
.
found
=
true
-- else no foo.1 mps conversion
929
if
trace_figures
then
930
report_inclusion
(
"
format %a supported by output file format
"
,
format
)
931
end
932
end
933
else
934
specification
.
askedname
=
askedname
935
specification
.
found
=
false
936
end
937
end
938
if
specification
.
found
then
939
specification
.
foundname
=
specification
.
foundname
or
specification
.
fullname
940
else
941
specification
.
foundname
=
nil
942
end
943
specification
.
badname
=
figures
.
badname
(
askedname
)
944
local
askedhash
=
f_hash_part
(
945
askedname
,
946
specification
.
conversion
or
"
default
"
,
947
specification
.
resolution
or
"
default
"
,
948
specification
.
arguments
or
"
"
949
)
950
figures_found
[
askedhash
]
=
specification
951
if
not
specification
.
found
then
952
figures
.
nofmissing
=
figures
.
nofmissing
+
1
953
end
954
return
specification
955
end
956 957
local
resolve_too
=
false
-- true
958 959
local
internalschemes
=
{
960
file
=
true
,
961
tree
=
true
,
962
dirfile
=
true
,
963
dirtree
=
true
,
964
}
965 966
local
function
locate
(
request
)
-- name, format, cache
967
-- not resolvers.cleanpath(request.name) as it fails on a!b.pdf and b~c.pdf
968
-- todo: more restricted cleanpath
969
local
askedname
=
request
.
name
or
"
"
970
local
askedcache
=
request
.
cache
971
local
askedconversion
=
request
.
conversion
972
local
askedresolution
=
request
.
resolution
973
local
askedarguments
=
request
.
arguments
974
local
askedhash
=
f_hash_part
(
975
askedname
,
976
askedconversion
or
"
default
"
,
977
askedresolution
or
"
default
"
,
978
askedarguments
or
"
"
979
)
980
local
foundname
=
figures_found
[
askedhash
]
981
if
foundname
then
982
return
foundname
983
end
984
--
985
--
986
local
askedformat
=
request
.
format
987
if
not
askedformat
or
askedformat
=
=
"
"
or
askedformat
=
=
"
unknown
"
then
988
askedformat
=
file
.
suffix
(
askedname
)
or
"
"
989
elseif
askedformat
=
=
v_auto
then
990
if
trace_figures
then
991
report_inclusion
(
"
ignoring suffix of %a
"
,
askedname
)
992
end
993
askedformat
=
"
"
994
askedname
=
file
.
removesuffix
(
askedname
)
995
end
996
-- protocol check
997
local
hashed
=
urlhashed
(
askedname
)
998
if
not
hashed
then
999
-- go on
1000
elseif
internalschemes
[
hashed
.
scheme
]
then
1001
local
path
=
hashed
.
path
1002
if
path
and
path
~
=
"
"
then
1003
askedname
=
path
1004
end
1005
else
1006
local
foundname
=
resolvers
.
findbinfile
(
askedname
)
1007
if
not
foundname
or
not
isfile
(
foundname
)
then
-- foundname can be dummy
1008
if
trace_figures
then
1009
report_inclusion
(
"
unknown url %a
"
,
askedname
)
1010
end
1011
-- url not found
1012
return
register
(
askedname
)
1013
end
1014
local
guessedformat
=
figures
.
guess
(
foundname
)
1015
if
askedformat
~
=
guessedformat
then
1016
if
trace_figures
then
1017
report_inclusion
(
"
url %a has unknown format
"
,
askedname
)
1018
end
1019
-- url found, but wrong format
1020
return
register
(
askedname
)
1021
else
1022
if
trace_figures
then
1023
report_inclusion
(
"
url %a is resolved to %a
"
,
askedname
,
foundname
)
1024
end
1025
return
register
(
askedname
,
{
1026
askedname
=
askedname
,
1027
fullname
=
foundname
,
1028
format
=
askedformat
,
1029
cache
=
askedcache
,
1030
conversion
=
askedconversion
,
1031
resolution
=
askedresolution
,
1032
arguments
=
askedarguments
,
1033
}
)
1034
end
1035
end
1036
-- we could use the hashed data instead
1037
local
askedpath
=
file
.
is_rootbased_path
(
askedname
)
1038
local
askedbase
=
file
.
basename
(
askedname
)
1039
if
askedformat
~
=
"
"
then
1040
askedformat
=
lower
(
askedformat
)
1041
if
trace_figures
then
1042
report_inclusion
(
"
forcing format %a
"
,
askedformat
)
1043
end
1044
local
format
=
figures_suffixes
[
askedformat
]
1045
if
not
format
then
1046
for
i
=
1
,
#
figures_patterns
do
1047
local
pattern
=
figures_patterns
[
i
]
1048
if
find
(
askedformat
,
pattern
[
1
]
)
then
1049
format
=
pattern
[
2
]
1050
if
trace_figures
then
1051
report_inclusion
(
"
asked format %a matches %a
"
,
askedformat
,
pattern
[
1
]
)
1052
end
1053
break
1054
end
1055
end
1056
end
1057
if
format
then
1058
local
foundname
,
quitscanning
,
forcedformat
,
internal
=
figures
.
exists
(
askedname
,
format
,
resolve_too
)
-- not askedformat
1059
if
foundname
then
1060
return
register
(
askedname
,
{
1061
askedname
=
askedname
,
1062
fullname
=
foundname
,
-- askedname,
1063
format
=
forcedformat
or
format
,
1064
cache
=
askedcache
,
1065
-- foundname = foundname, -- no
1066
conversion
=
askedconversion
,
1067
resolution
=
askedresolution
,
1068
arguments
=
askedarguments
,
1069
internal
=
internal
,
1070
}
)
1071
elseif
quitscanning
then
1072
return
register
(
askedname
)
1073
end
1074
askedformat
=
format
-- new per 2013-08-05
1075
elseif
trace_figures
then
1076
report_inclusion
(
"
unknown format %a
"
,
askedformat
)
1077
end
1078
if
askedpath
then
1079
-- path and type given, todo: strip pieces of path
1080
local
foundname
,
quitscanning
,
forcedformat
=
figures
.
exists
(
askedname
,
askedformat
,
resolve_too
)
1081
if
foundname
then
1082
return
register
(
askedname
,
{
1083
askedname
=
askedname
,
1084
fullname
=
foundname
,
-- askedname,
1085
format
=
forcedformat
or
askedformat
,
1086
cache
=
askedcache
,
1087
conversion
=
askedconversion
,
1088
resolution
=
askedresolution
,
1089
arguments
=
askedarguments
,
1090
}
)
1091
end
1092
else
1093
-- type given
1094
for
i
=
1
,
#
figure_paths
do
1095
local
path
=
resolveprefix
(
figure_paths
[
i
]
)
-- we resolve (e.g. jobfile:)
1096
local
check
=
path
.
.
"
/
"
.
.
askedname
1097
-- we pass 'true' as it can be an url as well, as the type
1098
-- is given we don't waste much time
1099
local
foundname
,
quitscanning
,
forcedformat
=
figures
.
exists
(
check
,
askedformat
,
resolve_too
)
1100
if
foundname
then
1101
return
register
(
check
,
{
1102
askedname
=
askedname
,
1103
fullname
=
foundname
,
-- check,
1104
format
=
askedformat
,
1105
cache
=
askedcache
,
1106
conversion
=
askedconversion
,
1107
resolution
=
askedresolution
,
1108
arguments
=
askedarguments
,
1109
}
)
1110
end
1111
end
1112
if
figures
.
defaultsearch
then
1113
local
check
=
resolvers
.
findfile
(
askedname
)
1114
if
check
and
check
~
=
"
"
then
1115
return
register
(
askedname
,
{
1116
askedname
=
askedname
,
1117
fullname
=
check
,
1118
format
=
askedformat
,
1119
cache
=
askedcache
,
1120
conversion
=
askedconversion
,
1121
resolution
=
askedresolution
,
1122
arguments
=
askedarguments
,
1123
}
)
1124
end
1125
end
1126
end
1127
elseif
askedpath
then
1128
if
trace_figures
then
1129
report_inclusion
(
"
using rootbased path
"
)
1130
end
1131
for
i
=
1
,
#
figures_order
do
1132
local
format
=
figures_order
[
i
]
1133
local
list
=
figures_formats
[
format
]
.
list
or
{
format
}
1134
for
j
=
1
,
#
list
do
1135
local
suffix
=
list
[
j
]
1136
local
check
=
file
.
addsuffix
(
askedname
,
suffix
)
1137
local
foundname
,
quitscanning
,
forcedformat
=
figures
.
exists
(
check
,
format
,
resolve_too
)
1138
if
foundname
then
1139
return
register
(
askedname
,
{
1140
askedname
=
askedname
,
1141
fullname
=
foundname
,
-- check,
1142
format
=
forcedformat
or
format
,
1143
cache
=
askedcache
,
1144
conversion
=
askedconversion
,
1145
resolution
=
askedresolution
,
1146
arguments
=
askedarguments
,
1147
}
)
1148
end
1149
end
1150
end
1151
else
1152
if
figures
.
preferquality
then
1153
if
trace_figures
then
1154
report_inclusion
(
"
unknown format, quality preferred
"
)
1155
end
1156
for
j
=
1
,
#
figures_order
do
1157
local
format
=
figures_order
[
j
]
1158
local
list
=
figures_formats
[
format
]
.
list
or
{
format
}
1159
for
k
=
1
,
#
list
do
1160
local
suffix
=
list
[
k
]
1161
-- local name = file.replacesuffix(askedbase,suffix)
1162
local
name
=
file
.
replacesuffix
(
askedname
,
suffix
)
1163
for
i
=
1
,
#
figure_paths
do
1164
local
path
=
resolveprefix
(
figure_paths
[
i
]
)
-- we resolve (e.g. jobfile:)
1165
local
check
=
path
.
.
"
/
"
.
.
name
1166
local
isfile
=
internalschemes
[
urlhashed
(
check
)
.
scheme
]
1167
if
not
isfile
then
1168
if
trace_figures
then
1169
report_inclusion
(
"
warning: skipping path %a
"
,
path
)
1170
end
1171
else
1172
local
foundname
,
quitscanning
,
forcedformat
=
figures
.
exists
(
check
,
format
,
resolve_too
)
-- true)
1173
if
foundname
then
1174
return
register
(
askedname
,
{
1175
askedname
=
askedname
,
1176
fullname
=
foundname
,
-- check
1177
format
=
forcedformat
or
format
,
1178
cache
=
askedcache
,
1179
conversion
=
askedconversion
,
1180
resolution
=
askedresolution
,
1181
arguments
=
askedarguments
1182
}
)
1183
end
1184
end
1185
end
1186
end
1187
end
1188
else
-- 'location'
1189
if
trace_figures
then
1190
report_inclusion
(
"
unknown format, using path strategy
"
)
1191
end
1192
for
i
=
1
,
#
figure_paths
do
1193
local
path
=
resolveprefix
(
figure_paths
[
i
]
)
-- we resolve (e.g. jobfile:)
1194
for
j
=
1
,
#
figures_order
do
1195
local
format
=
figures_order
[
j
]
1196
local
list
=
figures_formats
[
format
]
.
list
or
{
format
}
1197
for
k
=
1
,
#
list
do
1198
local
suffix
=
list
[
k
]
1199
local
check
=
path
.
.
"
/
"
.
.
file
.
replacesuffix
(
askedbase
,
suffix
)
1200
local
foundname
,
quitscanning
,
forcedformat
=
figures
.
exists
(
check
,
format
,
resolve_too
)
1201
if
foundname
then
1202
return
register
(
askedname
,
{
1203
askedname
=
askedname
,
1204
fullname
=
foudname
,
-- check,
1205
format
=
forcedformat
or
format
,
1206
cache
=
askedcache
,
1207
conversion
=
askedconversion
,
1208
resolution
=
askedresolution
,
1209
arguments
=
askedarguments
,
1210
}
)
1211
end
1212
end
1213
end
1214
end
1215
end
1216
if
figures
.
defaultsearch
then
1217
if
trace_figures
then
1218
report_inclusion
(
"
using default tex path
"
)
1219
end
1220
for
j
=
1
,
#
figures_order
do
1221
local
format
=
figures_order
[
j
]
1222
local
list
=
figures_formats
[
format
]
.
list
or
{
format
}
1223
for
k
=
1
,
#
list
do
1224
local
suffix
=
list
[
k
]
1225
local
check
=
resolvers
.
findfile
(
file
.
replacesuffix
(
askedname
,
suffix
)
)
1226
if
check
and
check
~
=
"
"
then
1227
return
register
(
askedname
,
{
1228
askedname
=
askedname
,
1229
fullname
=
check
,
1230
format
=
format
,
1231
cache
=
askedcache
,
1232
conversion
=
askedconversion
,
1233
resolution
=
askedresolution
,
1234
arguments
=
askedarguments
,
1235
}
)
1236
end
1237
end
1238
end
1239
end
1240
end
1241
return
register
(
askedname
,
{
-- these two are needed for hashing 'found'
1242
conversion
=
askedconversion
,
1243
resolution
=
askedresolution
,
1244
arguments
=
askedarguments
,
1245
}
)
1246
end
1247 1248
-- -- -- plugins -- -- --
1249 1250
function
identifiers
.
default
(
data
)
1251
local
dr
,
du
,
ds
=
data
.
request
,
data
.
used
,
data
.
status
1252
local
l
=
locate
(
dr
)
1253
local
foundname
=
l
.
foundname
1254
local
fullname
=
l
.
fullname
or
foundname
1255
if
fullname
then
1256
du
.
format
=
l
.
format
or
false
1257
du
.
fullname
=
fullname
-- can be cached
1258
ds
.
fullname
=
foundname
-- original
1259
ds
.
format
=
l
.
format
1260
ds
.
status
=
(
l
.
bugged
and
0
)
or
(
l
.
found
and
10
)
or
0
1261
end
1262
return
data
1263
end
1264 1265
function
figures
.
identify
(
data
)
1266
data
=
data
or
callstack
[
#
callstack
]
or
lastfiguredata
1267
if
data
then
1268
local
list
=
identifiers
.
list
-- defined at the end
1269
for
i
=
1
,
#
list
do
1270
local
identifier
=
list
[
i
]
1271
local
data
=
identifier
(
data
)
1272
-- if data and (not data.status and data.status.status > 0) then
1273
if
data
and
(
not
data
.
status
and
data
.
status
.
status
>
0
)
then
1274
break
1275
end
1276
end
1277
end
1278
return
data
1279
end
1280 1281
function
figures
.
exists
(
askedname
,
format
,
resolve
)
1282
return
(
existers
[
format
]
or
existers
.
generic
)
(
askedname
,
resolve
)
1283
end
1284 1285
function
figures
.
check
(
data
)
1286
data
=
data
or
callstack
[
#
callstack
]
or
lastfiguredata
1287
return
(
checkers
[
data
.
status
.
format
]
or
checkers
.
generic
)
(
data
)
1288
end
1289 1290
local
used_images
=
{
}
1291 1292
statistics
.
register
(
"
used graphics
"
,
function
(
)
1293
if
trace_usage
then
1294
local
filename
=
file
.
nameonly
(
environment
.
jobname
)
.
.
"
-figures-usage.lua
"
1295
if
next
(
figures_found
)
then
1296
local
found
=
{
}
1297
for
_
,
data
in
sortedhash
(
figures_found
)
do
1298
found
[
#
found
+
1
]
=
data
1299
for
k
,
v
in
next
,
data
do
1300
if
v
=
=
false
or
v
=
=
"
"
then
1301
data
[
k
]
=
nil
1302
end
1303
end
1304
end
1305
for
i
=
1
,
#
used_images
do
1306
local
u
=
used_images
[
i
]
1307
local
s
=
u
.
status
1308
if
s
then
1309
s
.
status
=
nil
-- doesn't say much here
1310
if
s
.
error
then
1311
u
.
used
=
{
}
-- better show that it's not used
1312
end
1313
end
1314
for
_
,
t
in
next
,
u
do
1315
for
k
,
v
in
next
,
t
do
1316
if
v
=
=
false
or
v
=
=
"
"
or
k
=
=
"
private
"
then
1317
t
[
k
]
=
nil
1318
end
1319
end
1320
end
1321
end
1322
table
.
save
(
filename
,
{
1323
found
=
found
,
1324
used
=
used_images
,
1325
}
)
1326
return
format
(
"
log saved in '%s'
"
,
filename
)
1327
else
1328
os
.
remove
(
filename
)
1329
end
1330
end
1331
end
)
1332 1333
function
figures
.
include
(
data
)
1334
data
=
data
or
callstack
[
#
callstack
]
or
lastfiguredata
1335
if
trace_usage
then
1336
used_images
[
#
used_images
+
1
]
=
data
1337
end
1338
return
(
includers
[
data
.
status
.
format
]
or
includers
.
generic
)
(
data
)
1339
end
1340 1341
function
figures
.
scale
(
data
)
-- will become lua code
1342
data
=
data
or
callstack
[
#
callstack
]
or
lastfiguredata
1343
ctx_doscalefigure
(
)
1344
return
data
1345
end
1346 1347
function
figures
.
done
(
data
)
1348
figures
.
nofprocessed
=
figures
.
nofprocessed
+
1
1349
data
=
data
or
callstack
[
#
callstack
]
or
lastfiguredata
1350
local
dr
,
du
,
ds
,
nr
=
data
.
request
,
data
.
used
,
data
.
status
,
figures
.
boxnumber
1351
local
box
=
texgetbox
(
nr
)
1352
ds
.
width
=
box
.
width
1353
ds
.
height
=
box
.
height
1354
-- somehow this fails on some of tacos files
1355
-- ds.xscale = ds.width /(du.width or 1)
1356
-- ds.yscale = ds.height/(du.height or 1)
1357
-- du.width and du.height can be false
1358
if
du
.
width
and
du
.
height
and
du
.
width
>
0
and
du
.
height
>
0
then
1359
ds
.
xscale
=
ds
.
width
/
du
.
width
1360
ds
.
yscale
=
ds
.
height
/
du
.
height
1361
elseif
du
.
xsize
and
du
.
ysize
and
du
.
xsize
>
0
and
du
.
ysize
>
0
then
1362
ds
.
xscale
=
ds
.
width
/
du
.
xsize
1363
ds
.
yscale
=
ds
.
height
/
du
.
ysize
1364
else
1365
ds
.
xscale
=
1
1366
ds
.
yscale
=
1
1367
end
1368
-- sort of redundant but can be limited
1369
ds
.
page
=
ds
.
page
or
du
.
page
or
dr
.
page
1370
return
data
1371
end
1372 1373
function
figures
.
dummy
(
data
)
1374
data
=
data
or
callstack
[
#
callstack
]
or
lastfiguredata
1375
local
dr
,
du
,
nr
=
data
.
request
,
data
.
used
,
figures
.
boxnumber
1376
local
box
=
hpack
(
node
.
new
(
"
hlist
"
)
)
-- we need to set the dir (luatex 0.60 buglet)
1377
du
.
width
=
du
.
width
or
figures
.
defaultwidth
1378
du
.
height
=
du
.
height
or
figures
.
defaultheight
1379
du
.
depth
=
du
.
depth
or
figures
.
defaultdepth
1380
-- box.dir = "TLT"
1381
box
.
width
=
du
.
width
1382
box
.
height
=
du
.
height
1383
box
.
depth
=
du
.
depth
1384
texsetbox
(
nr
,
box
)
-- hm, should be global (to be checked for consistency)
1385
end
1386 1387
-- -- -- generic -- -- --
1388 1389
function
existers
.
generic
(
askedname
,
resolve
)
1390
-- not findbinfile
1391
local
result
1392
if
hasscheme
(
askedname
)
then
1393
result
=
resolvers
.
findbinfile
(
askedname
)
1394
elseif
isfile
(
askedname
)
then
1395
result
=
askedname
1396
elseif
resolve
then
1397
result
=
resolvers
.
findbinfile
(
askedname
)
1398
end
1399
if
not
result
or
result
=
=
"
"
then
1400
result
=
false
1401
end
1402
if
trace_figures
then
1403
if
result
then
1404
report_inclusion
(
"
%a resolved to %a
"
,
askedname
,
result
)
1405
else
1406
report_inclusion
(
"
%a cannot be resolved
"
,
askedname
)
1407
end
1408
end
1409
return
result
1410
end
1411 1412
-- pdf : 0-3: 0 90 180 270
1413
-- jpeg: 0 unset 1-4: 0 90 180 270 5-8: flipped r/c
1414 1415
local
transforms
=
setmetatableindex
(
1416
{
1417
[
"
orientation-1
"
]
=
0
,
[
"
R0
"
]
=
0
,
1418
[
"
orientation-2
"
]
=
4
,
[
"
R0MH
"
]
=
4
,
1419
[
"
orientation-3
"
]
=
2
,
[
"
R180
"
]
=
2
,
1420
[
"
orientation-4
"
]
=
6
,
[
"
R0MV
"
]
=
6
,
1421
[
"
orientation-5
"
]
=
5
,
[
"
R270MH
"
]
=
5
,
1422
[
"
orientation-6
"
]
=
3
,
[
"
R90
"
]
=
3
,
1423
[
"
orientation-7
"
]
=
7
,
[
"
R90MH
"
]
=
7
,
1424
[
"
orientation-8
"
]
=
1
,
[
"
R270
"
]
=
1
,
1425
}
,
1426
function
(
t
,
k
)
-- transforms are 0 .. 7
1427
local
v
=
tonumber
(
k
)
or
0
1428
if
v
<
0
or
v
>
7
then
1429
v
=
0
1430
end
1431
t
[
k
]
=
v
1432
return
v
1433
end
1434
)
1435 1436
local
function
checktransform
(
figure
,
forced
)
1437
if
auto_transform
then
1438 1439
local
orientation
=
(
forced
~
=
"
"
and
forced
~
=
v_auto
and
forced
)
or
figure
.
orientation
or
0
1440
local
transform
=
transforms
[
"
orientation-
"
.
.
orientation
]
1441
figure
.
transform
=
transform
1442
if
odd
(
transform
)
then
1443
return
figure
.
height
,
figure
.
width
1444
else
1445
return
figure
.
width
,
figure
.
height
1446
end
1447
end
1448
end
1449 1450
local
pagecount
=
{
}
1451 1452
function
checkers
.
generic
(
data
)
1453
local
dr
,
du
,
ds
=
data
.
request
,
data
.
used
,
data
.
status
1454
local
name
=
du
.
fullname
or
"
unknown generic
"
1455
local
page
=
du
.
page
or
dr
.
page
1456
local
size
=
dr
.
size
or
"
crop
"
1457
local
color
=
dr
.
color
or
"
natural
"
1458
local
mask
=
dr
.
mask
or
"
none
"
1459
local
conversion
=
dr
.
conversion
1460
local
resolution
=
dr
.
resolution
1461
local
arguments
=
dr
.
arguments
1462
local
scanimage
=
dr
.
scanimage
or
scanimage
1463
local
userpassword
=
dr
.
userpassword
1464
local
ownerpassword
=
dr
.
ownerpassword
1465
if
not
conversion
or
conversion
=
=
"
"
then
1466
conversion
=
"
default
"
1467
end
1468
if
not
resolution
or
resolution
=
=
"
"
then
1469
resolution
=
"
default
"
1470
end
1471
if
not
arguments
or
arguments
=
=
"
"
then
1472
arguments
=
"
default
"
1473
end
1474
local
hash
=
f_hash_full
(
1475
name
,
1476
page
,
1477
size
,
1478
color
,
1479
mask
,
1480
conversion
,
1481
resolution
,
1482
arguments
1483
)
1484
--
1485
local
figure
=
figures_loaded
[
hash
]
1486
if
figure
=
=
nil
then
1487
figure
=
createimage
{
1488
filename
=
name
,
1489
page
=
page
,
1490
pagebox
=
dr
.
size
,
1491
keepopen
=
dr
.
keepopen
or
false
,
1492
userpassword
=
userpassword
,
1493
ownerpassword
=
ownerpassword
,
1494
-- visiblefilename = "", -- this prohibits the full filename ending up in the file
1495
}
1496
codeinjections
.
setfigurecolorspace
(
data
,
figure
)
1497
codeinjections
.
setfiguremask
(
data
,
figure
)
1498
if
figure
then
1499
-- new, bonus check (a bogus check in lmtx)
1500
if
page
and
page
>
1
then
1501
local
f
=
scanimage
{
1502
filename
=
name
,
1503
userpassword
=
userpassword
,
1504
ownerpassword
=
ownerpassword
,
1505
}
1506
if
f
and
f
.
page
and
f
.
pages
<
page
then
1507
report_inclusion
(
"
no page %i in %a, using page 1
"
,
page
,
name
)
1508
page
=
1
1509
figure
.
page
=
page
1510
end
1511
end
1512
-- till here
1513
local
f
,
comment
=
checkimage
(
scanimage
(
figure
)
)
1514
if
not
f
then
1515
ds
.
comment
=
comment
1516
ds
.
found
=
false
1517
ds
.
error
=
true
1518
end
1519
if
figure
.
attr
and
not
f
.
attr
then
1520
-- tricky as img doesn't allow it
1521
f
.
attr
=
figure
.
attr
1522
end
1523
if
dr
.
cmyk
=
=
v_yes
then
1524
f
.
enforcecmyk
=
true
1525
elseif
dr
.
cmyk
=
=
v_auto
and
attributes
.
colors
.
model
=
=
"
cmyk
"
then
1526
f
.
enforcecmyk
=
true
1527
end
1528
figure
=
f
1529
end
1530
local
f
,
d
=
codeinjections
.
setfigurealternative
(
data
,
figure
)
1531
figure
=
f
or
figure
1532
data
=
d
or
data
1533
figures_loaded
[
hash
]
=
figure
1534
if
trace_conversion
then
1535
report_inclusion
(
"
new graphic, using hash %a
"
,
hash
)
1536
end
1537
else
1538
if
trace_conversion
then
1539
report_inclusion
(
"
existing graphic, using hash %a
"
,
hash
)
1540
end
1541
end
1542
if
figure
then
1543
local
width
,
height
=
checktransform
(
figure
,
dr
.
transform
)
1544
--
1545
du
.
width
=
width
1546
du
.
height
=
height
1547
du
.
pages
=
figure
.
pages
1548
du
.
depth
=
figure
.
depth
or
0
1549
du
.
colordepth
=
figure
.
colordepth
or
0
1550
du
.
xresolution
=
figure
.
xres
or
0
1551
du
.
yresolution
=
figure
.
yres
or
0
1552
du
.
xsize
=
figure
.
xsize
or
0
1553
du
.
ysize
=
figure
.
ysize
or
0
1554
du
.
rotation
=
figure
.
rotation
or
0
-- in pdf multiples or 90% in jpeg 1
1555
du
.
orientation
=
figure
.
orientation
or
0
-- jpeg 1 2 3 4 (0=unset)
1556
ds
.
private
=
figure
1557
ds
.
hash
=
hash
1558
end
1559
return
data
1560
end
1561 1562
local
nofimages
=
0
1563
local
pofimages
=
{
}
1564 1565
function
figures
.
getrealpage
(
index
)
1566
return
pofimages
[
index
]
or
0
1567
end
1568 1569
local
function
updatepage
(
specification
)
1570
local
n
=
specification
.
n
1571
pofimages
[
n
]
=
pofimages
[
n
]
or
tex
.
count
.
realpageno
-- so when reused we register the first one only
1572
end
1573 1574
function
includers
.
generic
(
data
)
1575
local
dr
,
du
,
ds
=
data
.
request
,
data
.
used
,
data
.
status
1576
-- here we set the 'natural dimensions'
1577
dr
.
width
=
du
.
width
1578
dr
.
height
=
du
.
height
1579
local
hash
=
figures
.
hash
(
data
)
1580
local
figure
=
figures_used
[
hash
]
1581
-- figures.registerresource {
1582
-- filename = du.fullname,
1583
-- width = dr.width,
1584
-- height = dr.height,
1585
-- }
1586
if
figure
=
=
nil
then
1587
figure
=
ds
.
private
-- the img object
1588
if
figure
then
1589
figure
=
(
dr
.
copyimage
or
copyimage
)
(
figure
)
1590
if
figure
then
1591
figure
.
width
=
dr
.
width
or
figure
.
width
1592
figure
.
height
=
dr
.
height
or
figure
.
height
1593
end
1594
end
1595
figures_used
[
hash
]
=
figure
1596
end
1597
if
figure
then
1598
local
nr
=
figures
.
boxnumber
1599
nofimages
=
nofimages
+
1
1600
ds
.
pageindex
=
nofimages
1601
local
image
=
wrapimage
(
figure
)
1602
local
pager
=
new_latelua
{
action
=
updatepage
,
n
=
nofimages
}
1603
image
.
next
=
pager
1604
pager
.
prev
=
image
1605
local
box
=
hpack
(
image
)
1606
box
.
width
=
figure
.
width
1607
box
.
height
=
figure
.
height
1608
box
.
depth
=
0
1609
texsetbox
(
nr
,
box
)
1610
ds
.
objectnumber
=
figure
.
objnum
1611
-- indexed[figure.index] = figure
1612
ctx_relocateexternalfigure
(
)
1613
end
1614
return
data
1615
end
1616 1617
-- -- -- nongeneric -- -- --
1618 1619
local
function
checkers_nongeneric
(
data
,
command
)
-- todo: macros and context.*
1620
local
dr
,
du
,
ds
=
data
.
request
,
data
.
used
,
data
.
status
1621
local
name
=
du
.
fullname
or
"
unknown nongeneric
"
1622
local
hash
=
name
1623
if
dr
.
object
then
1624
-- hm, bugged ... waiting for an xform interface
1625
if
not
objects
.
data
[
"
FIG
"
]
[
hash
]
then
1626
if
type
(
command
)
=
=
"
function
"
then
1627
command
(
)
1628
end
1629
ctx_dosetfigureobject
(
"
FIG
"
,
hash
)
1630
end
1631
ctx_doboxfigureobject
(
"
FIG
"
,
hash
)
1632
elseif
type
(
command
)
=
=
"
function
"
then
1633
command
(
)
1634
end
1635
return
data
1636
end
1637 1638
local
function
includers_nongeneric
(
data
)
1639
return
data
1640
end
1641 1642
checkers
.
nongeneric
=
checkers_nongeneric
1643
includers
.
nongeneric
=
includers_nongeneric
1644 1645
-- -- -- mov -- -- --
1646 1647
function
checkers
.
mov
(
data
)
1648
local
dr
,
du
,
ds
=
data
.
request
,
data
.
used
,
data
.
status
1649
local
width
=
todimen
(
dr
.
width
or
figures
.
defaultwidth
)
1650
local
height
=
todimen
(
dr
.
height
or
figures
.
defaultheight
)
1651
local
foundname
=
du
.
fullname
1652
dr
.
width
,
dr
.
height
=
width
,
height
1653
du
.
width
,
du
.
height
,
du
.
foundname
=
width
,
height
,
foundname
1654
if
trace_inclusion
then
1655
report_inclusion
(
"
including movie %a, width %p, height %p
"
,
foundname
,
width
,
height
)
1656
end
1657
-- we need to push the node.write in between ... we could make a shared helper for this
1658
ctx_startfoundexternalfigure
(
width
.
.
"
sp
"
,
height
.
.
"
sp
"
)
1659
context
(
function
(
)
1660
nodeinjections
.
insertmovie
{
1661
width
=
width
,
1662
height
=
height
,
1663
factor
=
bpfactor
,
1664
[
"
repeat
"
]
=
dr
[
"
repeat
"
]
,
1665
controls
=
dr
.
controls
,
1666
preview
=
dr
.
preview
,
1667
label
=
dr
.
label
,
1668
foundname
=
foundname
,
1669
}
1670
end
)
1671
ctx_stopfoundexternalfigure
(
)
1672
return
data
1673
end
1674 1675
includers
.
mov
=
includers
.
nongeneric
1676 1677
-- -- -- mps -- -- --
1678 1679
internalschemes
.
mprun
=
true
1680 1681
-- mprun.foo.1 mprun.6 mprun:foo.2
1682 1683
local
ctx_docheckfiguremprun
=
context
.
docheckfiguremprun
1684
local
ctx_docheckfiguremps
=
context
.
docheckfiguremps
1685 1686
local
function
internal
(
askedname
)
1687
local
spec
,
mprun
,
mpnum
=
match
(
lower
(
askedname
)
,
"
mprun([:%.]?)(.-)%.(%d+)
"
)
1688
-- mpnum = tonumber(mpnum) or 0 -- can be string or number, fed to context anyway
1689
if
spec
~
=
"
"
then
1690
return
mprun
,
mpnum
1691
else
1692
return
"
"
,
mpnum
1693
end
1694
end
1695 1696
function
existers
.
mps
(
askedname
)
1697
local
mprun
,
mpnum
=
internal
(
askedname
)
1698
if
mpnum
then
1699
return
askedname
,
true
,
"
mps
"
,
true
1700
else
1701
return
existers
.
generic
(
askedname
)
1702
end
1703
end
1704 1705
function
checkers
.
mps
(
data
)
1706
local
mprun
,
mpnum
=
internal
(
data
.
used
.
fullname
)
1707
if
mpnum
then
1708
return
checkers_nongeneric
(
data
,
function
(
)
ctx_docheckfiguremprun
(
mprun
,
mpnum
)
end
)
1709
else
1710
return
checkers_nongeneric
(
data
,
function
(
)
ctx_docheckfiguremps
(
data
.
used
.
fullname
)
end
)
1711
end
1712
end
1713 1714
includers
.
mps
=
includers
.
nongeneric
1715 1716
-- -- -- tex -- -- --
1717 1718
local
ctx_docheckfiguretex
=
context
.
docheckfiguretex
1719 1720
function
existers
.
tex
(
askedname
)
1721
askedname
=
resolvers
.
findfile
(
askedname
)
1722
return
askedname
~
=
"
"
and
askedname
or
false
,
true
,
"
tex
"
,
true
1723
end
1724 1725
function
checkers
.
tex
(
data
)
1726
return
checkers_nongeneric
(
data
,
function
(
)
ctx_docheckfiguretex
(
data
.
used
.
fullname
)
end
)
1727
end
1728 1729
includers
.
tex
=
includers
.
nongeneric
1730 1731
-- -- -- buffer -- -- --
1732 1733
local
ctx_docheckfigurebuffer
=
context
.
docheckfigurebuffer
1734 1735
function
existers
.
buffer
(
askedname
)
1736
local
name
=
file
.
nameonly
(
askedname
)
1737
local
okay
=
buffers
.
exists
(
name
)
1738
return
okay
and
name
,
true
,
"
buffer
"
,
true
-- always quit scanning
1739
end
1740 1741
function
checkers
.
buffer
(
data
)
1742
return
checkers_nongeneric
(
data
,
function
(
)
ctx_docheckfigurebuffer
(
file
.
nameonly
(
data
.
used
.
fullname
)
)
end
)
1743
end
1744 1745
includers
.
buffers
=
includers
.
nongeneric
1746 1747
-- -- -- auto -- -- --
1748 1749
function
existers
.
auto
(
askedname
)
1750
local
name
=
gsub
(
askedname
,
"
.auto$
"
,
"
"
)
1751
local
format
=
figures
.
guess
(
name
)
1752
-- if format then
1753
-- report_inclusion("format guess %a for %a",format,name)
1754
-- else
1755
-- report_inclusion("format guess for %a is not possible",name)
1756
-- end
1757
return
format
and
name
,
true
,
format
1758
end
1759 1760
checkers
.
auto
=
checkers
.
generic
1761
includers
.
auto
=
includers
.
generic
1762 1763
-- -- -- cld -- -- --
1764 1765
local
ctx_docheckfigurecld
=
context
.
docheckfigurecld
1766 1767
function
existers
.
cld
(
askedname
)
1768
askedname
=
resolvers
.
findfile
(
askedname
)
1769
return
askedname
~
=
"
"
and
askedname
or
false
,
true
,
"
cld
"
,
true
1770
end
1771 1772
function
checkers
.
cld
(
data
)
1773
return
checkers_nongeneric
(
data
,
function
(
)
ctx_docheckfigurecld
(
data
.
used
.
fullname
)
end
)
1774
end
1775 1776
includers
.
cld
=
includers
.
nongeneric
1777 1778
-- -- -- converters -- -- --
1779 1780
setmetatableindex
(
converters
,
"
table
"
)
1781 1782
-- We keep this helper because it has been around for a while and therefore it can
1783
-- be a depedency in an existing workflow.
1784 1785
function
programs
.
makeoptions
(
options
)
1786
local
to
=
type
(
options
)
1787
return
(
to
=
=
"
table
"
and
concat
(
options
,
"
"
)
)
or
(
to
=
=
"
string
"
and
options
)
or
"
"
1788
end
1789 1790
function
programs
.
run
(
binary
,
argument
,
variables
)
1791
local
found
=
nil
1792
if
type
(
binary
)
=
=
"
table
"
then
1793
for
i
=
1
,
#
binary
do
1794
local
b
=
binary
[
i
]
1795
found
=
os
.
which
(
b
)
1796
if
found
then
1797
binary
=
b
1798
break
1799
end
1800
end
1801
if
not
found
then
1802
binary
=
concat
(
binary
,
"
|
"
)
1803
end
1804
elseif
binary
then
1805
found
=
os
.
which
(
match
(
binary
,
"
[%S]+
"
)
)
1806
end
1807
if
type
(
argument
)
=
=
"
table
"
then
1808
argument
=
concat
(
argument
,
"
"
)
-- for old times sake
1809
end
1810
if
not
found
then
1811
report_inclusion
(
"
program %a is not installed
"
,
binary
or
"
?
"
)
1812
elseif
not
argument
or
argument
=
=
"
"
then
1813
report_inclusion
(
"
nothing to run, no arguments for program %a
"
,
binary
)
1814
else
1815
-- no need to use the full found filename (found) .. we also don't quote the program
1816
-- name any longer as in luatex there is too much messing with these names
1817
local
command
=
format
(
[[
%s %s
]]
,
binary
,
replacetemplate
(
longtostring
(
argument
)
,
variables
)
)
1818
if
trace_conversion
or
trace_programs
then
1819
report_inclusion
(
"
running command: %s
"
,
command
)
1820
end
1821
os
.
execute
(
command
)
1822
end
1823
end
1824 1825
-- the rest of the code has been moved to grph-con.lua
1826 1827
-- -- -- bases -- -- --
1828 1829
local
bases
=
allocate
(
)
1830
figures
.
bases
=
bases
1831 1832
local
bases_list
=
nil
-- index => { basename, fullname, xmlroot }
1833
local
bases_used
=
nil
-- [basename] => { basename, fullname, xmlroot } -- pointer to list
1834
local
bases_found
=
nil
1835
local
bases_enabled
=
false
1836 1837
local
function
reset
(
)
1838
bases_list
=
allocate
(
)
1839
bases_used
=
allocate
(
)
1840
bases_found
=
allocate
(
)
1841
bases_enabled
=
false
1842
bases
.
list
=
bases_list
1843
bases
.
used
=
bases_used
1844
bases
.
found
=
bases_found
1845
end
1846 1847
reset
(
)
1848 1849
function
bases
.
use
(
basename
)
1850
if
basename
=
=
"
reset
"
then
1851
reset
(
)
1852
else
1853
basename
=
file
.
addsuffix
(
basename
,
"
xml
"
)
1854
if
not
bases_used
[
basename
]
then
1855
local
t
=
{
basename
,
nil
,
nil
}
1856
bases_used
[
basename
]
=
t
1857
bases_list
[
#
bases_list
+
1
]
=
t
1858
if
not
bases_enabled
then
1859
bases_enabled
=
true
1860
xml
.
registerns
(
"
rlx
"
,
"
http://www.pragma-ade.com/schemas/rlx
"
)
-- we should be able to do this per xml file
1861
end
1862
if
trace_bases
then
1863
report_inclusion
(
"
registering base %a
"
,
basename
)
1864
end
1865
end
1866
end
1867
end
1868 1869
implement
{
name
=
"
usefigurebase
"
,
actions
=
bases
.
use
,
arguments
=
"
string
"
}
1870 1871
local
function
bases_find
(
basename
,
askedlabel
)
1872
if
trace_bases
then
1873
report_inclusion
(
"
checking for %a in base %a
"
,
askedlabel
,
basename
)
1874
end
1875
basename
=
file
.
addsuffix
(
basename
,
"
xml
"
)
1876
local
t
=
bases_found
[
askedlabel
]
1877
if
t
=
=
nil
then
1878
local
base
=
bases_used
[
basename
]
1879
local
page
=
0
1880
if
base
[
2
]
=
=
nil
then
1881
-- no yet located
1882
for
i
=
1
,
#
figure_paths
do
1883
local
path
=
resolveprefix
(
figure_paths
[
i
]
)
-- we resolve (e.g. jobfile:)
1884
local
xmlfile
=
path
.
.
"
/
"
.
.
basename
1885
if
io
.
exists
(
xmlfile
)
then
1886
base
[
2
]
=
xmlfile
1887
base
[
3
]
=
xml
.
load
(
xmlfile
)
1888
if
trace_bases
then
1889
report_inclusion
(
"
base %a loaded
"
,
xmlfile
)
1890
end
1891
break
1892
end
1893
end
1894
end
1895
t
=
false
1896
if
base
[
2
]
and
base
[
3
]
then
-- rlx:library
1897
for
e
in
xml
.
collected
(
base
[
3
]
,
"
/(*:library|figurelibrary)/*:figure/*:label
"
)
do
1898
page
=
page
+
1
1899
if
xml
.
text
(
e
)
=
=
askedlabel
then
1900
t
=
{
1901
base
=
file
.
replacesuffix
(
base
[
2
]
,
"
pdf
"
)
,
1902
format
=
"
pdf
"
,
1903
name
=
xml
.
text
(
e
,
"
../*:file
"
)
,
-- to be checked
1904
page
=
page
,
1905
}
1906
bases_found
[
askedlabel
]
=
t
1907
if
trace_bases
then
1908
report_inclusion
(
"
figure %a found in base %a
"
,
askedlabel
,
base
[
2
]
)
1909
end
1910
return
t
1911
end
1912
end
1913
if
trace_bases
and
not
t
then
1914
report_inclusion
(
"
figure %a not found in base %a
"
,
askedlabel
,
base
[
2
]
)
1915
end
1916
end
1917
end
1918
return
t
1919
end
1920 1921
-- we can access sequential or by name
1922 1923
local
function
bases_locate
(
askedlabel
)
1924
for
i
=
1
,
#
bases_list
do
1925
local
entry
=
bases_list
[
i
]
1926
local
t
=
bases_find
(
entry
[
1
]
,
askedlabel
,
1
,
true
)
1927
if
t
then
1928
return
t
1929
end
1930
end
1931
return
false
1932
end
1933 1934
function
identifiers
.
base
(
data
)
1935
if
bases_enabled
then
1936
local
dr
,
du
,
ds
=
data
.
request
,
data
.
used
,
data
.
status
1937
local
fbl
=
bases_locate
(
dr
.
name
or
dr
.
label
)
1938
if
fbl
then
1939
du
.
page
=
fbl
.
page
1940
du
.
format
=
fbl
.
format
1941
du
.
fullname
=
fbl
.
base
1942
ds
.
fullname
=
fbl
.
name
1943
ds
.
format
=
fbl
.
format
1944
ds
.
page
=
fbl
.
page
1945
ds
.
status
=
10
1946
end
1947
end
1948
return
data
1949
end
1950 1951
bases
.
locate
=
bases_locate
1952
bases
.
find
=
bases_find
1953 1954
identifiers
.
list
=
{
1955
identifiers
.
base
,
1956
identifiers
.
default
1957
}
1958 1959
-- tracing
1960 1961
statistics
.
register
(
"
graphics processing time
"
,
function
(
)
1962
local
nofprocessed
=
figures
.
nofprocessed
1963
if
nofprocessed
>
0
then
1964
local
nofnames
,
nofbadnames
=
0
,
0
1965
for
hash
,
data
in
next
,
figures_found
do
1966
nofnames
=
nofnames
+
1
1967
if
data
.
badname
then
1968
nofbadnames
=
nofbadnames
+
1
1969
end
1970
end
1971
return
format
(
"
%s seconds including tex, %s processed images, %s unique asked, %s bad names
"
,
1972
statistics
.
elapsedtime
(
figures
)
,
nofprocessed
,
nofnames
,
nofbadnames
)
1973
else
1974
return
nil
1975
end
1976
end
)
1977 1978
-- helper
1979 1980
function
figures
.
applyratio
(
width
,
height
,
w
,
h
)
-- width and height are strings and w and h are numbers
1981
if
not
width
or
width
=
=
"
"
then
1982
if
not
height
or
height
=
=
"
"
then
1983
return
figures
.
defaultwidth
,
figures
.
defaultheight
1984
else
1985
height
=
todimen
(
height
)
1986
if
w
and
h
then
1987
return
height
*
w
/
h
,
height
1988
else
1989
return
figures
.
defaultwidth
,
height
1990
end
1991
end
1992
else
1993
width
=
todimen
(
width
)
1994
if
not
height
or
height
=
=
"
"
then
1995
if
w
and
h
then
1996
return
width
,
width
*
h
/
w
1997
else
1998
return
width
,
figures
.
defaultheight
1999
end
2000
else
2001
return
width
,
todimen
(
height
)
2002
end
2003
end
2004
end
2005 2006
-- example of simple plugins:
2007
--
2008
-- figures.converters.png = {
2009
-- png = function(oldname,newname,resolution)
2010
-- local command = string.format('gm convert -depth 1 "%s" "%s"',oldname,newname)
2011
-- logs.report(string.format("running command %s",command))
2012
-- os.execute(command)
2013
-- end,
2014
-- }
2015 2016
-- local n = "foo.pdf"
2017
-- local d = figures.getinfo(n)
2018
-- if d then
2019
-- for i=1,d.used.pages do
2020
-- local p = figures.getinfo(n,i)
2021
-- if p then
2022
-- local u = p.used
2023
-- print(u.width,u.height,u.orientation)
2024
-- end
2025
-- end
2026
-- end
2027 2028
function
figures
.
getinfo
(
name
,
page
)
2029
if
type
(
name
)
=
=
"
string
"
then
2030
name
=
{
name
=
name
,
page
=
page
}
2031
end
2032
if
name
.
name
then
2033
local
data
=
figures
.
push
(
name
)
2034
data
=
figures
.
identify
(
data
)
2035
if
data
.
status
and
data
.
status
.
status
>
0
then
2036
data
=
figures
.
check
(
data
)
2037
end
2038
figures
.
pop
(
)
2039
return
data
2040
end
2041
end
2042 2043
function
figures
.
getpdfinfo
(
name
,
page
,
metadata
)
2044
-- not that useful but as we have it for detailed inclusion we can as
2045
-- we expose it
2046
if
type
(
name
)
~
=
"
table
"
then
2047
name
=
{
name
=
name
,
page
=
page
,
metadata
=
metadata
}
2048
end
2049
return
codeinjections
.
getinfo
(
name
)
2050
end
2051 2052
-- interfacing
2053 2054
implement
{
2055
name
=
"
figure_push
"
,
2056
scope
=
"
private
"
,
2057
actions
=
figures
.
push
,
2058
arguments
=
{
2059
{
2060
{
"
name
"
}
,
2061
{
"
label
"
}
,
2062
{
"
page
"
}
,
2063
{
"
file
"
}
,
2064
{
"
size
"
}
,
2065
{
"
object
"
}
,
2066
{
"
prefix
"
}
,
2067
{
"
cache
"
}
,
2068
{
"
format
"
}
,
2069
{
"
preset
"
}
,
2070
{
"
controls
"
}
,
2071
{
"
resources
"
}
,
2072
{
"
preview
"
}
,
2073
{
"
display
"
}
,
2074
{
"
mask
"
}
,
2075
{
"
conversion
"
}
,
2076
{
"
resolution
"
}
,
2077
{
"
color
"
}
,
2078
{
"
cmyk
"
}
,
2079
{
"
arguments
"
}
,
2080
{
"
repeat
"
}
,
2081
{
"
transform
"
}
,
2082
{
"
compact
"
}
,
2083
{
"
width
"
,
"
dimen
"
}
,
2084
{
"
height
"
,
"
dimen
"
}
,
2085
{
"
userpassword
"
}
,
2086
{
"
ownerpassword
"
}
,
2087
}
2088
}
2089
}
2090 2091
-- beware, we get a number passed by default
2092 2093
implement
{
name
=
"
figure_pop
"
,
scope
=
"
private
"
,
actions
=
figures
.
pop
}
2094
implement
{
name
=
"
figure_done
"
,
scope
=
"
private
"
,
actions
=
figures
.
done
}
2095
implement
{
name
=
"
figure_dummy
"
,
scope
=
"
private
"
,
actions
=
figures
.
dummy
}
2096
implement
{
name
=
"
figure_identify
"
,
scope
=
"
private
"
,
actions
=
figures
.
identify
}
2097
implement
{
name
=
"
figure_scale
"
,
scope
=
"
private
"
,
actions
=
figures
.
scale
}
2098
implement
{
name
=
"
figure_check
"
,
scope
=
"
private
"
,
actions
=
figures
.
check
}
2099
implement
{
name
=
"
figure_include
"
,
scope
=
"
private
"
,
actions
=
figures
.
include
}
2100 2101
implement
{
2102
name
=
"
setfigurelookuporder
"
,
2103
actions
=
figures
.
setorder
,
2104
arguments
=
"
string
"
2105
}
2106 2107
implement
{
2108
name
=
"
figure_reset
"
,
2109
scope
=
"
private
"
,
2110
arguments
=
{
"
integer
"
,
"
dimen
"
,
"
dimen
"
}
,
2111
actions
=
function
(
box
,
width
,
height
)
2112
figures
.
boxnumber
=
box
2113
figures
.
defaultwidth
=
width
2114
figures
.
defaultheight
=
height
2115
end
2116
}
2117 2118
-- require("util-lib-imp-gm")
2119
--
2120
-- figures.converters.tif.pdf = function(oldname,newname,resolution)
2121
-- logs.report("graphics","using gm library to convert %a",oldname)
2122
-- utilities.graphicmagick.convert {
2123
-- inputname = oldname,
2124
-- outputname = newname,
2125
-- }
2126
-- end
2127
--
2128
-- \externalfigure[t:/sources/hakker1b.tiff]
2129 2130
-- something relatively new:
2131 2132
local
registered
=
{
}
2133 2134
local
ctx_doexternalfigurerepeat
=
context
.
doexternalfigurerepeat
2135 2136
implement
{
2137
name
=
"
figure_register_page
"
,
2138
arguments
=
"
3 strings
"
,
2139
actions
=
function
(
a
,
b
,
c
)
2140
registered
[
#
registered
+
1
]
=
{
a
,
b
,
c
}
2141
context
(
#
registered
)
2142
end
2143
}
2144 2145
implement
{
2146
name
=
"
figure_nof_registered_pages
"
,
2147
actions
=
function
(
)
2148
context
(
#
registered
)
2149
end
2150
}
2151 2152
implement
{
2153
name
=
"
figure_flush_registered_pages
"
,
2154
arguments
=
"
string
"
,
2155
actions
=
function
(
n
)
2156
local
f
=
registered
[
tonumber
(
n
)
]
2157
if
f
then
2158
ctx_doexternalfigurerepeat
(
f
[
1
]
,
f
[
2
]
,
f
[
3
]
,
n
)
2159
end
2160
end
2161
}
2162