Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-strip usage when encoding group4 TIF breaks use in PDF #5740

Closed
josch opened this issue Sep 30, 2021 · 11 comments · Fixed by #5744
Closed

Multi-strip usage when encoding group4 TIF breaks use in PDF #5740

josch opened this issue Sep 30, 2021 · 11 comments · Fixed by #5744
Labels

Comments

@josch
Copy link

josch commented Sep 30, 2021

Hi,

since #5514, group4 tiff images are not anymore saved as a single strip. This breaks the use-case of utilizing PIL for encoding images as group4 for inclusion in PDF documents, as the PDF format only supports a single group4 encoded strip per image.

Please allow for a way to force single-strip group4 encoding to restore this functionality, maybe by allowing to specify the maximum strip length?

(I realize this sounds a lot like https://xkcd.com/1172/ 😄)

@radarhere radarhere added the TIFF label Sep 30, 2021
@radarhere radarhere changed the title multi-strip usage when encoding group4 TIF breaks use in PDF Multi-strip usage when encoding group4 TIF breaks use in PDF Sep 30, 2021
@radarhere
Copy link
Member

radarhere commented Sep 30, 2021

If you understand #5514, and acknowledge that this is an obscure use case, is this hack interesting to you at all? It would let you override the values for ROWSPERSTRIP, STRIPBYTECOUNTS and STRIPOFFSETS.

from PIL import Image, TiffImagePlugin

pillow__getitem__ = TiffImagePlugin.ImageFileDirectory_v2.__getitem__
def __getitem__(self, tag):
	overrides = {
		TiffImagePlugin.ROWSPERSTRIP: 500,
		TiffImagePlugin.STRIPBYTECOUNTS: 31500,
		TiffImagePlugin.STRIPOFFSETS: 0
	}
	return overrides.get(tag, pillow__getitem__(self, tag))
TiffImagePlugin.ImageFileDirectory_v2.__getitem__ = __getitem__

im = Image.open("Tests/images/hopper_g4_500.tif")
im.save("out.tif", compression="group4")

@kmilos
Copy link
Contributor

kmilos commented Sep 30, 2021

Perhaps adding a constant that can be overridden instead of the hard-coded 64 KB here could be a neat way to address this?

@josch
Copy link
Author

josch commented Sep 30, 2021

@radarhere thanks a lot for your input! Yes, that hack is indeed interesting to me because even if @kmilos solution is implemented in a future pillow version, we cannot travel back in time and thus I would need a solution for those pillow versions that have #5514 applied but do not yet have a constant that can be overridden.

Unfortunately, I'm afraid that I'm not getting your code snippet to work. Did you test it? I suspect that how it is written, the values will not be overwritten by the IFD writer.

@kmilos
Copy link
Contributor

kmilos commented Oct 1, 2021

Unfortunately, I'm afraid that I'm not getting your code snippet to work.

Perhaps STRIPBYTECOUNTS and STRIPOFFSETS need to be 1-tuples, e.g. (31500,) and (0,) in the example above...

@josch
Copy link
Author

josch commented Oct 1, 2021

Yes, I tried that already and the resulting image is corrupted.

@radarhere
Copy link
Member

I've tested the code I wrote, yes.

Could we get a copy of your original image, and the values you're trying to write?

@josch
Copy link
Author

josch commented Oct 3, 2021

Sorry, it was my mistake. Of course I have to reset TiffImagePlugin.ImageFileDirectory_v2.__getitem__ back to its original before reading the bytecounts and offsets so that I can extract the CCITT group4 data. It works now.

Thanks a lot! :)

@hugovk hugovk closed this as completed Oct 3, 2021
@radarhere
Copy link
Member

radarhere commented Aug 1, 2022

To explicitly point it out here, #5744 since added the ability to set STRIP_SIZE.

You might also be interested to know that #6453 has requested group4 compression in PDFs to be a part of Pillow itself. I've created PR #6470 to add this feature.

@radarhere
Copy link
Member

#6470 has been merged, so strip_size can now also be passed in as an encoder argument when saving - im.save("out.tif", strip_size=...)

@josch
Copy link
Author

josch commented Aug 8, 2022

Thank you @radarhere, using im.save("out.tif", strip_size=...) will be much cleaner than temporarily setting PIL.TiffImagePlugin.STRIP_SIZE which has the potential of leaving the program in a bad state if the execution flow happens to leave before the original value is restored (for example due to an exception).

But is there a way (other than checking and comparing the PIL version) to check whether the current version of PIL supports im.save("out.tif", strip_size=...)? With PIL.TiffImagePlugin.STRIP_SIZE I was able to check hasattr(TiffImagePlugin, "STRIP_SIZE") and only then execute the code that sets PIL.TiffImagePlugin.STRIP_SIZE. Can a check like that also be done for whether an image plugin supports a specific encoder argument?

@radarhere
Copy link
Member

Not directly, no.

But if you don't like version numbers for some reason, perhaps you could check indirectly.

Type "help", "copyright", "credits" or "license" for more information.
>>> from PIL import Image
>>> Image.init()
1
>>> "MPO" in Image.SAVE_ALL
True

#6444 added support for saving multiple MPO frames, as will be released in Pillow 9.3.0 for the first time. So by checking if Pillow supports saving multiple MPO frames, you are checking that Pillow is >= 9.3.0 (presuming that you aren't concerned about people compiling Pillow from source in between versions).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants