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

Added support for embedding indexed images #443

Merged
merged 23 commits into from May 27, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions fpdf/fpdf.py
Expand Up @@ -4044,11 +4044,11 @@ def _putimage(self, info):
# Palette
if info["cs"] == "Indexed":
self._newobj()
filter, pal = (
("/Filter /FlateDecode ", zlib.compress(info["pal"]))
if self.compress
else ("", info["pal"])
)
if self.compress:
filter, pal = ("/Filter /FlateDecode ", zlib.compress(info["pal"]))
else:
filter, pal = ("", info["pal"])
self._out(f"<<{filter}/Length {len(pal)}>>")
self._out(pdf_stream(pal))
self._out("endobj")
Expand Down
30 changes: 28 additions & 2 deletions fpdf/image_parsing.py
Expand Up @@ -56,15 +56,20 @@ def get_img_info(img, image_filter="AUTO", dims=None):
"""
if Image is None:
raise EnvironmentError("Pillow not available - fpdf2 cannot insert images")

if not isinstance(img, Image.Image):
img = Image.open(img)

if dims:
img = img.resize(dims, resample=RESAMPLE)

if image_filter == "AUTO":
# Very simple logic for now:
image_filter = "DCTDecode" if img.format == "JPEG" else "FlateDecode"
if img.mode not in ("L", "LA", "RGB", "RGBA"):

if img.mode not in ("L", "LA", "RGB", "RGBA", "P", "PA"):
img = img.convert("RGBA")

w, h = img.size
info = {}
if img.mode == "L":
Expand All @@ -79,6 +84,20 @@ def get_img_info(img, image_filter="AUTO", dims=None):
"JPXDecode",
):
info["smask"] = _to_data(img, image_filter, select_slice=alpha_channel)
elif img.mode == "P":
dpn, bpc, colspace = 1, 8, "Indexed"
info["data"] = _to_data(img, image_filter)
info["pal"] = img.palette.palette
elif img.mode == "PA":
dpn, bpc, colspace = 1, 8, "Indexed"
info["pal"] = img.palette.palette
alpha_channel = slice(1, None, 2)
info["data"] = _to_data(img, image_filter, remove_slice=alpha_channel)
if _has_alpha(img, alpha_channel) and image_filter not in (
"DCTDecode",
"JPXDecode",
):
info["smask"] = _to_data(img, image_filter, select_slice=alpha_channel)
elif img.mode == "RGB":
dpn, bpc, colspace = 3, 8, "DeviceRGB"
info["data"] = _to_data(img, image_filter)
Expand All @@ -102,7 +121,6 @@ def get_img_info(img, image_filter="AUTO", dims=None):
"bpc": bpc,
"f": image_filter,
"dp": dp,
"pal": "",
"trns": "",
}
)
Expand All @@ -113,18 +131,26 @@ def get_img_info(img, image_filter="AUTO", dims=None):
def _to_data(img, image_filter, **kwargs):
if image_filter == "FlateDecode":
return _to_zdata(img, **kwargs)

if img.mode == "LA":
img = img.convert("L")

if img.mode == "RGBA":
img = img.convert("RGB")

if img.mode == "PA":
img = img.convert("P")

if image_filter == "DCTDecode":
compressed_bytes = BytesIO()
img.save(compressed_bytes, format="JPEG")
return compressed_bytes.getvalue()

if image_filter == "JPXDecode":
compressed_bytes = BytesIO()
img.save(compressed_bytes, format="JPEG2000")
return compressed_bytes.getvalue()

raise FPDFException(f'Unsupported image filter: "{image_filter}"')


Expand Down
Empty file.
Binary file added test/image/png_indexed/image_png_indexed.pdf
Binary file not shown.
Binary file added test/image/png_indexed/palette_1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/image/png_indexed/palette_2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/image/png_indexed/palette_3.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 46 additions & 0 deletions test/image/png_indexed/test_png_indexed.py
@@ -0,0 +1,46 @@
from pathlib import Path

import fpdf
from test.conftest import assert_pdf_equal
from PIL import Image


HERE = Path(__file__).resolve().parent


def test_png_indexed_files(tmp_path):
pdf = fpdf.FPDF()
pdf.add_page()

# palette images (P)
pdf.image(HERE / "palette_1.png", x=10, y=10, w=50, h=50, link=None)
RedShy marked this conversation as resolved.
Show resolved Hide resolved
pdf.image(HERE / "palette_2.png", x=80, y=10, w=50, h=50, link=None)
pdf.image(HERE / "palette_3.png", x=150, y=10, w=50, h=50, link=None)

# palette with alpha images (PA)
pdf.image(
Image.open(HERE / "palette_1.png").convert("PA"),
x=10,
y=80,
w=50,
h=50,
link=None,
)
pdf.image(
Image.open(HERE / "palette_2.png").convert("PA"),
x=80,
y=80,
w=50,
h=50,
link=None,
)
pdf.image(
Image.open(HERE / "palette_3.png").convert("PA"),
x=150,
y=80,
w=50,
h=50,
link=None,
)

assert_pdf_equal(pdf, HERE / "image_png_indexed.pdf", tmp_path)