Compare commits

..

7 commits

7 changed files with 51 additions and 33 deletions

View file

@ -82,7 +82,10 @@ Bugs
only nine different values from 1 to 9 are permitted, Anroid phones and only nine different values from 1 to 9 are permitted, Anroid phones and
Canon DSLR cameras produce JPEG images with the invalid value of zero. Canon DSLR cameras produce JPEG images with the invalid value of zero.
Either fix your input images with `exiftool` or similar software before Either fix your input images with `exiftool` or similar software before
passing the JPEG to `img2pdf` or run `img2pdf` with `--rotation=ifvalid`. passing the JPEG to `img2pdf` or run `img2pdf` with `--rotation=ifvalid`
(if you run img2pdf from the commandline) or by passing
`rotation=img2pdf.Rotation.ifvalid` as an argument to `convert()` when using
img2pdf as a library.
- img2pdf uses PIL (or Pillow) to obtain image meta data and to convert the - img2pdf uses PIL (or Pillow) to obtain image meta data and to convert the
input if necessary. To prevent decompression bomb denial of service attacks, input if necessary. To prevent decompression bomb denial of service attacks,
@ -191,6 +194,10 @@ The package can also be used as a library:
with open("name.pdf","wb") as f: with open("name.pdf","wb") as f:
f.write(img2pdf.convert(glob.glob("/path/to/*.jpg"))) f.write(img2pdf.convert(glob.glob("/path/to/*.jpg")))
# ignore invalid rotation values in the input images
with open("name.pdf","wb") as f:
f.write(img2pdf.convert('test.jpg'), rotation=img2pdf.Rotation.ifvalid)
# writing to file descriptor # writing to file descriptor
with open("name.pdf","wb") as f1, open("test.jpg") as f2: with open("name.pdf","wb") as f1, open("test.jpg") as f2:
img2pdf.convert(f2, outputstream=f1) img2pdf.convert(f2, outputstream=f1)

View file

@ -16,12 +16,13 @@ environment:
- PYTHON: "C:\\Python37-x64" - PYTHON: "C:\\Python37-x64"
install: install:
- "%PYTHON%\\python.exe -m pip install tox wheel pyinstaller" - "%PYTHON%\\python.exe -m pip install tox wheel pyinstaller Pillow"
build: off build: off
test_script: # don't run tests on windows because we don't have imagemagick
- "%PYTHON%\\python.exe -m tox" #test_script:
# - "%PYTHON%\\python.exe -m tox"
after_test: after_test:
- "%PYTHON%\\python.exe setup.py bdist_wheel" - "%PYTHON%\\python.exe setup.py bdist_wheel"

View file

@ -1277,17 +1277,25 @@ def get_imgmetadata(
elif value in (2, 4, 5, 7): elif value in (2, 4, 5, 7):
if rotreq == Rotation.ifvalid: if rotreq == Rotation.ifvalid:
logger.warning( logger.warning(
"Unsupported flipped rotation mode (%d)", value "Unsupported flipped rotation mode (%d): use "
"--rotation=ifvalid or "
"rotation=img2pdf.Rotation.ifvalid to ignore",
value,
) )
else: else:
raise ExifOrientationError( raise ExifOrientationError(
"Unsupported flipped rotation mode (%d)" % value "Unsupported flipped rotation mode (%d): use "
"--rotation=ifvalid or "
"rotation=img2pdf.Rotation.ifvalid to ignore" % value
) )
else: else:
if rotreq == Rotation.ifvalid: if rotreq == Rotation.ifvalid:
logger.warning("Invalid rotation (%d)", value) logger.warning("Invalid rotation (%d)", value)
else: else:
raise ExifOrientationError("Invalid rotation (%d)" % value) raise ExifOrientationError(
"Invalid rotation (%d): use --rotation=ifvalid "
"or rotation=img2pdf.Rotation.ifvalid to ignore" % value
)
elif rotreq in (Rotation.none, Rotation["0"]): elif rotreq in (Rotation.none, Rotation["0"]):
rotation = 0 rotation = 0
elif rotreq == Rotation["90"]: elif rotreq == Rotation["90"]:

View file

@ -2190,15 +2190,37 @@ def gif_palette8_img(tmp_path_factory, tmp_palette8_png):
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def gif_animation_img(tmp_path_factory, tmp_normal_png, tmp_inverse_png): def gif_animation_img(tmp_path_factory, tmp_normal_png, tmp_inverse_png):
in_img = tmp_path_factory.mktemp("gif_animation_img") / "in.gif" in_img = tmp_path_factory.mktemp("gif_animation_img") / "in.gif"
pal_img = tmp_path_factory.mktemp("gif_animation_img") / "pal.gif"
tmp_img = tmp_path_factory.mktemp("gif_animation_img") / "tmp.gif"
subprocess.check_call( subprocess.check_call(
CONVERT CONVERT
+ [ + [
str(tmp_normal_png), str(tmp_normal_png),
str(tmp_inverse_png), str(tmp_inverse_png),
"-strip", str(tmp_img),
str(in_img),
] ]
) )
# create palette image with all unique colors
subprocess.check_call(
CONVERT
+ [
str(tmp_img),
"-unique-colors",
str(pal_img),
]
)
# make sure all frames have the same palette by using -remap
subprocess.check_call(
CONVERT
+ [
str(tmp_img),
"-strip",
"-remap", str(pal_img),
str(in_img)
]
)
pal_img.unlink()
tmp_img.unlink()
identify = json.loads( identify = json.loads(
subprocess.check_output(CONVERT + [str(in_img) + "[0]", "json:"]) subprocess.check_output(CONVERT + [str(in_img) + "[0]", "json:"])
) )
@ -2228,6 +2250,7 @@ def gif_animation_img(tmp_path_factory, tmp_normal_png, tmp_inverse_png):
"y": 0, "y": 0,
}, str(identify) }, str(identify)
assert identify[0]["image"].get("compression") == "LZW", str(identify) assert identify[0]["image"].get("compression") == "LZW", str(identify)
colormap_frame0 = identify[0]["image"].get("colormap")
identify = json.loads( identify = json.loads(
subprocess.check_output(CONVERT + [str(in_img) + "[1]", "json:"]) subprocess.check_output(CONVERT + [str(in_img) + "[1]", "json:"])
) )
@ -2258,6 +2281,8 @@ def gif_animation_img(tmp_path_factory, tmp_normal_png, tmp_inverse_png):
}, str(identify) }, str(identify)
assert identify[0]["image"].get("compression") == "LZW", str(identify) assert identify[0]["image"].get("compression") == "LZW", str(identify)
assert identify[0]["image"].get("scene") == 1, str(identify) assert identify[0]["image"].get("scene") == 1, str(identify)
colormap_frame1 = identify[0]["image"].get("colormap")
assert colormap_frame0 == colormap_frame1
yield in_img yield in_img
in_img.unlink() in_img.unlink()
@ -6712,29 +6737,6 @@ def test_general(general_input, engine):
y = pikepdf.open(out) y = pikepdf.open(out)
pydictx = rec(x.Root) pydictx = rec(x.Root)
pydicty = rec(y.Root) pydicty = rec(y.Root)
if f.endswith(os.path.sep + "animation.gif"):
# starting with PIL 8.2.0 the palette is half the size when encoding
# our test GIF image as PNG
#
# to still compare successfully, we truncate the expected palette
import PIL
if PIL.__version__ >= "8.2.0":
assert len(pydictx["/Pages"]["/Kids"]) == 2
for p in pydictx["/Pages"]["/Kids"]:
assert p["/Resources"]["/XObject"]["/Im0"]["/ColorSpace"][2] == 127
assert len(pydicty["/Pages"]["/Kids"]) == 2
for p in pydicty["/Pages"]["/Kids"]:
cs = p["/Resources"]["/XObject"]["/Im0"]["/ColorSpace"]
cs[2] = decimal.Decimal("127")
cs[3] = cs[3][:384]
else:
assert (
pydictx["/Pages"]["/Kids"][0]["/Resources"]["/XObject"]["/Im0"][
"/ColorSpace"
][2]
== 255
)
assert pydictx == pydicty assert pydictx == pydicty
# the python-pil version 2.3.0-1ubuntu3 in Ubuntu does not have the # the python-pil version 2.3.0-1ubuntu3 in Ubuntu does not have the
# close() method # close() method

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

View file

@ -4,7 +4,7 @@
# and then run "tox" from this directory. # and then run "tox" from this directory.
[tox] [tox]
envlist = py35, py36, py37, py38, py39 envlist = py37, py38, py39, py310
skip_missing_interpreters = true skip_missing_interpreters = true
[testenv] [testenv]