Repository navigation
PPTX: a hyperlink on a whole shape (p:cNvPr/a:hlinkClick) is dropped #4782
Copy link
Copy link
Closed
Description
Bug
A hyperlink attached to a whole shape, written as a:hlinkClick under the shape's p:cNvPr (python-pptx: shape.click_action.hyperlink.address), does not reach the document: the shape's text item has hyperlink=None.
#4442 covers the run-level form (a:rPr/a:hlinkClick) and PR #4443 fixes that form only; this is the other place PowerPoint stores a link, and it is lost on 2.138.0 and on main.
Steps to reproduce
"""Reproduce: a PPTX hyperlink on a whole shape (p:cNvPr/a:hlinkClick) is lost; so is one on text runs (#4442)."""
import sys
from io import BytesIO
import docling
import pptx
from docling.datamodel.base_models import InputFormat
from docling.document_converter import DocumentConverter
from docling_core.types.doc import ContentLayer, TextItem
from docling_core.types.io import DocumentStream
from lxml import etree
from pptx import Presentation
from pptx.util import Inches
def build() -> BytesIO:
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])
paragraph = slide.shapes.add_textbox(Inches(1), Inches(1), Inches(6), Inches(1)).text_frame.paragraphs[0]
paragraph.add_run().text = "Read "
for word in ("the", " story"):
run = paragraph.add_run()
run.text = word
run.hyperlink.address = "https://example.com/story"
paragraph.add_run().text = " tonight."
button = slide.shapes.add_textbox(Inches(1), Inches(3), Inches(4), Inches(1))
button.text_frame.text = "Watch the talk"
button.click_action.hyperlink.address = "https://example.com/talk"
print(etree.tostring(button._element.nvSpPr.cNvPr, pretty_print=True).decode().strip())
buf = BytesIO()
prs.save(buf)
buf.seek(0)
return buf
result = DocumentConverter(allowed_formats=[InputFormat.PPTX]).convert(
DocumentStream(name="repro.pptx", stream=build())
)
print(f"docling {docling.__version__}, python-pptx {pptx.__version__}, python {sys.version.split()[0]}")
for item, _ in result.document.iterate_items(included_content_layers=set(ContentLayer)):
if isinstance(item, TextItem):
print(f"{item.label.value}: {item.text!r} hyperlink={item.hyperlink!r}")Output (the cNvPr print shows the link is in the file):
<p:cNvPr ... id="3" name="TextBox 2">
<a:hlinkClick r:id="rId3"/>
</p:cNvPr>
docling 2.138.0, python-pptx 1.0.2, python 3.14.8
paragraph: 'Read the story tonight.' hyperlink=None
paragraph: 'Watch the talk' hyperlink=None
(The first paragraph is the run-level loss of #4442; the second is this issue.)
Expected
The shape's text item carries the shape's link as its hyperlink, the way the DOCX backend carries a hyperlink run. Where a run inside the shape has its own link, the run's link wins for that run.
Docling version
2.138.0
Python version
3.14.8
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity