Compare commits

..

5 commits

Author SHA1 Message Date
85cbe1d128
factor out argparse.ArgumentParser to allow for generating completions via shtab 2023-06-11 08:09:46 +02:00
b25429a4c1
src/img2pdf_test.py: add tests for timestamps 2023-06-11 08:01:36 +02:00
c703e9df06
fix date(1) based timestamp parser 2023-06-11 07:48:23 +02:00
79e9985f35
src/img2pdf_test.py: black 2023-06-11 07:47:22 +02:00
cb2644c34f
do not include thumbnails in the output by default unless --include-thumbnails is used
This is relevant for the MPO format which otherwise would result in PDF
files containing the same image in different sizes multiple times. With
this change, the default is to only have a single page containing the
full MPO. This means that extracting that MPO also gets the thumbnails
back.

With the --include-thumbnails option, each frame gets stored on its own
page as it is done for multi-frame GIF, for example.

Closes: #135
2023-06-11 07:31:07 +02:00
2 changed files with 323 additions and 33 deletions

View file

@ -1750,7 +1750,9 @@ def parse_miff(data):
# fmt: on
def read_images(rawdata, colorspace, first_frame_only=False, rot=None):
def read_images(
rawdata, colorspace, first_frame_only=False, rot=None, include_thumbnails=False
):
im = BytesIO(rawdata)
im.seek(0)
imgdata = None
@ -1836,6 +1838,77 @@ def read_images(rawdata, colorspace, first_frame_only=False, rot=None):
if imgformat == ImageFormat.MPO:
result = []
img_page_count = 0
assert len(imgdata._MpoImageFile__mpoffsets) == len(imgdata.mpinfo[0xB002])
num_frames = len(imgdata.mpinfo[0xB002])
# An MPO file can be a main image together with one or more thumbnails
# if that is the case, then we only include all frames if the
# --include-thumbnails option is given. If it is not, such an MPO file
# will be embedded as is, so including its thumbnails but showing up
# as a single image page in the resulting PDF.
num_main_frames = 0
num_thumbnail_frames = 0
for i, mpent in enumerate(imgdata.mpinfo[0xB002]):
# check only the first frame for being the main image
if (
i == 0
and mpent["Attribute"]["DependentParentImageFlag"]
and not mpent["Attribute"]["DependentChildImageFlag"]
and mpent["Attribute"]["RepresentativeImageFlag"]
and mpent["Attribute"]["MPType"] == "Baseline MP Primary Image"
):
num_main_frames += 1
elif (
not mpent["Attribute"]["DependentParentImageFlag"]
and mpent["Attribute"]["DependentChildImageFlag"]
and not mpent["Attribute"]["RepresentativeImageFlag"]
and mpent["Attribute"]["MPType"]
in [
"Large Thumbnail (VGA Equivalent)",
"Large Thumbnail (Full HD Equivalent)",
]
):
num_thumbnail_frames += 1
logger.debug(f"number of frames: {num_frames}")
logger.debug(f"number of main frames: {num_main_frames}")
logger.debug(f"number of thumbnail frames: {num_thumbnail_frames}")
# this MPO file is a main image plus zero or more thumbnails
# embed as-is unless the --include-thumbnails option was given
if num_frames == 1 or (
not include_thumbnails
and num_main_frames == 1
and num_thumbnail_frames + 1 == num_frames
):
color, ndpi, imgwidthpx, imgheightpx, rotation, iccp = get_imgmetadata(
imgdata, imgformat, default_dpi, colorspace, rawdata, rot
)
if color == Colorspace["1"]:
raise JpegColorspaceError("jpeg can't be monochrome")
if color == Colorspace["P"]:
raise JpegColorspaceError("jpeg can't have a color palette")
if color == Colorspace["RGBA"]:
raise JpegColorspaceError("jpeg can't have an alpha channel")
logger.debug("read_images() embeds an MPO verbatim")
cleanup()
return [
(
color,
ndpi,
ImageFormat.JPEG,
rawdata,
None,
imgwidthpx,
imgheightpx,
[],
False,
8,
rotation,
iccp,
)
]
# If the control flow reaches here, the MPO has more than a single
# frame but was not detected to be a main image followed by multiple
# thumbnails. We thus treat this MPO as we do other multi-frame images
# and include all its frames as individual pages.
for offset, mpent in zip(
imgdata._MpoImageFile__mpoffsets, imgdata.mpinfo[0xB002]
):
@ -2509,6 +2582,7 @@ def convert(*images, **kwargs):
artborder=None,
pdfa=None,
rotation=None,
include_thumbnails=False,
)
for kwname, default in _default_kwargs.items():
if kwname not in kwargs:
@ -2601,6 +2675,7 @@ def convert(*images, **kwargs):
kwargs["colorspace"],
kwargs["first_frame_only"],
kwargs["rotation"],
kwargs["include_thumbnails"],
):
pagewidth, pageheight, imgwidthpdf, imgheightpdf = kwargs["layout_fun"](
imgwidthpx, imgheightpx, ndpi
@ -2976,7 +3051,7 @@ def valid_date(string):
else:
try:
return parser.parse(string)
except TypeError:
except:
pass
# as a last resort, try the local date utility
try:
@ -2989,7 +3064,7 @@ def valid_date(string):
except subprocess.CalledProcessError:
pass
else:
return datetime.utcfromtimestamp(int(utime))
return datetime.fromtimestamp(int(utime))
raise argparse.ArgumentTypeError("cannot parse date: %s" % string)
@ -3691,7 +3766,7 @@ def gui():
app.mainloop()
def main(argv=sys.argv):
def get_main_parser():
rendered_papersizes = ""
for k, v in sorted(papersizes.items()):
rendered_papersizes += " %-8s %s\n" % (papernames[k], v)
@ -3936,6 +4011,17 @@ RGB.""",
"input image be converted into a page in the resulting PDF.",
)
outargs.add_argument(
"--include-thumbnails",
action="store_true",
help="Some multi-frame formats like MPO carry a main image and "
"one or more scaled-down copies of the main image (thumbnails). "
"In such a case, img2pdf will only include the main image and "
"not create additional pages for each of the thumbnails. If this "
"option is set, img2pdf will instead create one page per frame and "
"thus store each thumbnail on its own page.",
)
outargs.add_argument(
"--pillow-limit-break",
action="store_true",
@ -4243,8 +4329,11 @@ and left/right, respectively. It is not possible to specify asymmetric borders.
action="store_true",
help="Instruct the PDF viewer to open the PDF in fullscreen mode",
)
return parser
args = parser.parse_args(argv[1:])
def main(argv=sys.argv):
args = get_main_parser().parse_args(argv[1:])
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
@ -4333,6 +4422,7 @@ and left/right, respectively. It is not possible to specify asymmetric borders.
artborder=args.art_border,
pdfa=args.pdfa,
rotation=args.rotation,
include_thumbnails=args.include_thumbnails,
)
except Exception as e:
logger.error("error: " + str(e))

View file

@ -19,6 +19,8 @@ from packaging.version import parse as parse_version
import warnings
import json
import pathlib
import itertools
import xml.etree.ElementTree as ET
img2pdfprog = os.getenv("img2pdfprog", default="src/img2pdf.py")
@ -37,6 +39,14 @@ for glob in ICC_PROFILE_PATHS:
ICC_PROFILE = path
break
HAVE_FAKETIME = True
try:
ver = subprocess.check_output(["faketime", "--version"])
if b"faketime: Version " not in ver:
HAVE_FAKETIME = False
except FileNotFoundError:
HAVE_FAKETIME = False
HAVE_MUTOOL = True
try:
ver = subprocess.check_output(["mutool", "-v"], stderr=subprocess.STDOUT)
@ -130,6 +140,25 @@ psnr_re = re.compile(rb"((?:inf|(?:0|[1-9][0-9]*)(?:\.[0-9]+)?))(?: \([0-9.]+\))
###############################################################################
# Interpret a datetime string in a given timezone and format it according to a
# given format string in in UTC.
# We avoid using the Python datetime module for this job because doing so would
# just replicate the code we want to test for correctness.
def tz2utcstrftime(string, fmt, timezone):
return (
subprocess.check_output(
[
"date",
"--utc",
f'--date=TZ="{timezone}" {string}',
f"+{fmt}",
]
)
.decode("utf8")
.removesuffix("\n")
)
def find_closest_palette_color(color, palette):
if color.ndim == 0:
idx = (numpy.abs(palette - color)).argmin()
@ -603,7 +632,7 @@ def alpha_value():
alpha = numpy.zeros((60, 60, 4), dtype=numpy.dtype("int64"))
# draw three circles
for (xpos, ypos, color) in [
for xpos, ypos, color in [
(12, 3, [0xFFFF, 0, 0, 0xFFFF]),
(21, 21, [0, 0xFFFF, 0, 0xFFFF]),
(3, 21, [0, 0, 0xFFFF, 0xFFFF]),
@ -2331,7 +2360,10 @@ def tiff_float_img(tmp_path_factory, tmp_normal_png):
assert identify[0]["image"].get("colorspace") == "sRGB", 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) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 8, str(identify)
@ -2392,7 +2424,10 @@ def tiff_cmyk8_img(tmp_path_factory, tmp_normal_png):
assert identify[0]["image"].get("colorspace") == "CMYK", str(identify)
assert identify[0]["image"].get("type") == "ColorSeparation", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 8, str(identify)
@ -2451,7 +2486,10 @@ def tiff_cmyk16_img(tmp_path_factory, tmp_normal_png):
assert identify[0]["image"].get("colorspace") == "CMYK", str(identify)
assert identify[0]["image"].get("type") == "ColorSeparation", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 16, str(identify)
@ -2500,7 +2538,10 @@ def tiff_rgb8_img(tmp_path_factory, tmp_normal_png):
assert identify[0]["image"].get("colorspace") == "sRGB", 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) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 8, str(identify)
@ -2556,7 +2597,10 @@ def tiff_rgb12_img(tmp_path_factory, tmp_normal16_png):
assert identify[0]["image"].get("colorspace") == "sRGB", 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) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("baseDepth") == 12, str(identify)
@ -2612,7 +2656,10 @@ def tiff_rgb14_img(tmp_path_factory, tmp_normal16_png):
assert identify[0]["image"].get("colorspace") == "sRGB", 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) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("baseDepth") == 14, str(identify)
@ -2668,7 +2715,10 @@ def tiff_rgb16_img(tmp_path_factory, tmp_normal16_png):
assert identify[0]["image"].get("colorspace") == "sRGB", 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) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 16, str(identify)
@ -2725,7 +2775,10 @@ def tiff_rgba8_img(tmp_path_factory, tmp_alpha_png):
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "TrueColorAlpha", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 8, str(identify)
@ -2782,7 +2835,10 @@ def tiff_rgba16_img(tmp_path_factory, tmp_alpha_png):
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "TrueColorAlpha", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 16, str(identify)
@ -2838,7 +2894,10 @@ def tiff_gray1_img(tmp_path_factory, tmp_gray1_png):
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "Bilevel", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 1, str(identify)
@ -2895,7 +2954,10 @@ def tiff_gray2_img(tmp_path_factory, tmp_gray2_png):
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "Grayscale", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 2, str(identify)
@ -2952,7 +3014,10 @@ def tiff_gray4_img(tmp_path_factory, tmp_gray4_png):
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "Grayscale", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 4, str(identify)
@ -3009,7 +3074,10 @@ def tiff_gray8_img(tmp_path_factory, tmp_gray8_png):
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "Grayscale", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 8, str(identify)
@ -3066,7 +3134,10 @@ def tiff_gray16_img(tmp_path_factory, tmp_gray16_png):
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "Grayscale", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 16, str(identify)
@ -3125,7 +3196,10 @@ def tiff_multipage_img(tmp_path_factory, tmp_normal_png, tmp_inverse_png):
assert identify[0]["image"].get("colorspace") == "sRGB", 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) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 8, str(identify)
@ -3165,7 +3239,10 @@ def tiff_multipage_img(tmp_path_factory, tmp_normal_png, tmp_inverse_png):
assert identify[0]["image"].get("colorspace") == "sRGB", 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) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 8, str(identify)
@ -3214,7 +3291,10 @@ def tiff_palette1_img(tmp_path_factory, tmp_palette1_png):
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "Palette", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 8, str(identify)
@ -3264,7 +3344,10 @@ def tiff_palette2_img(tmp_path_factory, tmp_palette2_png):
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "Palette", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 8, str(identify)
@ -3314,7 +3397,10 @@ def tiff_palette4_img(tmp_path_factory, tmp_palette4_png):
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "Palette", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 8, str(identify)
@ -3364,7 +3450,10 @@ def tiff_palette8_img(tmp_path_factory, tmp_palette8_png):
assert identify[0]["image"].get("colorspace") == "sRGB", str(identify)
assert identify[0]["image"].get("type") == "Palette", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 8, str(identify)
@ -3427,7 +3516,10 @@ def tiff_ccitt_lsb_m2l_white_img(tmp_path_factory, tmp_gray1_png):
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "Bilevel", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 1, str(identify)
@ -3677,7 +3769,10 @@ def tiff_ccitt_lsb_m2l_black_img(tmp_path_factory, tmp_gray1_png):
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "Bilevel", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 1, str(identify)
@ -3767,7 +3862,10 @@ def tiff_ccitt_nometa1_img(tmp_path_factory, tmp_gray1_png):
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
assert identify[0]["image"].get("type") == "Bilevel", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 1, str(identify)
@ -3851,7 +3949,10 @@ def tiff_ccitt_nometa2_img(tmp_path_factory, tmp_gray1_png):
assert identify[0]["image"].get("units") == "PixelsPerInch", str(identify)
assert identify[0]["image"].get("type") == "Bilevel", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("colorspace") == "Gray", str(identify)
@ -3920,7 +4021,10 @@ def miff_cmyk8_img(tmp_path_factory, tmp_normal_png):
assert identify[0]["image"].get("colorspace") == "CMYK", str(identify)
assert identify[0]["image"].get("type") == "ColorSeparation", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 8, str(identify)
@ -3968,7 +4072,10 @@ def miff_cmyk16_img(tmp_path_factory, tmp_normal_png):
assert identify[0]["image"].get("colorspace") == "CMYK", str(identify)
assert identify[0]["image"].get("type") == "ColorSeparation", str(identify)
endian = "endianess" if identify[0].get("version", "0") < "1.0" else "endianness"
assert identify[0]["image"].get(endian) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 16, str(identify)
@ -4007,7 +4114,10 @@ def miff_rgb8_img(tmp_path_factory, tmp_normal_png):
assert identify[0]["image"].get("colorspace") == "sRGB", 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) in ["Undefined", "LSB",], str(
assert identify[0]["image"].get(endian) in [
"Undefined",
"LSB",
], str(
identify
) # FIXME: should be LSB
assert identify[0]["image"].get("depth") == 8, str(identify)
@ -6832,6 +6942,96 @@ def general_input(request):
return request.param
@pytest.mark.skipif(not HAVE_FAKETIME, reason="requires faketime")
@pytest.mark.parametrize(
"engine,testdata,timezone,pdfa",
itertools.product(
["internal", "pikepdf"],
["2021-02-05 17:49:00"],
["Europe/Berlin", "GMT+12"],
[True, False],
),
)
def test_faketime(tmp_path_factory, jpg_img, engine, testdata, timezone, pdfa):
expected = tz2utcstrftime(testdata, "D:%Y%m%d%H%M%SZ", timezone)
out_pdf = tmp_path_factory.mktemp("faketime") / "out.pdf"
subprocess.check_call(
["env", f"TZ={timezone}", "faketime", "-f", testdata, img2pdfprog]
+ (["--pdfa"] if pdfa else [])
+ [
"--producer=",
"--engine=" + engine,
"--output=" + str(out_pdf),
str(jpg_img),
]
)
with pikepdf.open(str(out_pdf)) as p:
assert p.docinfo.CreationDate == expected
assert p.docinfo.ModDate == expected
if pdfa:
assert p.Root.Metadata.Subtype == "/XML"
assert p.Root.Metadata.Type == "/Metadata"
expected = tz2utcstrftime(testdata, "%Y-%m-%dT%H:%M:%SZ", timezone)
root = ET.fromstring(p.Root.Metadata.read_bytes())
for k in ["ModifyDate", "CreateDate"]:
assert (
root.find(
f".//xmp:{k}", {"xmp": "http://ns.adobe.com/xap/1.0/"}
).text
== expected
)
out_pdf.unlink()
@pytest.mark.parametrize(
"engine,testdata,timezone,pdfa",
itertools.product(
["internal", "pikepdf"],
[
"2021-02-05 17:49:00",
"2021-02-05T17:49:00",
"Fri, 05 Feb 2021 17:49:00 +0100",
"last year 12:00",
],
["Europe/Berlin", "GMT+12"],
[True, False],
),
)
def test_date(tmp_path_factory, jpg_img, engine, testdata, timezone, pdfa):
# we use the date utility to convert the timestamp from the local
# timezone into UTC with the format used by PDF
expected = tz2utcstrftime(testdata, "D:%Y%m%d%H%M%SZ", timezone)
out_pdf = tmp_path_factory.mktemp("faketime") / "out.pdf"
subprocess.check_call(
["env", f"TZ={timezone}", img2pdfprog]
+ (["--pdfa"] if pdfa else [])
+ [
f"--moddate={testdata}",
f"--creationdate={testdata}",
"--producer=",
"--engine=" + engine,
"--output=" + str(out_pdf),
str(jpg_img),
]
)
with pikepdf.open(str(out_pdf)) as p:
assert p.docinfo.CreationDate == expected
assert p.docinfo.ModDate == expected
if pdfa:
assert p.Root.Metadata.Subtype == "/XML"
assert p.Root.Metadata.Type == "/Metadata"
expected = tz2utcstrftime(testdata, "%Y-%m-%dT%H:%M:%SZ", timezone)
root = ET.fromstring(p.Root.Metadata.read_bytes())
for k in ["ModifyDate", "CreateDate"]:
assert (
root.find(
f".//xmp:{k}", {"xmp": "http://ns.adobe.com/xap/1.0/"}
).text
== expected
)
out_pdf.unlink()
@pytest.mark.parametrize("engine", ["internal", "pikepdf"])
def test_general(general_input, engine):
inputf = os.path.join(os.path.dirname(__file__), "tests", "input", general_input)