Compare commits

...

4 commits

View file

@ -22,7 +22,17 @@ import sys
import os import os
import zlib import zlib
import argparse import argparse
from PIL import Image, TiffImagePlugin from PIL import Image, TiffImagePlugin, GifImagePlugin
if hasattr(GifImagePlugin, "LoadingStrategy"):
# Pillow 9.0.0 started emitting all frames but the first as RGB instead of
# P to make sure that more than 256 colors can be represented. But palette
# images compress far better than RGB images in PDF so we instruct Pillow
# to only emit RGB frames if the palette differs and return P otherwise.
# This works since Pillow 9.1.0.
GifImagePlugin.LOADING_STRATEGY = (
GifImagePlugin.LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY
)
# TiffImagePlugin.DEBUG = True # TiffImagePlugin.DEBUG = True
from PIL.ExifTags import TAGS from PIL.ExifTags import TAGS
@ -91,7 +101,10 @@ ImageFormat = Enum("ImageFormat", "JPEG JPEG2000 CCITTGroup4 PNG GIF TIFF MPO ot
PageMode = Enum("PageMode", "none outlines thumbs") PageMode = Enum("PageMode", "none outlines thumbs")
PageLayout = Enum("PageLayout", "single onecolumn twocolumnright twocolumnleft twopageright twopageleft") PageLayout = Enum(
"PageLayout",
"single onecolumn twocolumnright twocolumnleft twopageright twopageleft",
)
Magnification = Enum("Magnification", "fit fith fitbh") Magnification = Enum("Magnification", "fit fith fitbh")
@ -389,6 +402,28 @@ class ExifOrientationError(Exception):
pass pass
# temporary change the attribute of an object using a context manager
class temp_attr:
def __init__(self, obj, field, value):
self.obj = obj
self.field = field
self.value = value
def __enter__(self):
self.exists = False
if hasattr(self.obj, self.field):
self.exists = True
self.old_value = getattr(self.obj, self.field)
print(f"setting {self.obj}.{self.field} = {self.value}")
setattr(self.obj, self.field, self.value)
def __exit__(self, exctype, excinst, exctb):
if self.exists:
setattr(self.obj, self.field, self.old_value)
else:
delattr(self.obj, self.field)
# without pdfrw this function is a no-op # without pdfrw this function is a no-op
def my_convert_load(string): def my_convert_load(string):
return string return string
@ -1106,8 +1141,16 @@ class pdfdoc(object):
[initial_page, PdfName.XYZ, NullObject, NullObject, 0] [initial_page, PdfName.XYZ, NullObject, NullObject, 0]
) )
# the /OpenAction array must contain the page as an indirect object # The /OpenAction array must contain the page as an indirect object.
# This changed some time after 4.2.0 and on or before 5.0.0 and current
# versions require to use .obj or otherwise we get:
# TypeError: Can't convert ObjectHelper (or subclass) to Object
# implicitly. Use .obj to get access the underlying object.
# See https://github.com/pikepdf/pikepdf/issues/313 for details.
if self.engine == Engine.pikepdf: if self.engine == Engine.pikepdf:
if isinstance(initial_page, pikepdf.Page):
initial_page = self.writer.make_indirect(initial_page.obj)
else:
initial_page = self.writer.make_indirect(initial_page) initial_page = self.writer.make_indirect(initial_page)
if self.magnification == Magnification.fit: if self.magnification == Magnification.fit:
@ -1410,27 +1453,29 @@ def transcode_monochrome(imgdata):
# into putting everything into a single strip. Thanks to Andrew Murray for # into putting everything into a single strip. Thanks to Andrew Murray for
# the hack. # the hack.
# #
# This can be dropped once this gets merged: # Since version 8.4.0 Pillow allows us to modify the strip size explicitly
# https://github.com/python-pillow/Pillow/pull/5744 tmp_strip_size = (imgdata.size[0] + 7) // 8 * imgdata.size[1]
if hasattr(TiffImagePlugin, "STRIP_SIZE"):
# we are using Pillow 8.4.0 or later
with temp_attr(TiffImagePlugin, "STRIP_SIZE", tmp_strip_size):
im.save(newimgio, format="TIFF", compression="group4")
else:
# only needed for Pillow 8.3.x but works for versions before that as
# well
pillow__getitem__ = TiffImagePlugin.ImageFileDirectory_v2.__getitem__ pillow__getitem__ = TiffImagePlugin.ImageFileDirectory_v2.__getitem__
def __getitem__(self, tag): def __getitem__(self, tag):
overrides = { overrides = {
TiffImagePlugin.ROWSPERSTRIP: imgdata.size[1], TiffImagePlugin.ROWSPERSTRIP: imgdata.size[1],
TiffImagePlugin.STRIPBYTECOUNTS: [ TiffImagePlugin.STRIPBYTECOUNTS: [tmp_strip_size],
(imgdata.size[0] + 7) // 8 * imgdata.size[1]
],
TiffImagePlugin.STRIPOFFSETS: [0], TiffImagePlugin.STRIPOFFSETS: [0],
} }
return overrides.get(tag, pillow__getitem__(self, tag)) return overrides.get(tag, pillow__getitem__(self, tag))
# use try/finally to make sure that __getitem__ is reset even if save() with temp_attr(
# raises an exception TiffImagePlugin.ImageFileDirectory_v2, "__getitem__", __getitem__
try: ):
TiffImagePlugin.ImageFileDirectory_v2.__getitem__ = __getitem__
im.save(newimgio, format="TIFF", compression="group4") im.save(newimgio, format="TIFF", compression="group4")
finally:
TiffImagePlugin.ImageFileDirectory_v2.__getitem__ = pillow__getitem__
# Open new image in memory # Open new image in memory
newimgio.seek(0) newimgio.seek(0)