img2pdf/src/img2pdf_test.py

6707 lines
262 KiB
Python
Executable file

#!/usr/bin/env python3
import sys
import numpy
import scipy.signal
import zlib
import struct
import subprocess
import pytest
import re
import pikepdf
import hashlib
import img2pdf
import os
from io import BytesIO
from PIL import Image
import decimal
from packaging.version import parse as parse_version
import warnings
import json
import pathlib
HAVE_MUTOOL = True
try:
ver = subprocess.check_output(["mutool", "-v"], stderr=subprocess.STDOUT)
m = re.fullmatch(r"mutool version ([0-9.]+)\n", ver.decode("utf8"))
if m is None:
HAVE_MUTOOL = False
else:
if parse_version(m.group(1)) < parse_version("1.10.0"):
HAVE_MUTOOL = False
except FileNotFoundError:
HAVE_MUTOOL = False
if not HAVE_MUTOOL:
warnings.warn("mutool >= 1.10.0 not available, skipping checks...")
HAVE_PDFIMAGES_CMYK = True
try:
ver = subprocess.check_output(["pdfimages", "-v"], stderr=subprocess.STDOUT)
m = re.fullmatch(r"pdfimages version ([0-9.]+)", ver.split(b"\n")[0].decode("utf8"))
if m is None:
HAVE_PDFIMAGES_CMYK = False
else:
if parse_version(m.group(1)) < parse_version("0.42.0"):
HAVE_PDFIMAGES_CMYK = False
except FileNotFoundError:
HAVE_PDFIMAGES_CMYK = False
if not HAVE_PDFIMAGES_CMYK:
warnings.warn("pdfimages >= 0.42.0 not available, skipping CMYK checks...")
HAVE_IMAGEMAGICK_MODERN = True
try:
ver = subprocess.check_output(["convert", "-version"], stderr=subprocess.STDOUT)
m = re.fullmatch(
r"Version: ImageMagick ([0-9.]+-[0-9]+) .*", ver.split(b"\n")[0].decode("utf8")
)
if m is None:
HAVE_IMAGEMAGICK_MODERN = False
else:
if parse_version(m.group(1)) < parse_version("6.9.10-12"):
HAVE_IMAGEMAGICK_MODERN = False
except FileNotFoundError:
HAVE_IMAGEMAGICK_MODERN = False
except subprocess.CalledProcessError:
HAVE_IMAGEMAGICK_MODERN = False
if not HAVE_IMAGEMAGICK_MODERN:
warnings.warn("imagemagick >= 6.9.10-12 not available, skipping certain checks...")
HAVE_JP2 = True
try:
ver = subprocess.check_output(
["identify", "-list", "format"], stderr=subprocess.STDOUT
)
found = False
for line in ver.split(b"\n"):
if re.match(rb"\s+JP2\* JP2\s+rw-\s+JPEG-2000 File Format Syntax", line):
found = True
break
if not found:
HAVE_JP2 = False
except FileNotFoundError:
HAVE_JP2 = False
except subprocess.CalledProcessError:
HAVE_JP2 = False
if not HAVE_JP2:
warnings.warn("imagemagick has no jpeg 2000 support, skipping certain checks...")
###############################################################################
# HELPER FUNCTIONS #
###############################################################################
def find_closest_palette_color(color, palette):
if color.ndim == 0:
idx = (numpy.abs(palette - color)).argmin()
else:
# naive distance function by computing the euclidean distance in RGB space
idx = ((palette - color) ** 2).sum(axis=-1).argmin()
return palette[idx]
def floyd_steinberg(img, palette):
result = numpy.array(img, copy=True)
for y in range(result.shape[0]):
for x in range(result.shape[1]):
oldpixel = result[y, x]
newpixel = find_closest_palette_color(oldpixel, palette)
quant_error = oldpixel - newpixel
result[y, x] = newpixel
if x + 1 < result.shape[1]:
result[y, x + 1] += quant_error * 7 / 16
if y + 1 < result.shape[0]:
result[y + 1, x - 1] += quant_error * 3 / 16
result[y + 1, x] += quant_error * 5 / 16
if x + 1 < result.shape[1] and y + 1 < result.shape[0]:
result[y + 1, x + 1] += quant_error * 1 / 16
return result
def convolve_rgba(img, kernel):
return numpy.stack(
(
scipy.signal.convolve2d(img[:, :, 0], kernel, "same"),
scipy.signal.convolve2d(img[:, :, 1], kernel, "same"),
scipy.signal.convolve2d(img[:, :, 2], kernel, "same"),
scipy.signal.convolve2d(img[:, :, 3], kernel, "same"),
),
axis=-1,
)
def rgb2gray(img):
result = numpy.zeros((60, 60), dtype=numpy.dtype("int64"))
count = 0
for y in range(img.shape[0]):
for x in range(img.shape[1]):
clin = sum(img[y, x] * [0.2126, 0.7152, 0.0722]) / 0xFFFF
if clin <= 0.0031308:
csrgb = 12.92 * clin
else:
csrgb = 1.055 * clin ** (1 / 2.4) - 0.055
result[y, x] = csrgb * 0xFFFF
count += 1
# if count == 24:
# raise Exception(result[y, x])
return result
def palettize(img, pal):
result = numpy.zeros((img.shape[0], img.shape[1]), dtype=numpy.dtype("int64"))
for y in range(img.shape[0]):
for x in range(img.shape[1]):
for i, col in enumerate(pal):
if numpy.array_equal(img[y, x], col):
result[y, x] = i
break
else:
raise Exception()
return result
# we cannot use zlib.compress() because different compressors may compress the
# same data differently, for example by using different optimizations on
# different architectures:
# https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/thread/R7GD4L5Z6HELCDAL2RDESWR2F3ZXHWVX/
#
# to make the compressed representation of the uncompressed data bit-by-bit
# identical on all platforms we make use of the compression method 0, that is,
# no compression at all :)
def compress(data):
# two-byte zlib header (rfc1950)
# common header for lowest compression level
# bits 0-3: Compression info, base-2 logarithm of the LZ77 window size,
# minus eight -- 7 indicates a 32K window size
# bits 4-7: Compression method -- 8 is deflate
# bits 8-9: Compression level -- 0 is fastest
# bit 10: preset dictionary -- 0 is none
# bits 11-15: check bits so that the 16-bit unsigned integer stored in MSB
# order is a multiple of 31
result = b"\x78\x01"
# content is stored in deflate format (rfc1951)
# maximum chunk size is the largest 16 bit unsigned integer
chunksize = 0xFFFF
for i in range(0, len(data), chunksize):
# bits 0-4 are unused
# bits 5-6 indicate compression method -- 0 is no compression
# bit 7 indicates the last chunk
if i * chunksize < len(data) - chunksize:
result += b"\x00"
else:
# last chunck
result += b"\x01"
chunk = data[i : i + chunksize]
# the chunk length as little endian 16 bit unsigned integer
result += struct.pack("<H", len(chunk))
# the one's complement of the chunk length
# one's complement is all bits inverted which is the result of
# xor with 0xffff for a 16 bit unsigned integer
result += struct.pack("<H", len(chunk) ^ 0xFFFF)
result += chunk
# adler32 checksum of the uncompressed data as big endian 32 bit unsigned
# integer
result += struct.pack(">I", zlib.adler32(data))
return result
def write_png(data, path, bitdepth, colortype, palette=None, iccp=None):
with open(str(path), "wb") as f:
f.write(b"\x89PNG\r\n\x1A\n")
# PNG image type Colour type Allowed bit depths
# Greyscale 0 1, 2, 4, 8, 16
# Truecolour 2 8, 16
# Indexed-colour 3 1, 2, 4, 8
# Greyscale with alpha 4 8, 16
# Truecolour with alpha 6 8, 16
block = b"IHDR" + struct.pack(
">IIBBBBB",
data.shape[1], # width
data.shape[0], # height
bitdepth, # bitdepth
colortype, # colortype
0, # compression
0, # filtertype
0, # interlaced
)
f.write(
struct.pack(">I", len(block) - 4)
+ block
+ struct.pack(">I", zlib.crc32(block))
)
if iccp is not None:
with open(iccp, "rb") as infh:
iccdata = infh.read()
block = b"iCCP"
block += b"icc\0" # arbitrary profile name
block += b"\0" # compression method (deflate)
block += zlib.compress(iccdata)
f.write(
struct.pack(">I", len(block) - 4)
+ block
+ struct.pack(">I", zlib.crc32(block))
)
if palette is not None:
block = b"PLTE"
for col in palette:
block += struct.pack(">BBB", col[0], col[1], col[2])
f.write(
struct.pack(">I", len(block) - 4)
+ block
+ struct.pack(">I", zlib.crc32(block))
)
raw = b""
for y in range(data.shape[0]):
raw += b"\0"
if bitdepth == 16:
raw += data[y].astype(">u2").tobytes()
elif bitdepth == 8:
raw += data[y].astype(">u1").tobytes()
elif bitdepth in [4, 2, 1]:
valsperbyte = 8 // bitdepth
for x in range(0, data.shape[1], valsperbyte):
val = 0
for j in range(valsperbyte):
if x + j >= data.shape[1]:
break
val |= (data[y, x + j].astype(">u2") & (2 ** bitdepth - 1)) << (
(valsperbyte - j - 1) * bitdepth
)
raw += struct.pack(">B", val)
else:
raise Exception()
compressed = compress(raw)
block = b"IDAT" + compressed
f.write(
struct.pack(">I", len(compressed))
+ block
+ struct.pack(">I", zlib.crc32(block))
)
block = b"IEND"
f.write(struct.pack(">I", 0) + block + struct.pack(">I", zlib.crc32(block)))
def compare(im1, im2, exact, icc, cmyk):
if exact:
if cmyk:
raise Exception("cmyk cannot be exact")
elif icc:
raise Exception("icc cannot be exact")
else:
subprocess.check_call(
[
"compare",
"-metric",
"AE",
im1,
im2,
"null:",
]
)
else:
iccargs = []
if icc:
iccargs = ["-profile", "/usr/share/color/icc/sRGB.icc"]
psnr = subprocess.run(
["compare"]
+ iccargs
+ [
"-metric",
"PSNR",
im1,
im2,
"null:",
],
check=False,
stderr=subprocess.PIPE,
).stderr
assert psnr != b"0"
psnr = float(psnr.strip(b"0"))
assert psnr != 0 # or otherwise we would use the exact variant
assert psnr > 50
def compare_ghostscript(tmpdir, img, pdf, gsdevice="png16m", exact=True, icc=False):
if gsdevice in ["png16m", "pnggray"]:
ext = "png"
elif gsdevice in ["tiff24nc", "tiff32nc", "tiff48nc"]:
ext = "tiff"
else:
raise Exception("unknown gsdevice: " + gsdevice)
subprocess.check_call(
[
"gs",
"-dQUIET",
"-dNOPAUSE",
"-dBATCH",
"-sDEVICE=" + gsdevice,
"-r96",
"-sOutputFile=" + str(tmpdir / "gs-") + "%00d." + ext,
str(pdf),
]
)
compare(str(img), str(tmpdir / "gs-1.") + ext, exact, icc, False)
(tmpdir / ("gs-1." + ext)).unlink()
def compare_poppler(tmpdir, img, pdf, exact=True, icc=False):
subprocess.check_call(
["pdftocairo", "-r", "96", "-png", str(pdf), str(tmpdir / "poppler")]
)
compare(str(img), str(tmpdir / "poppler-1.png"), exact, icc, False)
(tmpdir / "poppler-1.png").unlink()
def compare_mupdf(tmpdir, img, pdf, exact=True, cmyk=False):
if not HAVE_MUTOOL:
return
if cmyk:
out = tmpdir / "mupdf.pam"
subprocess.check_call(
["mutool", "draw", "-r", "96", "-c", "cmyk", "-o", str(out), str(pdf)]
)
else:
out = tmpdir / "mupdf.png"
subprocess.check_call(
["mutool", "draw", "-r", "96", "-png", "-o", str(out), str(pdf)]
)
compare(str(img), str(out), exact, False, cmyk)
out.unlink()
def compare_pdfimages_jpg(tmpdir, img, pdf):
subprocess.check_call(["pdfimages", "-j", str(pdf), str(tmpdir / "images")])
assert img.read_bytes() == (tmpdir / "images-000.jpg").read_bytes()
(tmpdir / "images-000.jpg").unlink()
def compare_pdfimages_cmyk(tmpdir, img, pdf):
if not HAVE_PDFIMAGES_CMYK:
return
subprocess.check_call(["pdfimages", "-j", str(pdf), str(tmpdir / "images")])
assert img.read_bytes() == (tmpdir / "images-000.jpg").read_bytes()
(tmpdir / "images-000.jpg").unlink()
def compare_pdfimages_jp2(tmpdir, img, pdf):
subprocess.check_call(["pdfimages", "-jp2", str(pdf), str(tmpdir / "images")])
assert img.read_bytes() == (tmpdir / "images-000.jp2").read_bytes()
(tmpdir / "images-000.jp2").unlink()
def compare_pdfimages_tiff(tmpdir, img, pdf):
subprocess.check_call(["pdfimages", "-tiff", str(pdf), str(tmpdir / "images")])
subprocess.check_call(
["compare", "-metric", "AE", str(img), str(tmpdir / "images-000.tif"), "null:"]
)
(tmpdir / "images-000.tif").unlink()
def compare_pdfimages_png(tmpdir, img, pdf, exact=True, icc=False):
subprocess.check_call(["pdfimages", "-png", str(pdf), str(tmpdir / "images")])
if exact:
if icc:
raise Exception("not exact with icc")
subprocess.check_call(
[
"compare",
"-metric",
"AE",
str(img),
str(tmpdir / "images-000.png"),
"null:",
]
)
else:
if icc:
psnr = subprocess.run(
[
"compare",
"-metric",
"PSNR",
"(",
"-profile",
"/usr/share/color/icc/ghostscript/srgb.icc",
"-depth",
"8",
str(img),
")",
str(tmpdir / "images-000.png"),
"null:",
],
check=False,
stderr=subprocess.PIPE,
).stderr
else:
psnr = subprocess.run(
[
"compare",
"-metric",
"PSNR",
str(img),
str(tmpdir / "images-000.png"),
"null:",
],
check=False,
stderr=subprocess.PIPE,
).stderr
assert psnr != b"0"
psnr = float(psnr.strip(b"0"))
assert psnr != 0 # or otherwise we would use the exact variant
assert psnr > 50
(tmpdir / "images-000.png").unlink()
def tiff_header_for_ccitt(width, height, img_size, ccitt_group=4):
# Quick and dirty TIFF header builder from
# https://stackoverflow.com/questions/2641770
tiff_header_struct = "<" + "2s" + "h" + "l" + "h" + "hhll" * 8 + "h"
return struct.pack(
# fmt: off
tiff_header_struct,
b'II', # Byte order indication: Little indian
42, # Version number (always 42)
8, # Offset to first IFD
8, # Number of tags in IFD
256, 4, 1, width, # ImageWidth, LONG, 1, width
257, 4, 1, height, # ImageLength, LONG, 1, lenght
258, 3, 1, 1, # BitsPerSample, SHORT, 1, 1
259, 3, 1, ccitt_group, # Compression, SHORT, 1, 4 = CCITT Group 4
262, 3, 1, 1, # Threshholding, SHORT, 1, 0 = WhiteIsZero
273, 4, 1, struct.calcsize(
tiff_header_struct), # StripOffsets, LONG, 1, len of header
278, 4, 1, height, # RowsPerStrip, LONG, 1, lenght
279, 4, 1, img_size, # StripByteCounts, LONG, 1, size of image
0
# last IFD
# fmt: on
)
pixel_R = [
[1, 1, 1, 0],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 0],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
]
pixel_G = [
[0, 1, 1, 0],
[1, 0, 0, 1],
[1, 0, 0, 0],
[1, 0, 1, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[0, 1, 1, 0],
]
pixel_B = [
[1, 1, 1, 0],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 0],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 0],
]
def alpha_value():
# gaussian kernel with sigma=3
kernel = numpy.array(
[
[0.011362, 0.014962, 0.017649, 0.018648, 0.017649, 0.014962, 0.011362],
[0.014962, 0.019703, 0.02324, 0.024556, 0.02324, 0.019703, 0.014962],
[0.017649, 0.02324, 0.027413, 0.028964, 0.027413, 0.02324, 0.017649],
[0.018648, 0.024556, 0.028964, 0.030603, 0.028964, 0.024556, 0.018648],
[0.017649, 0.02324, 0.027413, 0.028964, 0.027413, 0.02324, 0.017649],
[0.014962, 0.019703, 0.02324, 0.024556, 0.02324, 0.019703, 0.014962],
[0.011362, 0.014962, 0.017649, 0.018648, 0.017649, 0.014962, 0.011362],
],
float,
)
# constructs a 2D array of a circle with a width of 36
circle = list()
offsets_36 = [14, 11, 9, 7, 6, 5, 4, 3, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0]
for offs in offsets_36 + offsets_36[::-1]:
circle.append([0] * offs + [1] * (len(offsets_36) - offs) * 2 + [0] * offs)
alpha = numpy.zeros((60, 60, 4), dtype=numpy.dtype("int64"))
# draw three circles
for (xpos, ypos, color) in [
(12, 3, [0xFFFF, 0, 0, 0xFFFF]),
(21, 21, [0, 0xFFFF, 0, 0xFFFF]),
(3, 21, [0, 0, 0xFFFF, 0xFFFF]),
]:
for x, row in enumerate(circle):
for y, pos in enumerate(row):
if pos:
alpha[y + ypos, x + xpos] += color
alpha = numpy.clip(alpha, 0, 0xFFFF)
alpha = convolve_rgba(alpha, kernel)
# draw letters
for y, row in enumerate(pixel_R):
for x, pos in enumerate(row):
if pos:
alpha[13 + y, 28 + x] = [0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF]
for y, row in enumerate(pixel_G):
for x, pos in enumerate(row):
if pos:
alpha[39 + y, 40 + x] = [0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF]
for y, row in enumerate(pixel_B):
for x, pos in enumerate(row):
if pos:
alpha[39 + y, 15 + x] = [0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF]
return alpha
def icc_profile():
PCS = (0.96420288, 1.0, 0.82490540) # D50 illuminant constants
# approximate X,Y,Z values for white, red, green and blue
white = (0.95, 1.0, 1.09)
red = (0.44, 0.22, 0.014)
green = (0.39, 0.72, 0.1)
blue = (0.14, 0.06, 0.71)
getxyz = lambda v: (round(65536 * v[0]), round(65536 * v[1]), round(65536 * v[2]))
header = (
# header
+4 * b"\0" # cmmsignatures
+ 4 * b"\0" # version
+ b"mntr" # device class
+ b"RGB " # color space
+ b"XYZ " # PCS
+ 12 * b"\0" # datetime
+ b"\x61\x63\x73\x70" # static signature
+ 4 * b"\0" # platform
+ 4 * b"\0" # flags
+ 4 * b"\0" # device manufacturer
+ 4 * b"\0" # device model
+ 8 * b"\0" # device attributes
+ 4 * b"\0" # rendering intents
+ struct.pack(">III", *getxyz(PCS))
+ 4 * b"\0" # creator
+ 16 * b"\0" # identifier
+ 28 * b"\0" # reserved
)
def pad4(s):
if len(s) % 4 == 0:
return s
else:
return s + b"\x00" * (4 - len(s) % 4)
tagdata = [
b"desc\x00\x00\x00\x00" + struct.pack(">I", 5) + b"fake" + 79 * b"\x00",
b"XYZ \x00\x00\x00\x00" + struct.pack(">III", *getxyz(white)),
# by mixing up red, green and blue, we create a test profile
b"XYZ \x00\x00\x00\x00" + struct.pack(">III", *getxyz(blue)), # red
b"XYZ \x00\x00\x00\x00" + struct.pack(">III", *getxyz(red)), # green
b"XYZ \x00\x00\x00\x00" + struct.pack(">III", *getxyz(green)), # blue
# by only supplying two values, we create the most trivial "curve",
# where the remaining values will be linearly interpolated between them
b"curv\x00\x00\x00\x00" + struct.pack(">IHH", 2, 0, 65535),
b"text\x00\x00\x00\x00" + b"no copyright, use freely" + 1 * b"\x00",
]
table = [
(b"desc", 0),
(b"wtpt", 1),
(b"rXYZ", 2),
(b"gXYZ", 3),
(b"bXYZ", 4),
# we use the same curve for all three channels, so the same offset is referenced
(b"rTRC", 5),
(b"gTRC", 5),
(b"bTRC", 5),
(b"cprt", 6),
]
offset = (
lambda n: 4 # total size
+ len(header) # header length
+ 4 # number table entries
+ len(table) * 12 # table length
+ sum([len(pad4(s)) for s in tagdata[:n]])
)
table = struct.pack(">I", len(table)) + b"".join(
[t + struct.pack(">II", offset(o), len(tagdata[o])) for t, o in table]
)
data = b"".join([pad4(s) for s in tagdata])
data = (
struct.pack(">I", 4 + len(header) + len(table) + len(data))
+ header
+ table
+ data
)
return data
###############################################################################
# INPUT FIXTURES #
###############################################################################
@pytest.fixture(scope="session")
def alpha():
return alpha_value()
@pytest.fixture(scope="session")
def tmp_alpha_png(tmp_path_factory, alpha):
tmp_alpha_png = tmp_path_factory.mktemp("alpha_png") / "alpha.png"
write_png(alpha, str(tmp_alpha_png), 16, 6)
assert (
hashlib.md5(tmp_alpha_png.read_bytes()).hexdigest()
== "600bb4cffb039a022cec6ed55537deba"
)
yield tmp_alpha_png
tmp_alpha_png.unlink()
@pytest.fixture(scope="session")
def tmp_gray1_png(tmp_path_factory, alpha):
normal16 = alpha[:, :, 0:3]
gray16 = rgb2gray(normal16)
tmp_gray1_png = tmp_path_factory.mktemp("gray1_png") / "gray1.png"
write_png(
floyd_steinberg(gray16, numpy.arange(2) / 0x1 * 0xFFFF) / 0xFFFF * 0x1,
str(tmp_gray1_png),
1,
0,
)
assert (
hashlib.md5(tmp_gray1_png.read_bytes()).hexdigest()
== "dd2c528152d34324747355b73495a115"
)
yield tmp_gray1_png
tmp_gray1_png.unlink()
@pytest.fixture(scope="session")
def tmp_gray2_png(tmp_path_factory, alpha):
normal16 = alpha[:, :, 0:3]
gray16 = rgb2gray(normal16)
tmp_gray2_png = tmp_path_factory.mktemp("gray2_png") / "gray2.png"
write_png(
floyd_steinberg(gray16, numpy.arange(4) / 0x3 * 0xFFFF) / 0xFFFF * 0x3,
str(tmp_gray2_png),
2,
0,
)
assert (
hashlib.md5(tmp_gray2_png.read_bytes()).hexdigest()
== "68e614f4e6a85053d47098dad0ca3976"
)
yield tmp_gray2_png
tmp_gray2_png.unlink()
@pytest.fixture(scope="session")
def tmp_gray4_png(tmp_path_factory, alpha):
normal16 = alpha[:, :, 0:3]
gray16 = rgb2gray(normal16)
tmp_gray4_png = tmp_path_factory.mktemp("gray4_png") / "gray4.png"
write_png(
floyd_steinberg(gray16, numpy.arange(16) / 0xF * 0xFFFF) / 0xFFFF * 0xF,
str(tmp_gray4_png),
4,
0,
)
assert (
hashlib.md5(tmp_gray4_png.read_bytes()).hexdigest()
== "ff04a6fea88133eb77bbb748692ae0fd"
)
yield tmp_gray4_png
tmp_gray4_png.unlink()
@pytest.fixture(scope="session")
def tmp_gray8_png(tmp_path_factory, alpha):
normal16 = alpha[:, :, 0:3]
gray16 = rgb2gray(normal16)
tmp_gray8_png = tmp_path_factory.mktemp("gray8_png") / "gray8.png"
write_png(gray16 / 0xFFFF * 0xFF, tmp_gray8_png, 8, 0)
assert (
hashlib.md5(tmp_gray8_png.read_bytes()).hexdigest()
== "90b4ed9123f295dda7fde499744dede7"
)
yield tmp_gray8_png
tmp_gray8_png.unlink()
@pytest.fixture(scope="session")
def tmp_gray16_png(tmp_path_factory, alpha):
normal16 = alpha[:, :, 0:3]
gray16 = rgb2gray(normal16)
tmp_gray16_png = tmp_path_factory.mktemp("gray16_png") / "gray16.png"
write_png(gray16, str(tmp_gray16_png), 16, 0)
assert (
hashlib.md5(tmp_gray16_png.read_bytes()).hexdigest()
== "f76153d2e72fada11d934c32c8168a57"
)
yield tmp_gray16_png
tmp_gray16_png.unlink()
@pytest.fixture(scope="session")
def tmp_inverse_png(tmp_path_factory, alpha):
normal16 = alpha[:, :, 0:3]
tmp_inverse_png = tmp_path_factory.mktemp("inverse_png") / "inverse.png"
write_png(0xFF - normal16 / 0xFFFF * 0xFF, str(tmp_inverse_png), 8, 2)
assert (
hashlib.md5(tmp_inverse_png.read_bytes()).hexdigest()
== "0a7d57dc09c4d8fd1ad3511b116c7dfa"
)
yield tmp_inverse_png
tmp_inverse_png.unlink()
@pytest.fixture(scope="session")
def tmp_icc_profile(tmp_path_factory):
tmp_icc_profile = tmp_path_factory.mktemp("icc_profile") / "fake.icc"
tmp_icc_profile.write_bytes(icc_profile())
yield tmp_icc_profile
tmp_icc_profile.unlink()
@pytest.fixture(scope="session")
def tmp_icc_png(tmp_path_factory, alpha, tmp_icc_profile):
normal16 = alpha[:, :, 0:3]
tmp_icc_png = tmp_path_factory.mktemp("icc_png") / "icc.png"
write_png(
normal16 / 0xFFFF * 0xFF,
str(tmp_icc_png),
8,
2,
iccp=str(tmp_icc_profile),
)
assert (
hashlib.md5(tmp_icc_png.read_bytes()).hexdigest()
== "bf25f673c1617f5f9353b2a043747655"
)
yield tmp_icc_png
tmp_icc_png.unlink()
@pytest.fixture(scope="session")
def tmp_normal16_png(tmp_path_factory, alpha):
normal16 = alpha[:, :, 0:3]
tmp_normal16_png = tmp_path_factory.mktemp("normal16_png") / "normal16.png"
write_png(normal16, str(tmp_normal16_png), 16, 2)
assert (
hashlib.md5(tmp_normal16_png.read_bytes()).hexdigest()
== "820dd30a2566775fc64c110e8ac65c7e"
)
yield tmp_normal16_png
tmp_normal16_png.unlink()
@pytest.fixture(scope="session")
def tmp_normal_png(tmp_path_factory, alpha):
normal16 = alpha[:, :, 0:3]
tmp_normal_png = tmp_path_factory.mktemp("normal_png") / "normal.png"
write_png(normal16 / 0xFFFF * 0xFF, str(tmp_normal_png), 8, 2)
assert (
hashlib.md5(tmp_normal_png.read_bytes()).hexdigest()
== "bc30c705f455991cd04be1c298063002"
)
yield tmp_normal_png
tmp_normal_png.unlink()
@pytest.fixture(scope="session")
def tmp_palette1_png(tmp_path_factory, alpha):
normal16 = alpha[:, :, 0:3]
tmp_palette1_png = tmp_path_factory.mktemp("palette1_png") / "palette1.png"
# don't choose black and white or otherwise imagemagick will classify the
# image as bilevel with 8/1-bit depth instead of palette with 8-bit color
# don't choose gray colors or otherwise imagemagick will classify the
# image as grayscale
pal1 = numpy.array(
[[0x01, 0x02, 0x03], [0xFE, 0xFD, 0xFC]], dtype=numpy.dtype("int64")
)
write_png(
palettize(
floyd_steinberg(normal16, pal1 * 0xFFFF / 0xFF) / 0xFFFF * 0xFF, pal1
),
str(tmp_palette1_png),
1,
3,
pal1,
)
assert (
hashlib.md5(tmp_palette1_png.read_bytes()).hexdigest()
== "3d065f731540e928fb730b3233e4e8a7"
)
yield tmp_palette1_png
tmp_palette1_png.unlink()
@pytest.fixture(scope="session")
def tmp_palette2_png(tmp_path_factory, alpha):
normal16 = alpha[:, :, 0:3]
tmp_palette2_png = tmp_path_factory.mktemp("palette2_png") / "palette2.png"
# choose values slightly off red, lime and blue because otherwise
# imagemagick will classify the image as Depth: 8/1-bit
pal2 = numpy.array(
[[0, 0, 0], [0xFE, 0, 0], [0, 0xFE, 0], [0, 0, 0xFE]],
dtype=numpy.dtype("int64"),
)
write_png(
palettize(
floyd_steinberg(normal16, pal2 * 0xFFFF / 0xFF) / 0xFFFF * 0xFF, pal2
),
str(tmp_palette2_png),
2,
3,
pal2,
)
assert (
hashlib.md5(tmp_palette2_png.read_bytes()).hexdigest()
== "0b0d4412c28da26163a622d218ee02ca"
)
yield tmp_palette2_png
tmp_palette2_png.unlink()
@pytest.fixture(scope="session")
def tmp_palette4_png(tmp_path_factory, alpha):
normal16 = alpha[:, :, 0:3]
tmp_palette4_png = tmp_path_factory.mktemp("palette4_png") / "palette4.png"
# windows 16 color palette
pal4 = numpy.array(
[
[0x00, 0x00, 0x00],
[0x80, 0x00, 0x00],
[0x00, 0x80, 0x00],
[0x80, 0x80, 0x00],
[0x00, 0x00, 0x80],
[0x80, 0x00, 0x80],
[0x00, 0x80, 0x80],
[0xC0, 0xC0, 0xC0],
[0x80, 0x80, 0x80],
[0xFF, 0x00, 0x00],
[0x00, 0xFF, 0x00],
[0xFF, 0x00, 0x00],
[0x00, 0xFF, 0x00],
[0xFF, 0x00, 0xFF],
[0x00, 0xFF, 0x00],
[0xFF, 0xFF, 0xFF],
],
dtype=numpy.dtype("int64"),
)
write_png(
palettize(
floyd_steinberg(normal16, pal4 * 0xFFFF / 0xFF) / 0xFFFF * 0xFF, pal4
),
str(tmp_palette4_png),
4,
3,
pal4,
)
assert (
hashlib.md5(tmp_palette4_png.read_bytes()).hexdigest()
== "163f6d7964b80eefa0dc6a48cb7315dd"
)
yield tmp_palette4_png
tmp_palette4_png.unlink()
@pytest.fixture(scope="session")
def tmp_palette8_png(tmp_path_factory, alpha):
normal16 = alpha[:, :, 0:3]
tmp_palette8_png = tmp_path_factory.mktemp("palette8_png") / "palette8.png"
# create a 256 color palette by first writing 16 shades of gray
# and then writing an array of RGB colors with 6, 8 and 5 levels
# for red, green and blue, respectively
pal8 = numpy.zeros((256, 3), dtype=numpy.dtype("int64"))
i = 0
for gray in range(15, 255, 15):
pal8[i] = [gray, gray, gray]
i += 1
for red in 0, 0x33, 0x66, 0x99, 0xCC, 0xFF:
for green in 0, 0x24, 0x49, 0x6D, 0x92, 0xB6, 0xDB, 0xFF:
for blue in 0, 0x40, 0x80, 0xBF, 0xFF:
pal8[i] = [red, green, blue]
i += 1
assert i == 256
write_png(
palettize(
floyd_steinberg(normal16, pal8 * 0xFFFF / 0xFF) / 0xFFFF * 0xFF, pal8
),
str(tmp_palette8_png),
8,
3,
pal8,
)
assert (
hashlib.md5(tmp_palette8_png.read_bytes()).hexdigest()
== "8847bb734eba0e2d85e3f97fc2849dd4"
)
yield tmp_palette8_png
tmp_palette8_png.unlink()
@pytest.fixture(scope="session")
def jpg_img(tmp_path_factory, tmp_normal_png):
in_img = tmp_path_factory.mktemp("jpg") / "in.jpg"
subprocess.check_call(["convert", str(tmp_normal_png), str(in_img)])
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "JPEG", str(identify)
assert (
identify[0]["image"]["formatDescription"]
== "Joint Photographic Experts Group JFIF format"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/jpeg", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert "resolution" not in identify[0]["image"]
assert identify[0]["image"].get("units") == "Undefined", str(identify)
assert identify[0]["image"].get("type") == "TrueColor", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) == "Undefined", str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "JPEG", str(identify)
assert identify[0]["image"].get("orientation") == "Undefined", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("jpeg:colorspace") == "2"
), str(identify)
yield in_img
in_img.unlink()
@pytest.fixture(scope="session")
def jpg_rot_img(tmp_path_factory, tmp_normal_png):
in_img = tmp_path_factory.mktemp("jpg_rot") / "in.jpg"
subprocess.check_call(["convert", str(tmp_normal_png), str(in_img)])
subprocess.check_call(
["exiftool", "-overwrite_original", "-all=", str(in_img), "-n"]
)
subprocess.check_call(
[
"exiftool",
"-overwrite_original",
"-Orientation=6",
"-XResolution=96",
"-YResolution=96",
"-n",
str(in_img),
]
)
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "JPEG", str(identify)
assert (
identify[0]["image"]["formatDescription"]
== "Joint Photographic Experts Group JFIF format"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/jpeg", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("resolution") == {"x": 96, "y": 96}
assert identify[0]["image"].get("units") == "PixelsPerInch", str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "TrueColor", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "JPEG", str(identify)
assert identify[0]["image"].get("orientation") == "RightTop", str(identify)
yield in_img
in_img.unlink()
@pytest.fixture(scope="session")
def jpg_cmyk_img(tmp_path_factory, tmp_normal_png):
in_img = tmp_path_factory.mktemp("jpg_cmyk") / "in.jpg"
subprocess.check_call(
["convert", str(tmp_normal_png), "-colorspace", "cmyk", str(in_img)]
)
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "JPEG", str(identify)
assert (
identify[0]["image"]["formatDescription"]
== "Joint Photographic Experts Group JFIF format"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/jpeg", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "CMYK", str(identify)
assert identify[0]["image"].get("type") == "ColorSeparation", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "JPEG", str(identify)
yield in_img
in_img.unlink()
@pytest.fixture(scope="session")
def jpg_2000_img(tmp_path_factory, tmp_normal_png):
in_img = tmp_path_factory.mktemp("jpg_2000") / "in.jp2"
subprocess.check_call(["convert", str(tmp_normal_png), str(in_img)])
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "JP2", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "JPEG-2000 File Format Syntax"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/jp2", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "TrueColor", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "JPEG2000", str(identify)
yield in_img
in_img.unlink()
@pytest.fixture(scope="session")
def png_rgb8_img(tmp_normal_png):
in_img = tmp_normal_png
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "TrueColor", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig") == "8"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "8"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "2"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "2 (Truecolor)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
return in_img
@pytest.fixture(scope="session")
def png_rgb16_img(tmp_normal16_png):
in_img = tmp_normal16_png
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "TrueColor", str(identify)
assert identify[0]["image"].get("depth") == 16, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig")
== "16"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "16"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "2"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "2 (Truecolor)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
return in_img
@pytest.fixture(scope="session")
def png_rgba8_img(tmp_path_factory, tmp_alpha_png):
in_img = tmp_path_factory.mktemp("png_rgba8") / "in.png"
subprocess.check_call(
["convert", str(tmp_alpha_png), "-depth", "8", "-strip", str(in_img)]
)
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "TrueColorAlpha", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig") == "8"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "8"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "6"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "6 (RGBA)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
yield in_img
in_img.unlink()
@pytest.fixture(scope="session")
def png_rgba16_img(tmp_alpha_png):
in_img = tmp_alpha_png
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "TrueColorAlpha", str(identify)
assert identify[0]["image"].get("depth") == 16, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig")
== "16"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "16"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "6"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "6 (RGBA)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
return in_img
@pytest.fixture(scope="session")
def png_gray8a_img(tmp_path_factory, tmp_alpha_png):
in_img = tmp_path_factory.mktemp("png_gray8a") / "in.png"
subprocess.check_call(
[
"convert",
str(tmp_alpha_png),
"-colorspace",
"Gray",
"-dither",
"FloydSteinberg",
"-colors",
"256",
"-depth",
"8",
"-strip",
str(in_img),
]
)
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "GrayscaleAlpha", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig") == "8"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "8"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "4"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "4 (GrayAlpha)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
yield in_img
in_img.unlink()
@pytest.fixture(scope="session")
def png_gray16a_img(tmp_path_factory, tmp_alpha_png):
in_img = tmp_path_factory.mktemp("png_gray16a") / "in.png"
subprocess.check_call(
[
"convert",
str(tmp_alpha_png),
"-colorspace",
"Gray",
"-depth",
"16",
"-strip",
str(in_img),
]
)
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "GrayscaleAlpha", str(identify)
assert identify[0]["image"].get("depth") == 16, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig")
== "16"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "16"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "4"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "4 (GrayAlpha)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
yield in_img
in_img.unlink()
@pytest.fixture(scope="session")
def png_interlaced_img(tmp_path_factory, tmp_normal_png):
in_img = tmp_path_factory.mktemp("png_interlaced") / "in.png"
subprocess.check_call(
["convert", str(tmp_normal_png), "-interlace", "PNG", "-strip", str(in_img)]
)
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "TrueColor", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig") == "8"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "8"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "2"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "2 (Truecolor)"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.interlace_method")
== "1 (Adam7 method)"
), str(identify)
yield in_img
in_img.unlink()
@pytest.fixture(scope="session")
def png_gray1_img(tmp_path_factory, tmp_gray1_png):
identify = json.loads(
subprocess.check_output(["convert", str(tmp_gray1_png), "json:"])
)
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "Bilevel", str(identify)
assert identify[0]["image"].get("depth") == 1, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig") == "1"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "1"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "0"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "0 (Grayscale)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
return tmp_gray1_png
@pytest.fixture(scope="session")
def png_gray2_img(tmp_path_factory, tmp_gray2_png):
identify = json.loads(
subprocess.check_output(["convert", str(tmp_gray2_png), "json:"])
)
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "Grayscale", str(identify)
assert identify[0]["image"].get("depth") == 2, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig") == "2"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "2"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "0"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "0 (Grayscale)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
return tmp_gray2_png
@pytest.fixture(scope="session")
def png_gray4_img(tmp_path_factory, tmp_gray4_png):
identify = json.loads(
subprocess.check_output(["convert", str(tmp_gray4_png), "json:"])
)
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "Grayscale", str(identify)
assert identify[0]["image"].get("depth") == 4, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig") == "4"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "4"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "0"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "0 (Grayscale)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
return tmp_gray4_png
@pytest.fixture(scope="session")
def png_gray8_img(tmp_path_factory, tmp_gray8_png):
identify = json.loads(
subprocess.check_output(["convert", str(tmp_gray8_png), "json:"])
)
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "Grayscale", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig") == "8"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "8"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "0"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "0 (Grayscale)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
return tmp_gray8_png
@pytest.fixture(scope="session")
def png_gray16_img(tmp_path_factory, tmp_gray16_png):
identify = json.loads(
subprocess.check_output(["convert", str(tmp_gray16_png), "json:"])
)
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "Grayscale", str(identify)
assert identify[0]["image"].get("depth") == 16, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig")
== "16"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "16"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "0"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "0 (Grayscale)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
return tmp_gray16_png
@pytest.fixture(scope="session")
def png_palette1_img(tmp_path_factory, tmp_palette1_png):
identify = json.loads(
subprocess.check_output(["convert", str(tmp_palette1_png), "json:"])
)
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "Palette", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig") == "1"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "1"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "3"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "3 (Indexed)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
return tmp_palette1_png
@pytest.fixture(scope="session")
def png_palette2_img(tmp_path_factory, tmp_palette2_png):
identify = json.loads(
subprocess.check_output(["convert", str(tmp_palette2_png), "json:"])
)
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "Palette", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig") == "2"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "2"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "3"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "3 (Indexed)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
return tmp_palette2_png
@pytest.fixture(scope="session")
def png_palette4_img(tmp_path_factory, tmp_palette4_png):
identify = json.loads(
subprocess.check_output(["convert", str(tmp_palette4_png), "json:"])
)
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "Palette", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig") == "4"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "4"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "3"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "3 (Indexed)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
return tmp_palette4_png
@pytest.fixture(scope="session")
def png_palette8_img(tmp_path_factory, tmp_palette8_png):
identify = json.loads(
subprocess.check_output(["convert", str(tmp_palette8_png), "json:"])
)
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "PNG", str(identify)
assert (
identify[0]["image"].get("formatDescription") == "Portable Network Graphics"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/png", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "Palette", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "Zip", str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit-depth-orig") == "8"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.bit_depth") == "8"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color-type-orig")
== "3"
), str(identify)
assert (
identify[0]["image"].get("properties", {}).get("png:IHDR.color_type")
== "3 (Indexed)"
), str(identify)
assert (
identify[0]["image"]["properties"]["png:IHDR.interlace_method"]
== "0 (Not interlaced)"
), str(identify)
return tmp_palette8_png
@pytest.fixture(scope="session")
def gif_transparent_img(tmp_path_factory, tmp_alpha_png):
in_img = tmp_path_factory.mktemp("gif_transparent_img") / "in.gif"
subprocess.check_call(["convert", str(tmp_alpha_png), str(in_img)])
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "GIF", str(identify)
assert (
identify[0]["image"]["formatDescription"]
== "CompuServe graphics interchange format"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/gif", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "PaletteAlpha", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("colormapEntries") == 256, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "LZW", str(identify)
yield in_img
in_img.unlink()
@pytest.fixture(scope="session")
def gif_palette1_img(tmp_path_factory, tmp_palette1_png):
in_img = tmp_path_factory.mktemp("gif_palette1_img") / "in.gif"
subprocess.check_call(["convert", str(tmp_palette1_png), str(in_img)])
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "GIF", str(identify)
assert (
identify[0]["image"]["formatDescription"]
== "CompuServe graphics interchange format"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/gif", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "Palette", str(identify)
assert identify[0]["image"].get("depth") == 8, str(identify)
assert identify[0]["image"].get("colormapEntries") == 2, str(identify)
assert identify[0]["image"].get("pageGeometry") == {
"width": 60,
"height": 60,
"x": 0,
"y": 0,
}, str(identify)
assert identify[0]["image"].get("compression") == "LZW", str(identify)
yield in_img
in_img.unlink()
@pytest.fixture(scope="session")
def gif_palette2_img(tmp_path_factory, tmp_palette2_png):
in_img = tmp_path_factory.mktemp("gif_palette2_img") / "in.gif"
subprocess.check_call(["convert", str(tmp_palette2_png), str(in_img)])
identify = json.loads(subprocess.check_output(["convert", str(in_img), "json:"]))
assert len(identify) == 1
# somewhere between imagemagick 6.9.7.4 and 6.9.9.34, the json output was
# put into an array, here we cater for the older version containing just
# the bare dictionary
if "image" in identify:
identify = [identify]
assert "image" in identify[0]
assert identify[0]["image"].get("format") == "GIF", str(identify)
assert (
identify[0]["image"]["formatDescription"]
== "CompuServe graphics interchange format"
), str(identify)
assert identify[0]["image"].get("mimeType") == "image/gif", str(identify)
assert identify[0]["image"].get("geometry") == {
"width": 60,