What I'm trying to do
Export a tagged PDF (PDF/UA) where a hyperlink covers only part of a sentence, e.g.:
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
where only "adipiscing elit" should be a clickable link, and that link should be a properly nested Link structure element (not just a clickable /Link annotation), otherwise PDF/UA checkers (e.g. PAC) report: "Link annotation is not nested inside a Link structure element."
What works: linking the whole element
When the entire text of an element is the link target, this tags correctly:
<element kind="textField" uuid="f44d8ccb-580c-495c-9ac5-bc3b6bade20a"
x="0" y="0" width="200" height="15"
linkType="Reference" linkTarget="Self">
<expression><![CDATA["lorem-ipsum.example/dolor"]]></expression>
<hyperlinkReferenceExpression><![CDATA["https://lorem-ipsum.example/dolor"]]></hyperlinkReferenceExpression>
</element>
Inspecting the exported PDF's structure tree confirms the /Link annotation carries a /StructParent, and the corresponding Link tag in the structure tree references it back via OBJR. This matches how JRPrintText#getLinkType() is checked at the whole-element level by the tag-helper logic (JRPdfExporterTagHelper in the 7.0.7 CE implementation, or any PdfTagger SPI implementation since 7.0.8).
What doesn't tag correctly: inline <a> inside styled/mixed text
JasperReports' styled markup also lets you embed a hyperlink as an inline anchor inside a text element's rich content, e.g.:
<element kind="textField" uuid="9416616e-df13-452c-930e-5c53cc3944f4"
x="0" y="0" width="400" height="15" markup="styled">
<expression><![CDATA[
"Lorem ipsum dolor sit amet, consectetur "
+ "<a href=\"https://lorem-ipsum.example/adipiscing\">adipiscing elit</a>."
]]></expression>
</element>
This does paint a real, clickable /Link annotation for "adipiscing elit" at export time, but it is never wrapped in a matching Link structure tag. I traced this to the net.sf.jasperreports.pdf.common.PdfTagger SPI itself: startText(JRPrintText) / startText(JRPrintText, String) only ever receive the whole print element and check its getLinkType(); there's no callback for a hyperlink run embedded inside styled text. So this can't be fixed from a custom PdfTagger plugin alone since the gap is in how JRPdfExporter paints inline anchor runs, upstream of the tagger hook.
The only workaround I've found: an invisible overlay hotspot
Render the visible text as a plain styled run that only looks like a link (underline + link color, no real hyperlink), then overlay a second, invisible, transparent element, hand-positioned/sized to match the rendered substring, that carries the real linkType:
<element kind="frame" uuid="a460861e-4047-4453-a4f2-054537353f2d" x="0" y="0" width="400" height="15">
<!-- Visible text: styled to look like a link, carries no real hyperlink -->
<element kind="textField" uuid="924ed60a-c6e4-4a80-8e3f-3119389a83dd"
x="0" y="0" width="400" height="15" markup="styled">
<expression><![CDATA[
"Lorem ipsum dolor sit amet, consectetur "
+ "<style isUnderline='true' forecolor='#0000EE'>adipiscing elit</style>."
]]></expression>
</element>
<!-- Invisible overlay carrying the real link, manually positioned under "adipiscing elit" -->
<element kind="textField" uuid="851a7d63-a1e3-4dd4-92e0-4d083c603fe1"
x="238" y="0" width="70" height="15"
mode="Transparent" forecolor="rgba(255,255,255,0.0)"
linkType="Reference" linkTarget="Self">
<expression><![CDATA["adipiscing elit"]]></expression>
<hyperlinkReferenceExpression><![CDATA["https://lorem-ipsum.example/adipiscing"]]></hyperlinkReferenceExpression>
</element>
</element>
This does tag correctly (the invisible element is, itself, a whole-element hyperlink), but it has real downsides:
- Brittle positioning. The overlay's
x/width must be hand-measured in pixels against the rendered width of the visible substring. Any change in translated text length, font substitution, or font size breaks the alignment silently.
- Duplicated text. The same words exist twice in the content stream: once as a plain, non-interactive visible run, once as an invisible interactive run. Depending on how assistive tech handles zero-alpha/transparent text, this risks duplicate or confusing announcements, and shows up oddly in text extraction/copy-paste.
- Doesn't scale. Every "partial sentence is a link" occurrence needs its own manually tuned overlay; there's no way to parametrize this for dynamic, data-driven, or localized content.
The question
Is there a supported, first-class way to tag a hyperlink that covers only part of a mixed/styled text run as a proper Link structure element I'm missing? If not, would a feature request be in scope here: either extending the PdfTagger SPI with a per-run hyperlink hook, or having the built-in inline-<a>-to-annotation code path set /StructParent/OBJR the same way whole-element hyperlinks already do?
Environment: JasperReports 7.0.8, PDF export via jasperreports-pdf, tagged export enabled (net.sf.jasperreports.export.pdf.tagged=true).
What I'm trying to do
Export a tagged PDF (PDF/UA) where a hyperlink covers only part of a sentence, e.g.:
where only "adipiscing elit" should be a clickable link, and that link should be a properly nested
Linkstructure element (not just a clickable/Linkannotation), otherwise PDF/UA checkers (e.g. PAC) report: "Link annotation is not nested inside a Link structure element."What works: linking the whole element
When the entire text of an element is the link target, this tags correctly:
Inspecting the exported PDF's structure tree confirms the
/Linkannotation carries a/StructParent, and the correspondingLinktag in the structure tree references it back viaOBJR. This matches howJRPrintText#getLinkType()is checked at the whole-element level by the tag-helper logic (JRPdfExporterTagHelperin the 7.0.7 CE implementation, or anyPdfTaggerSPI implementation since 7.0.8).What doesn't tag correctly: inline
<a>inside styled/mixed textJasperReports' styled markup also lets you embed a hyperlink as an inline anchor inside a text element's rich content, e.g.:
This does paint a real, clickable
/Linkannotation for "adipiscing elit" at export time, but it is never wrapped in a matchingLinkstructure tag. I traced this to thenet.sf.jasperreports.pdf.common.PdfTaggerSPI itself:startText(JRPrintText)/startText(JRPrintText, String)only ever receive the whole print element and check itsgetLinkType(); there's no callback for a hyperlink run embedded inside styled text. So this can't be fixed from a customPdfTaggerplugin alone since the gap is in howJRPdfExporterpaints inline anchor runs, upstream of the tagger hook.The only workaround I've found: an invisible overlay hotspot
Render the visible text as a plain styled run that only looks like a link (underline + link color, no real hyperlink), then overlay a second, invisible, transparent element, hand-positioned/sized to match the rendered substring, that carries the real
linkType:This does tag correctly (the invisible element is, itself, a whole-element hyperlink), but it has real downsides:
x/widthmust be hand-measured in pixels against the rendered width of the visible substring. Any change in translated text length, font substitution, or font size breaks the alignment silently.The question
Is there a supported, first-class way to tag a hyperlink that covers only part of a mixed/styled text run as a proper
Linkstructure element I'm missing? If not, would a feature request be in scope here: either extending thePdfTaggerSPI with a per-run hyperlink hook, or having the built-in inline-<a>-to-annotation code path set/StructParent/OBJRthe same way whole-element hyperlinks already do?Environment: JasperReports 7.0.8, PDF export via
jasperreports-pdf, tagged export enabled (net.sf.jasperreports.export.pdf.tagged=true).