grph-img.lua /size: 26 Kb    last modification: 2020-07-01 14:35
1
    
if
not
modules
then
modules
=
{
}
end
modules
[
'
grph-img
'
]
=
{
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
-- The jpg identification and inclusion code is based on the code in \LUATEX\ but as we
10
-- use \LUA\ we can do it a bit cleaner. We can also use some helpers for reading from
11
-- file. We could make it even more lean and mean. When all works out ok I will clean
12
-- up this code a bit as we can divert more from luatex.
13 14
local
lower
,
strip
=
string
.
lower
,
string
.
strip
15
local
round
=
math
.
round
16
local
concat
=
table
.
concat
17
local
suffixonly
=
file
.
suffix
18 19
local
newreader
=
io
.
newreader
20
local
setmetatableindex
=
table
.
setmetatableindex
21
local
setmetatablecall
=
table
.
setmetatablecall
22 23
local
graphics
=
graphics
or
{
}
24
local
identifiers
=
{
}
25
graphics
.
identifiers
=
identifiers
26 27
do
28 29
local
colorspaces
=
{
30
[
1
]
=
1
,
-- gray
31
[
3
]
=
2
,
-- rgb
32
[
4
]
=
3
,
-- cmyk
33
}
34 35
local
tags
=
{
36
[
0xC0
]
=
{
name
=
"
SOF0
"
,
}
,
-- baseline DCT
37
[
0xC1
]
=
{
name
=
"
SOF1
"
,
}
,
-- extended sequential DCT
38
[
0xC2
]
=
{
name
=
"
SOF2
"
,
}
,
-- progressive DCT
39
[
0xC3
]
=
{
name
=
"
SOF3
"
,
supported
=
false
}
,
-- lossless (sequential)
40 41
[
0xC5
]
=
{
name
=
"
SOF5
"
,
supported
=
false
}
,
-- differential sequential DCT
42
[
0xC6
]
=
{
name
=
"
SOF6
"
,
supported
=
false
}
,
-- differential progressive DCT
43
[
0xC7
]
=
{
name
=
"
SOF7
"
,
supported
=
false
}
,
-- differential lossless (sequential)
44 45
[
0xC8
]
=
{
name
=
"
JPG
"
,
}
,
-- reserved for JPEG extensions
46
[
0xC9
]
=
{
name
=
"
SOF9
"
,
}
,
-- extended sequential DCT
47
[
0xCA
]
=
{
name
=
"
SOF10
"
,
supported
=
false
}
,
-- progressive DCT
48
[
0xCB
]
=
{
name
=
"
SOF11
"
,
supported
=
false
}
,
-- lossless (sequential)
49 50
[
0xCD
]
=
{
name
=
"
SOF13
"
,
supported
=
false
}
,
-- differential sequential DCT
51
[
0xCE
]
=
{
name
=
"
SOF14
"
,
supported
=
false
}
,
-- differential progressive DCT
52
[
0xCF
]
=
{
name
=
"
SOF15
"
,
supported
=
false
}
,
-- differential lossless (sequential)
53 54
[
0xC4
]
=
{
name
=
"
DHT
"
}
,
-- define Huffman table(s)
55 56
[
0xCC
]
=
{
name
=
"
DAC
"
}
,
-- define arithmetic conditioning table
57 58
[
0xD0
]
=
{
name
=
"
RST0
"
,
zerolength
=
true
}
,
-- restart
59
[
0xD1
]
=
{
name
=
"
RST1
"
,
zerolength
=
true
}
,
-- restart
60
[
0xD2
]
=
{
name
=
"
RST2
"
,
zerolength
=
true
}
,
-- restart
61
[
0xD3
]
=
{
name
=
"
RST3
"
,
zerolength
=
true
}
,
-- restart
62
[
0xD4
]
=
{
name
=
"
RST4
"
,
zerolength
=
true
}
,
-- restart
63
[
0xD5
]
=
{
name
=
"
RST5
"
,
zerolength
=
true
}
,
-- restart
64
[
0xD6
]
=
{
name
=
"
RST6
"
,
zerolength
=
true
}
,
-- restart
65
[
0xD7
]
=
{
name
=
"
RST7
"
,
zerolength
=
true
}
,
-- restart
66 67
[
0xD8
]
=
{
name
=
"
SOI
"
,
zerolength
=
true
}
,
-- start of image
68
[
0xD9
]
=
{
name
=
"
EOI
"
,
zerolength
=
true
}
,
-- end of image
69
[
0xDA
]
=
{
name
=
"
SOS
"
}
,
-- start of scan
70
[
0xDB
]
=
{
name
=
"
DQT
"
}
,
-- define quantization tables
71
[
0xDC
]
=
{
name
=
"
DNL
"
}
,
-- define number of lines
72
[
0xDD
]
=
{
name
=
"
DRI
"
}
,
-- define restart interval
73
[
0xDE
]
=
{
name
=
"
DHP
"
}
,
-- define hierarchical progression
74
[
0xDF
]
=
{
name
=
"
EXP
"
}
,
-- expand reference image(s)
75 76
[
0xE0
]
=
{
name
=
"
APP0
"
}
,
-- application marker, used for JFIF
77
[
0xE1
]
=
{
name
=
"
APP1
"
}
,
-- application marker
78
[
0xE2
]
=
{
name
=
"
APP2
"
}
,
-- application marker
79
[
0xE3
]
=
{
name
=
"
APP3
"
}
,
-- application marker
80
[
0xE4
]
=
{
name
=
"
APP4
"
}
,
-- application marker
81
[
0xE5
]
=
{
name
=
"
APP5
"
}
,
-- application marker
82
[
0xE6
]
=
{
name
=
"
APP6
"
}
,
-- application marker
83
[
0xE7
]
=
{
name
=
"
APP7
"
}
,
-- application marker
84
[
0xE8
]
=
{
name
=
"
APP8
"
}
,
-- application marker
85
[
0xE9
]
=
{
name
=
"
APP9
"
}
,
-- application marker
86
[
0xEA
]
=
{
name
=
"
APP10
"
}
,
-- application marker
87
[
0xEB
]
=
{
name
=
"
APP11
"
}
,
-- application marker
88
[
0xEC
]
=
{
name
=
"
APP12
"
}
,
-- application marker
89
[
0xED
]
=
{
name
=
"
APP13
"
}
,
-- application marker
90
[
0xEE
]
=
{
name
=
"
APP14
"
}
,
-- application marker, used by Adobe
91
[
0xEF
]
=
{
name
=
"
APP15
"
}
,
-- application marker
92 93
[
0xF0
]
=
{
name
=
"
JPG0
"
}
,
-- reserved for JPEG extensions
94
[
0xFD
]
=
{
name
=
"
JPG13
"
}
,
-- reserved for JPEG extensions
95
[
0xFE
]
=
{
name
=
"
COM
"
}
,
-- comment
96 97
[
0x01
]
=
{
name
=
"
TEM
"
,
zerolength
=
true
}
,
-- temporary use
98
}
99 100
-- More can be found in http://www.exif.org/Exif2-2.PDF but basically we have
101
-- good old tiff tags here.
102 103
local
function
read_APP1_Exif
(
f
,
xres
,
yres
,
orientation
)
-- untested
104
local
position
=
false
105
local
littleendian
=
false
106
-- endian II|MM
107
while
true
do
108
position
=
f
:
getposition
(
)
109
local
b
=
f
:
readbyte
(
)
110
if
b
=
=
0
then
111
-- next one
112
elseif
b
=
=
0x4D
and
f
:
readbyte
(
)
=
=
0x4D
then
-- M
113
-- big endian
114
break
115
elseif
b
=
=
0x49
and
f
:
readbyte
(
)
=
=
0x49
then
-- I
116
-- little endian
117
littleendian
=
true
118
break
119
else
120
-- warning "bad exif data"
121
return
xres
,
yres
,
orientation
122
end
123
end
124
-- version
125
local
version
=
littleendian
and
f
:
readcardinal2le
(
)
or
f
:
readcardinal2
(
)
126
if
version
~
=
42
then
127
return
xres
,
yres
,
orientation
128
end
129
-- offset to records
130
local
offset
=
littleendian
and
f
:
readcardinal4le
(
)
or
f
:
readcardinal4
(
)
131
if
not
offset
then
132
return
xres
,
yres
,
orientation
133
end
134
f
:
setposition
(
position
+
offset
)
135
local
entries
=
littleendian
and
f
:
readcardinal2le
(
)
or
f
:
readcardinal2
(
)
136
if
not
entries
or
entries
=
=
0
then
137
return
xres
,
yres
,
orientation
138
end
139
local
x_res
,
y_res
,
x_res_ms
,
y_res_ms
,
x_temp
,
y_temp
140
local
res_unit
,
res_unit_ms
141
for
i
=
1
,
entries
do
142
local
tag
=
littleendian
and
f
:
readcardinal2le
(
)
or
f
:
readcardinal2
(
)
143
local
kind
=
littleendian
and
f
:
readcardinal2le
(
)
or
f
:
readcardinal2
(
)
144
local
size
=
littleendian
and
f
:
readcardinal4le
(
)
or
f
:
readcardinal4
(
)
145
local
value
=
0
146
local
num
=
0
147
local
den
=
0
148
if
kind
=
=
1
or
kind
=
=
7
then
-- byte | undefined
149
value
=
f
:
readbyte
(
)
150
f
:
skip
(
3
)
151
elseif
kind
=
=
3
or
kind
=
=
8
then
-- (un)signed short
152
value
=
littleendian
and
f
:
readcardinal2le
(
)
or
f
:
readcardinal2
(
)
153
f
:
skip
(
2
)
154
elseif
kind
=
=
4
or
kind
=
=
9
then
-- (un)signed long
155
value
=
littleendian
and
f
:
readcardinal4le
(
)
or
f
:
readcardinal4
(
)
156
elseif
kind
=
=
5
or
kind
=
=
10
then
-- (s)rational
157
local
offset
=
littleendian
and
f
:
readcardinal4le
(
)
or
f
:
readcardinal4
(
)
158
local
saved
=
f
:
getposition
(
)
159
f
:
setposition
(
position
+
offset
)
160
num
=
littleendian
and
f
:
readcardinal4le
(
)
or
f
:
readcardinal4
(
)
161
den
=
littleendian
and
f
:
readcardinal4le
(
)
or
f
:
readcardinal4
(
)
162
f
:
setposition
(
saved
)
163
else
-- 2 -- ascii
164
f
:
skip
(
4
)
165
end
166
if
tag
=
=
274
then
-- orientation
167
orientation
=
value
168
elseif
tag
=
=
282
then
-- x resolution
169
if
den
~
=
0
then
170
x_res
=
num
/
den
171
end
172
elseif
tag
=
=
283
then
-- y resolution
173
if
den
~
=
0
then
174
y_res
=
num
/
den
175
end
176
elseif
tag
=
=
296
then
-- resolution unit
177
if
value
=
=
2
then
178
res_unit
=
1
179
elseif
value
=
=
3
then
180
res_unit
=
2
.
54
181
end
182
elseif
tag
=
=
0x5110
then
-- pixel unit
183
res_unit_ms
=
value
=
=
1
184
elseif
tag
=
=
0x5111
then
-- x pixels per unit
185
x_res_ms
=
value
186
elseif
tag
=
=
0x5112
then
-- y pixels per unit
187
y_res_ms
=
value
188
end
189
end
190
if
x_res
and
y_res
and
res_unit
and
res_unit
>
0
then
191
x_temp
=
round
(
x_res
*
res_unit
)
192
y_temp
=
round
(
y_res
*
res_unit
)
193
elseif
x_res_ms
and
y_res_ms
and
res_unit_ms
then
194
x_temp
=
round
(
x_res_ms
*
0
.
0254
)
-- in meters
195
y_temp
=
round
(
y_res_ms
*
0
.
0254
)
-- in meters
196
end
197
if
x_temp
and
a_temp
and
x_temp
>
0
and
y_temp
>
0
then
198
if
(
x_temp
~
=
x_res
or
y_temp
~
=
y_res
)
and
x_res
~
=
0
and
y_res
~
=
0
then
199
-- exif resolution differs from already found resolution
200
elseif
x_temp
=
=
1
or
y_temp
=
=
1
then
201
-- exif resolution is kind of weird
202
else
203
return
x_temp
,
y_temp
,
orientation
204
end
205
end
206
return
round
(
xres
)
,
round
(
yres
)
,
orientation
207
end
208 209
function
identifiers
.
jpg
(
filename
,
method
)
210
local
specification
=
{
211
filename
=
filename
,
212
filetype
=
"
jpg
"
,
213
}
214
if
not
filename
or
filename
=
=
"
"
then
215
specification
.
error
=
"
invalid filename
"
216
return
specification
-- error
217
end
218
local
f
=
newreader
(
filename
,
method
)
219
if
not
f
then
220
specification
.
error
=
"
unable to open file
"
221
return
specification
-- error
222
end
223
specification
.
xres
=
0
224
specification
.
yres
=
0
225
specification
.
orientation
=
1
226
specification
.
totalpages
=
1
227
specification
.
pagenum
=
1
228
specification
.
length
=
0
229
local
banner
=
f
:
readcardinal2
(
)
230
if
banner
~
=
0xFFD8
then
231
specification
.
error
=
"
no jpeg file
"
232
return
specification
-- error
233
end
234
local
xres
=
0
235
local
yres
=
0
236
local
orientation
=
1
237
local
okay
=
false
238
local
filesize
=
f
:
getsize
(
)
-- seek end
239
-- local majorversion = pdfmajorversion and pdfmajorversion() or 1
240
-- local minorversion = pdfminorversion and pdfminorversion() or 7
241
while
f
:
getposition
(
)
<
filesize
do
242
local
b
=
f
:
readbyte
(
)
243
if
not
b
then
244
break
245
elseif
b
~
=
0xFF
then
246
if
not
okay
then
247
-- or check for size
248
specification
.
error
=
"
incomplete file
"
249
end
250
break
251
end
252
local
category
=
f
:
readbyte
(
)
253
local
position
=
f
:
getposition
(
)
254
local
length
=
0
255
local
tagdata
=
tags
[
category
]
256
if
not
tagdata
then
257
specification
.
error
=
"
invalid tag
"
258
break
259
elseif
tagdata
.
supported
=
=
false
then
260
specification
.
error
=
"
unsupported
"
.
.
tagdata
.
comment
261
break
262
end
263
local
name
=
tagdata
.
name
264
if
name
=
=
"
SOF0
"
or
name
=
=
"
SOF1
"
or
name
=
=
"
SOF2
"
then
265
--
266
-- It makes no sense to support pdf < 1.3 so we now just omit this
267
-- test. There is no need to polute the code with useless tests.
268
--
269
-- if majorversion == 1 and minorversion <= 2 then
270
-- specification.error = "no progressive DCT in PDF <= 1.2"
271
-- break
272
-- end
273
length
=
f
:
readcardinal2
(
)
274
specification
.
colordepth
=
f
:
readcardinal
(
)
275
specification
.
ysize
=
f
:
readcardinal2
(
)
276
specification
.
xsize
=
f
:
readcardinal2
(
)
277
specification
.
colorspace
=
colorspaces
[
f
:
readcardinal
(
)
]
278
if
not
specification
.
colorspace
then
279
specification
.
error
=
"
unsupported color space
"
280
break
281
end
282
okay
=
true
283
elseif
name
=
=
"
APP0
"
then
284
length
=
f
:
readcardinal2
(
)
285
if
length
>
6
then
286
local
format
=
f
:
readstring
(
5
)
287
if
format
=
=
"
JFIF\000
"
then
288
f
:
skip
(
2
)
289
units
=
f
:
readcardinal
(
)
290
xres
=
f
:
readcardinal2
(
)
291
yres
=
f
:
readcardinal2
(
)
292
if
units
=
=
1
then
293
-- pixels per inch
294
if
xres
=
=
1
or
yres
=
=
1
then
295
-- warning
296
end
297
elseif
units
=
=
2
then
298
-- pixels per cm */
299
xres
=
xres
*
2
.
54
300
yres
=
yres
*
2
.
54
301
else
302
xres
=
0
303
yres
=
0
304
end
305
end
306
end
307
elseif
name
=
=
"
APP1
"
then
308
length
=
f
:
readcardinal2
(
)
309
if
length
>
7
then
310
local
format
=
f
:
readstring
(
5
)
311
if
format
=
=
"
Exif\000
"
then
312
xres
,
yres
,
orientation
=
read_APP1_Exif
(
f
,
xres
,
yres
,
orientation
)
313
end
314
end
315
elseif
not
tagdata
.
zerolength
then
316
length
=
f
:
readcardinal2
(
)
317
end
318
if
length
>
0
then
319
f
:
setposition
(
position
+
length
)
320
end
321
end
322
f
:
close
(
)
323
if
not
okay
then
324
specification
.
error
=
specification
.
error
or
"
invalid file
"
325
elseif
not
specification
.
error
then
326
if
xres
=
=
0
and
yres
~
=
0
then
327
xres
=
yres
328
end
329
if
yres
=
=
0
and
xres
~
=
0
then
330
yres
=
xres
331
end
332
end
333
specification
.
xres
=
xres
334
specification
.
yres
=
yres
335
specification
.
orientation
=
orientation
336
specification
.
length
=
filesize
337
return
specification
338
end
339 340
end
341 342
do
343 344
local
function
read_boxhdr
(
specification
,
f
)
345
local
size
=
f
:
readcardinal4
(
)
346
local
kind
=
f
:
readstring
(
4
)
347
if
kind
then
348
kind
=
strip
(
lower
(
kind
)
)
349
else
350
kind
=
"
"
351
end
352
if
size
=
=
1
then
353
size
=
f
:
readcardinal4
(
)
*
0xFFFF0000
+
f
:
readcardinal4
(
)
354
end
355
if
size
=
=
0
and
kind
~
=
"
jp2c
"
then
-- move this
356
specification
.
error
=
"
invalid size
"
357
end
358
return
kind
,
size
359
end
360 361
local
function
scan_ihdr
(
specification
,
f
)
362
specification
.
ysize
=
f
:
readcardinal4
(
)
363
specification
.
xsize
=
f
:
readcardinal4
(
)
364
f
:
skip
(
2
)
-- nc
365
specification
.
colordepth
=
f
:
readcardinal
(
)
+
1
366
f
:
skip
(
3
)
-- c unkc ipr
367
end
368 369
local
function
scan_resc_resd
(
specification
,
f
)
370
local
vr_n
=
f
:
readcardinal2
(
)
371
local
vr_d
=
f
:
readcardinal2
(
)
372
local
hr_n
=
f
:
readcardinal2
(
)
373
local
hr_d
=
f
:
readcardinal2
(
)
374
local
vr_e
=
f
:
readcardinal
(
)
375
local
hr_e
=
f
:
readcardinal
(
)
376
specification
.
xres
=
math
.
round
(
(
hr_n
/
hr_d
)
*
math
.
exp
(
hr_e
*
math
.
log
(
10
.
0
)
)
*
0
.
0254
)
377
specification
.
yres
=
math
.
round
(
(
vr_n
/
vr_d
)
*
math
.
exp
(
vr_e
*
math
.
log
(
10
.
0
)
)
*
0
.
0254
)
378
end
379 380
local
function
scan_res
(
specification
,
f
,
last
)
381
local
pos
=
f
:
getposition
(
)
382
while
true
do
383
local
kind
,
size
=
read_boxhdr
(
specification
,
f
)
384
pos
=
pos
+
size
385
if
kind
=
=
"
resc
"
then
386
if
specification
.
xres
=
=
0
and
specification
.
yres
=
=
0
then
387
scan_resc_resd
(
specification
,
f
)
388
if
f
:
getposition
(
)
~
=
pos
then
389
specification
.
error
=
"
invalid resc
"
390
return
391
end
392
end
393
elseif
tpos
=
=
"
resd
"
then
394
scan_resc_resd
(
specification
,
f
)
395
if
f
:
getposition
(
)
~
=
pos
then
396
specification
.
error
=
"
invalid resd
"
397
return
398
end
399
elseif
pos
>
last
then
400
specification
.
error
=
"
invalid res
"
401
return
402
elseif
pos
=
=
last
then
403
break
404
end
405
if
specification
.
error
then
406
break
407
end
408
f
:
setposition
(
pos
)
409
end
410
end
411 412
local
function
scan_jp2h
(
specification
,
f
,
last
)
413
local
okay
=
false
414
local
pos
=
f
:
getposition
(
)
415
while
true
do
416
local
kind
,
size
=
read_boxhdr
(
specification
,
f
)
417
pos
=
pos
+
size
418
if
kind
=
=
"
ihdr
"
then
419
scan_ihdr
(
specification
,
f
)
420
if
f
:
getposition
(
)
~
=
pos
then
421
specification
.
error
=
"
invalid ihdr
"
422
return
false
423
end
424
okay
=
true
425
elseif
kind
=
=
"
res
"
then
426
scan_res
(
specification
,
f
,
pos
)
427
elseif
pos
>
last
then
428
specification
.
error
=
"
invalid jp2h
"
429
return
false
430
elseif
pos
=
=
last
then
431
break
432
end
433
if
specification
.
error
then
434
break
435
end
436
f
:
setposition
(
pos
)
437
end
438
return
okay
439
end
440 441
function
identifiers
.
jp2
(
filename
,
method
)
442
local
specification
=
{
443
filename
=
filename
,
444
filetype
=
"
jp2
"
,
445
}
446
if
not
filename
or
filename
=
=
"
"
then
447
specification
.
error
=
"
invalid filename
"
448
return
specification
-- error
449
end
450
local
f
=
newreader
(
filename
,
method
)
451
if
not
f
then
452
specification
.
error
=
"
unable to open file
"
453
return
specification
-- error
454
end
455
specification
.
xres
=
0
456
specification
.
yres
=
0
457
specification
.
orientation
=
1
458
specification
.
totalpages
=
1
459
specification
.
pagenum
=
1
460
specification
.
length
=
0
461
local
xres
=
0
462
local
yres
=
0
463
local
orientation
=
1
464
local
okay
=
false
465
local
filesize
=
f
:
getsize
(
)
-- seek end
466
--
467
local
pos
=
0
468
-- signature
469
local
kind
,
size
=
read_boxhdr
(
specification
,
f
)
470
pos
=
pos
+
size
471
f
:
setposition
(
pos
)
472
-- filetype
473
local
kind
,
size
=
read_boxhdr
(
specification
,
f
)
474
if
kind
~
=
"
ftyp
"
then
475
specification
.
error
=
"
missing ftyp box
"
476
return
specification
477
end
478
pos
=
pos
+
size
479
f
:
setposition
(
pos
)
480
while
not
okay
do
481
local
kind
,
size
=
read_boxhdr
(
specification
,
f
)
482
pos
=
pos
+
size
483
if
kind
=
=
"
jp2h
"
then
484
okay
=
scan_jp2h
(
specification
,
f
,
pos
)
485
elseif
kind
=
=
"
jp2c
"
and
not
okay
then
486
specification
.
error
=
"
no ihdr box found
"
487
return
specification
488
end
489
f
:
setposition
(
pos
)
490
end
491
--
492
f
:
close
(
)
493
if
not
okay
then
494
specification
.
error
=
"
invalid file
"
495
elseif
not
specification
.
error
then
496
if
xres
=
=
0
and
yres
~
=
0
then
497
xres
=
yres
498
end
499
if
yres
=
=
0
and
xres
~
=
0
then
500
yres
=
xres
501
end
502
end
503
specification
.
xres
=
xres
504
specification
.
yres
=
yres
505
specification
.
orientation
=
orientation
506
specification
.
length
=
filesize
507
return
specification
508
end
509 510
end
511 512
do
513 514
-- 0 = gray "image b"
515
-- 2 = rgb "image c"
516
-- 3 = palette "image c" + "image i"
517
-- 4 = gray + alpha "image b"
518
-- 6 = rgb + alpha "image c"
519 520
-- for i=1,length/3 do
521
-- palette[i] = f:readstring3)
522
-- end
523 524
local
function
grab
(
t
,
f
,
once
)
525
if
once
then
526
for
i
=
1
,
#
t
do
527
local
l
=
t
[
i
]
528
f
:
setposition
(
l
.
offset
)
529
t
[
i
]
=
f
:
readstring
(
l
.
length
)
530
end
531
local
data
=
concat
(
t
)
532
-- t wiped in caller
533
return
data
534
else
535
local
data
=
{
}
536
for
i
=
1
,
#
t
do
537
local
l
=
t
[
i
]
538
f
:
setposition
(
l
.
offset
)
539
data
[
i
]
=
f
:
readstring
(
l
.
length
)
540
end
541
return
concat
(
data
)
542
end
543
end
544 545
function
identifiers
.
png
(
filename
,
method
)
546
local
specification
=
{
547
filename
=
filename
,
548
filetype
=
"
png
"
,
549
}
550
if
not
filename
or
filename
=
=
"
"
then
551
specification
.
error
=
"
invalid filename
"
552
return
specification
-- error
553
end
554
local
f
=
newreader
(
filename
,
method
)
555
if
not
f
then
556
specification
.
error
=
"
unable to open file
"
557
return
specification
-- error
558
end
559
specification
.
xres
=
0
560
specification
.
yres
=
0
561
specification
.
orientation
=
1
562
specification
.
totalpages
=
1
563
specification
.
pagenum
=
1
564
specification
.
offset
=
0
565
specification
.
length
=
0
566
local
filesize
=
f
:
getsize
(
)
-- seek end
567
local
tables
=
{
}
568
local
banner
=
f
:
readstring
(
8
)
569
if
banner
~
=
"
\137PNG\013\010\026\010
"
then
570
specification
.
error
=
"
no png file
"
571
return
specification
-- error
572
end
573
while
true
do
574
local
position
=
f
:
getposition
(
)
575
if
position
>
=
filesize
then
576
break
577
end
578
local
length
=
f
:
readcardinal4
(
)
579
if
not
length
then
580
break
581
end
582
local
kind
=
f
:
readstring
(
4
)
583
if
kind
then
584
kind
=
lower
(
kind
)
585
else
586
break
587
end
588
if
kind
=
=
"
ihdr
"
then
-- metadata
589
specification
.
xsize
=
f
:
readcardinal4
(
)
590
specification
.
ysize
=
f
:
readcardinal4
(
)
591
specification
.
colordepth
=
f
:
readcardinal
(
)
592
specification
.
colorspace
=
f
:
readcardinal
(
)
593
specification
.
compression
=
f
:
readcardinal
(
)
594
specification
.
filter
=
f
:
readcardinal
(
)
595
specification
.
interlace
=
f
:
readcardinal
(
)
596
tables
[
kind
]
=
true
597
elseif
kind
=
=
"
iend
"
then
598
tables
[
kind
]
=
true
599
break
600
elseif
kind
=
=
"
phys
"
then
601
local
x
=
f
:
readcardinal4
(
)
602
local
y
=
f
:
readcardinal4
(
)
603
local
u
=
f
:
readcardinal
(
)
604
if
u
=
=
1
then
-- meters
605
-- x = round(0.0254 * x)
606
-- y = round(0.0254 * y)
607
end
608
specification
.
xres
=
x
609
specification
.
yres
=
y
610
tables
[
kind
]
=
true
611
elseif
kind
=
=
"
idat
"
or
kind
=
=
"
plte
"
or
kind
=
=
"
gama
"
or
kind
=
=
"
trns
"
then
612
local
t
=
tables
[
kind
]
613
if
not
t
then
614
t
=
setmetatablecall
(
grab
)
615
tables
[
kind
]
=
t
616
end
617
t
[
#
t
+
1
]
=
{
618
offset
=
f
:
getposition
(
)
,
619
length
=
length
,
620
}
621
else
622
tables
[
kind
]
=
true
623
end
624
f
:
setposition
(
position
+
length
+
12
)
-- #size #kind #crc
625
end
626
f
:
close
(
)
627
specification
.
tables
=
tables
628
return
specification
629
end
630 631
end
632 633
do
634 635
local
function
gray
(
t
,
k
)
636
local
v
=
0
637
t
[
k
]
=
v
638
return
v
639
end
640 641
local
function
rgb
(
t
,
k
)
642
local
v
=
{
0
,
0
,
0
}
643
t
[
k
]
=
v
644
return
v
645
end
646 647
local
function
cmyk
(
t
,
k
)
648
local
v
=
{
0
,
0
,
0
,
0
}
649
t
[
k
]
=
v
650
return
v
651
end
652 653
function
identifiers
.
bitmap
(
specification
)
654
local
xsize
=
specification
.
xsize
or
0
655
local
ysize
=
specification
.
ysize
or
0
656
local
width
=
specification
.
width
or
xsize
*
65536
657
local
height
=
specification
.
height
or
ysize
*
65536
658
local
colordepth
=
specification
.
colordepth
or
1
-- 1 .. 2
659
local
colorspace
=
specification
.
colorspace
or
1
-- 1 .. 3
660
local
pixel
=
false
661
local
data
=
specification
.
data
662
local
mask
=
specification
.
mask
663
local
index
=
specification
.
index
664
if
colorspace
=
=
1
or
colorspace
=
=
"
gray
"
then
665
pixel
=
gray
666
colorspace
=
1
667
elseif
colorspace
=
=
2
or
colorspace
=
=
"
rgb
"
then
668
pixel
=
rgb
669
colorspace
=
2
670
elseif
colorspace
=
=
3
or
colorspace
=
=
"
cmyk
"
then
671
pixel
=
cmyk
672
colorspace
=
3
673
else
674
return
675
end
676
if
colordepth
=
=
8
then
677
colordepth
=
1
678
elseif
colordepth
=
=
16
then
679
colordepth
=
2
680
end
681
if
colordepth
>
1
then
682
-- not yet
683
return
684
end
685
if
data
then
686
-- assume correct data
687
else
688
data
=
{
}
689
for
i
=
1
,
ysize
do
690
data
[
i
]
=
setmetatableindex
(
pixel
)
691
end
692
end
693
if
mask
=
=
true
then
694
mask
=
{
}
695
for
i
=
1
,
ysize
do
696
mask
[
i
]
=
setmetatableindex
(
gray
)
697
end
698
end
699
if
index
then
700
index
=
setmetatableindex
(
pixel
)
701
end
702
local
specification
=
{
703
xsize
=
xsize
,
704
ysize
=
ysize
,
705
width
=
width
,
706
height
=
height
,
707
colordepth
=
colordepth
,
708
colorspace
=
colorspace
,
709
data
=
data
,
710
mask
=
mask
,
711
index
=
index
,
712
}
713
return
specification
714
end
715 716
end
717 718
function
graphics
.
identify
(
filename
,
filetype
)
719
local
identify
=
filetype
and
identifiers
[
filetype
]
720
if
identify
then
721
return
identify
(
filename
)
722
end
723
local
identify
=
identifiers
[
suffixonly
(
filename
)
]
724
if
identify
then
725
identify
=
identify
(
filename
)
726
else
727
identify
=
{
728
filename
=
filename
,
729
filetype
=
filetype
,
730
error
=
"
identification failed
"
,
731
}
732
end
733
-- inspect(identify)
734
return
identify
735
end
736 737
-- inspect(identifiers.jpg("t:/sources/hacker.jpg"))
738
-- inspect(identifiers.png("t:/sources/mill.png"))
739