From 762d5019fb1eba42bf547b738abeb1e4833ea0b5 Mon Sep 17 00:00:00 2001 From: usmanmateen Date: Thu, 8 Oct 2026 16:57:34 +0100 Subject: [PATCH 1/2] fix(latex): keep CMYK JPEG figures PNG can't store CMYK, so ImageRef.from_pil raised for a CMYK \includegraphics figure and the handler added the picture without an image, with only a debug log. Normalize the figure first, like the XLSX, OpenDocument, DOCX and PPTX backends do. Resolves #4695 Signed-off-by: usmanmateen Co-Authored-By: Claude Opus 5.5 --- docling/backend/latex/handlers/macros.py | 5 +++- tests/test_latex/test_figures.py | 29 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/docling/backend/latex/handlers/macros.py b/docling/backend/latex/handlers/macros.py index 26896e177a..41df15d8c9 100644 --- a/docling/backend/latex/handlers/macros.py +++ b/docling/backend/latex/handlers/macros.py @@ -38,6 +38,7 @@ from docling.backend.latex.utils.encoding import decode_latex_content from docling.backend.latex.utils.latex_context import LATEX_CONTEXT_DB from docling.backend.latex.utils.text import iter_child_nodelists +from docling.backend.utils.image import normalize_image_for_png if TYPE_CHECKING: from typing import Any @@ -374,7 +375,9 @@ def _process_macro( # noqa: C901 _log.debug( f"Loaded image {img_path}: {pil_image.size}, DPI={dpi}" ) - image = ImageRef.from_pil(image=pil_image, dpi=int(dpi)) + image = ImageRef.from_pil( + image=normalize_image_for_png(pil_image), dpi=int(dpi) + ) except Exception as e: _log.debug(f"Could not load image {img_path}: {e}") diff --git a/tests/test_latex/test_figures.py b/tests/test_latex/test_figures.py index cc412d3fa6..e74b9b8644 100644 --- a/tests/test_latex/test_figures.py +++ b/tests/test_latex/test_figures.py @@ -119,6 +119,35 @@ def test_latex_includegraphics(): assert "test_image.png" in picture.captions[0].resolve(doc).text +def test_latex_includegraphics_cmyk_jpeg(tmp_path: Path): + """A CMYK JPEG figure keeps its image; PNG cannot store CMYK.""" + from PIL import Image as PILImage + + PILImage.new("CMYK", (100, 50), (0, 255, 255, 0)).save(tmp_path / "fig.jpg") + tex_file = tmp_path / "test.tex" + tex_file.write_bytes( + b"\\documentclass{article}\n\\begin{document}\n" + b"\\includegraphics{fig.jpg}\n\\end{document}\n" + ) + + in_doc = InputDocument( + path_or_stream=tex_file, + format=InputFormat.LATEX, + backend=LatexDocumentBackend, + filename="test.tex", + ) + doc = LatexDocumentBackend(in_doc=in_doc, path_or_stream=tex_file).convert() + + assert len(doc.pictures) == 1 + image = doc.pictures[0].get_image(doc) + assert image is not None + assert image.mode == "RGB" + pixel = image.getpixel((50, 25)) + assert isinstance(pixel, tuple) + red, green, blue = pixel + assert red > 200 and green < 50 and blue < 50 + + def test_latex_includegraphics_missing_image(): """Test includegraphics gracefully handles missing images""" latex_content = b""" From 46d0517068107ff3bebddd1d3cb00c08c92589f4 Mon Sep 17 00:00:00 2001 From: usmanmateen Date: Thu, 8 Oct 2026 17:03:51 +0100 Subject: [PATCH 2/2] test(latex): compare the CMYK figure pixel exactly The CMYK colour decodes to exactly (255, 0, 0), so compare it directly, like the OpenDocument test does, instead of loose channel thresholds. Signed-off-by: usmanmateen Co-Authored-By: Claude Opus 5.5 --- tests/test_latex/test_figures.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_latex/test_figures.py b/tests/test_latex/test_figures.py index e74b9b8644..6740af6882 100644 --- a/tests/test_latex/test_figures.py +++ b/tests/test_latex/test_figures.py @@ -142,10 +142,7 @@ def test_latex_includegraphics_cmyk_jpeg(tmp_path: Path): image = doc.pictures[0].get_image(doc) assert image is not None assert image.mode == "RGB" - pixel = image.getpixel((50, 25)) - assert isinstance(pixel, tuple) - red, green, blue = pixel - assert red > 200 and green < 50 and blue < 50 + assert image.getpixel((50, 25)) == (255, 0, 0) def test_latex_includegraphics_missing_image():