Convert 8-bit PNG & GIF alpha channels to /SMasks in PDF #106
Loading…
Reference in a new issue
No description provided.
Delete branch "tzahola/img2pdf:png-alpha-to-smask"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
While it's true that PDF
/Image
objects don't support transparency, we can emulate it by splitting the alpha channel into a separate grayscale image (i.e./DeviceGray
), and apply it as a soft-mask (i.e./SMask
) on the opaque image.In this PR I'm proposing adding this feature to
img2pdf
in case of 8-bit true color PNGsfor 8-bit RGBA, LA (i.e. greyscale + alpha) and palletted images with transparency (e.g. GIFs):Interesting. I'm considering it. The only thing that makes me feel a bit iffy about this is, that there is no way that I know of, to get the original image with alpha channel back from a pdf with a soft-mask. Do you know how to?
Furthermore:
Yeah,
pdfimages
from Poppler will extract the soft-mask as a separate image. On the other hand, I've found that even for opaque images,pdfimages
won't recover the original pixel data if an image is using anything but the device-dependent color profiles: https://gitlab.freedesktop.org/poppler/poppler/-/issues/1084Personally, I'm using the attached
pdf2img
(attached as txt) shell script to losslessly recover images from PDF files (depends onqpdf
,jq
andconvert
from ImageMagick). Usage:It's not as sophisticated as
img2pdf
, but can be used to verify that the conversion is indeed lossless.Yeah, I'll look into it :)
I guess it could work too, but I need to check how ImageMagick deals with reconstructing a GIF from RGB data + bi-level alpha data (i.e. consisting solely of 0x00 an 0xFF). There's also another way of doing 1-bit transparency via the
/Mask
property, but I don't think using that would worth the additional complexity.You mean like this? :)
This code snippet works for one of my documents to extract an image with SMask. The resulting image is visually identical to the original, though compositing might be lossy.
However, I think the shell script by @tzahola is better, since it is lossless and more general, but I wanted to share the approach how it could be done with pikepdf/PIL.
Tweaked the code a bit, and added support for transparent GIFs, and grayscale + alpha PNGs. Also, both the SMask and the RGB part uses the PNG predictor.
Convert 8-bit PNG alpha channels to /SMasks in PDFto Convert 8-bit PNG & GIF alpha channels to /SMasks in PDFI'm writing some test cases (
8dac6242fc
), and noticed that most of them are disabled on macOS. I've tried enabling them, and with a few exceptions they worked fine (given that the dependencies were installed via Homebrew), so I've removed thesys.platform
limitation from those (9cd5121477
).However, I'm encountering an issue where some TIFF test cases are checking the
depth
field reported by ImageMagick differently, depending on its version. Namely, this one:I have
ImageMagick 7.1.0-0 Q16
installed, which reports1.0
as theversion
field in the JSON output, but both thedepth
andbaseDepth
fields have the same value, which is12
in this case. This seems to have been introduced recently by @josch in454d4e7775
, so I'm wondering if you could help me with some context on this conditional? Maybe it should be the other way around? Or should be<=
instead of<
? I'd rather not tear down my env to install some older version of ImageMagick unless absolutely necessary... :Dsmask
6516339ae3I am wondering whether converting to RGBA is really lossless? I know very little about images...
The only way we can end up here is if we have a palette-based PNG or GIF with transparency. The palette is basically a lookup table of RGB values, and the pixels are indices into this table. So converting to RGBA will simply look up the color value for each pixel from the palette. There should be no interpolation, color space conversion, etc. It just removes a layer of indirection.
Thanks for explaining!
In case @josch decides to merge this PR, how would I detect whether img2pdf can handle the image, or whether the alpha channel would still need to be removed?
6fbb6cd60d
to6e6987c982
"RGBA" (e.g. RGB PNG with alpha), "LA" (e.g. greyscale PNG with alpha), and "P + transparency in the info dict" (e.g. GIF with transparency) will work fine. I haven't yet seen a file that reported "PA" - do you have a sample file maybe for this?
I don't have a sample file, I just found that mode in the pillow docs, under the section "limited support for a few additional modes" (https://pillow.readthedocs.io/en/stable/handbook/concepts.html#concept-modes).
Yeah, I saw that too. To me it sounds like some ultra-esoteric use case, i.e. you use low-res paletted color (like GIF), but a full resolution alpha channel... (?) I wouldn't really bother supporting it.
So, the answer to your original question is, the file is not supported if
image.mode == "PA"
. 16-bit images with alpha channels are not supported either due to PIL being unable to properly deal with >8bit images, but since you're using PIL to validate the file, you can't detect them either, as PIL will e.g. report a 16-bit RGBA image simply as "L"... (img2pdf
uses tricks like reading PNG header bytes to detect these cases)@josch What's your opinion on this PR now?
I love it. This will definitely be part of the next img2pdf release. I'll probably name it 0.5.0 because support for transparancey is a major new feature. Unfortunately, I spent most of my time these days caring for my newborn daughter, so it might take a while before I get to merge your pull request. Thank you for your work!
No worries! :) Congratulations! 👶
From me too, congratulations :)
Take your time with the release, family is much more important than software
Hi,
could you rebase your commits on top of the current main branch? I merged some of the other merge requests which should fix compatibility with imagemagick 7 that you are running.
Would you be around to fix MacOS problems in case they happen on our CI?
https://travis-ci.com/github/josch/img2pdf
Thanks!
cheers, josch
6e6987c982
to8cbe03d486
Sure!
main
✅To find the reason for this, run the following:
This is essentially what happens in the function
tiff_rgb12_img
and we check the depth to make sure that the test input we created has the expected bit depth.What happens if you run the commands above?
For me it outputs the following:
Which is why these TIFF test cases are failing for me (i.e.
depth != 16
, whileversion == "1.0"
).This is my ImageMagick version:
With which ImageMagick version do you get
depth: 16
?If this is a difference between ImageMagick versions, then I think using the JSON schema version (i.e.
version < "1.0"
) is incorrect, and it should be done based on the version of ImageMagick itself instead. The schema version should be used for things like theendianess
vs.endianness
thing.I'm currently using 6.9.11.
Yes, that's probably correct. All of this will probably be a thing of the past in a couple more months, when imagemagick 7 is available everywhere.