From 04b79785c2e56c11365ec45a6d8f1609294f6b20 Mon Sep 17 00:00:00 2001 From: chrikrah <48338417+chrikrah@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:30:57 +0000 Subject: [PATCH] fix(pptx): keep the document when a 3-D area chart has no series class python-pptx registers both an element class and a plot class for `c:area3DChart`, but no series class, so reading its series raises `NotImplementedError`. The other 3-D plots raise `ValueError` or `AttributeError` from `chart.chart_type`, which the guard above already catches, so `c:area3DChart` was the one chart type whose failure escaped `_handle_chart` and aborted the conversion of the whole presentation. Catch it where the other failures are caught, so the chart degrades to a classified picture without tabular data, exactly as `c:bar3DChart` does. Signed-off-by: chrikrah <48338417+chrikrah@users.noreply.github.com> --- docling/backend/mspowerpoint_backend.py | 6 ++- tests/test_backend_pptx.py | 50 +++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/docling/backend/mspowerpoint_backend.py b/docling/backend/mspowerpoint_backend.py index 1bbc75ec89..b01940d335 100644 --- a/docling/backend/mspowerpoint_backend.py +++ b/docling/backend/mspowerpoint_backend.py @@ -1153,7 +1153,11 @@ def _handle_chart(self, shape, parent_slide, slide_ind, doc, slide_size): caption_text = self._chart_title_text(chart) try: table_data = self._chart_to_table_data(chart) - except (AttributeError, ValueError, KeyError) as exc: + except (AttributeError, ValueError, KeyError, NotImplementedError) as exc: + # NotImplementedError: python-pptx registers an element class and a + # plot class for c:area3DChart but no series class, so reading its + # series raises where every other 3-D plot raises earlier and is + # already caught above. _log.warning("Could not extract chart data: %s", exc) table_data = None diff --git a/tests/test_backend_pptx.py b/tests/test_backend_pptx.py index b6a08e4973..a63b6f5a07 100644 --- a/tests/test_backend_pptx.py +++ b/tests/test_backend_pptx.py @@ -4,6 +4,7 @@ import logging import struct import warnings +import zipfile import zlib from collections.abc import Iterable from pathlib import Path @@ -448,6 +449,55 @@ def test_chart_image_rendering(libreoffice_available): ) +def test_area_3d_chart_degrades_instead_of_failing_the_document(tmp_path: Path): + """A ``c:area3DChart`` must lose only its data, not the whole presentation. + + python-pptx has no series class for a 3-D area chart, so reading its series + raises ``NotImplementedError``. Every other 3-D plot raises earlier, where + it is already caught, so this one chart aborted the conversion of the entire + deck. It should degrade the way ``c:bar3DChart`` does: a classified picture + with no tabular data. + """ + from pptx import Presentation + from pptx.chart.data import CategoryChartData + from pptx.enum.chart import XL_CHART_TYPE + from pptx.util import Inches + + prs = Presentation() + slide = prs.slides.add_slide(prs.slide_layouts[6]) + chart_data = CategoryChartData() + chart_data.categories = ["Q1", "Q2"] + chart_data.add_series("Revenue", (10.0, 20.0)) + slide.shapes.add_chart( + XL_CHART_TYPE.AREA, Inches(1), Inches(1), Inches(4), Inches(3), chart_data + ) + slide.shapes.add_textbox( + Inches(0.2), Inches(0.2), Inches(3), Inches(0.5) + ).text_frame.text = "text beside the chart" + flat = tmp_path / "area2d.pptx" + prs.save(flat) + + # python-pptx cannot write a 3-D area chart, so retag the plot element the + # way PowerPoint writes one. + source = tmp_path / "area3d.pptx" + with ( + zipfile.ZipFile(flat) as original, + zipfile.ZipFile(source, "w", zipfile.ZIP_DEFLATED) as retagged, + ): + for info in original.infolist(): + payload = original.read(info.filename) + if info.filename == "ppt/charts/chart1.xml": + payload = payload.replace(b"c:areaChart", b"c:area3DChart") + retagged.writestr(info, payload) + + doc = get_converter().convert(source).document + + pictures = list(doc.pictures) + assert len(pictures) == 1 + assert pictures[0].meta.tabular_chart is None + assert "text beside the chart" in doc.export_to_markdown() + + def _add_bar_chart(shapes): """Add a small bar chart to a slide or group shape tree.""" from pptx.chart.data import CategoryChartData