forked from josch/img2pdf
6707 lines
262 KiB
Python
Executable file
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,
|
|