@@ -22,6 +22,7 @@ This file is part of the iText (R) project.
2222 */
2323package com .itextpdf .layout .renderer ;
2424
25+ import com .itextpdf .commons .datastructures .Tuple2 ;
2526import com .itextpdf .commons .utils .MessageFormatUtil ;
2627import com .itextpdf .io .logs .IoLogMessageConstant ;
2728import com .itextpdf .io .util .NumberUtil ;
@@ -37,12 +38,17 @@ This file is part of the iText (R) project.
3738import com .itextpdf .kernel .pdf .PdfName ;
3839import com .itextpdf .kernel .pdf .PdfNumber ;
3940import com .itextpdf .kernel .pdf .PdfPage ;
41+ import com .itextpdf .kernel .pdf .PdfVersion ;
4042import com .itextpdf .kernel .pdf .action .PdfAction ;
4143import com .itextpdf .kernel .pdf .annot .PdfAnnotation ;
4244import com .itextpdf .kernel .pdf .annot .PdfLinkAnnotation ;
4345import com .itextpdf .kernel .pdf .canvas .CanvasArtifact ;
4446import com .itextpdf .kernel .pdf .canvas .PdfCanvas ;
4547import com .itextpdf .kernel .pdf .extgstate .PdfExtGState ;
48+ import com .itextpdf .kernel .pdf .navigation .PdfStructureDestination ;
49+ import com .itextpdf .kernel .pdf .tagging .PdfStructElem ;
50+ import com .itextpdf .kernel .pdf .tagutils .TagStructureContext ;
51+ import com .itextpdf .kernel .pdf .tagutils .TagTreePointer ;
4652import com .itextpdf .kernel .pdf .xobject .PdfFormXObject ;
4753import com .itextpdf .kernel .pdf .xobject .PdfXObject ;
4854import com .itextpdf .layout .Document ;
@@ -135,6 +141,10 @@ public abstract class AbstractRenderer implements IRenderer {
135141
136142 private static final int ARC_QUARTER_CLOCKWISE_EXTENT = -90 ;
137143
144+ // For autoport
145+ private static final Tuple2 <String , PdfDictionary > CHECK_TUPLE2_TYPE =
146+ new Tuple2 <String , PdfDictionary >("" , new PdfDictionary ());
147+
138148 protected List <IRenderer > childRenderers = new ArrayList <>();
139149 protected List <IRenderer > positionedRenderers = new ArrayList <>();
140150 protected IPropertyContainer modelElement ;
@@ -1918,8 +1928,22 @@ protected void applyRelativePositioningTranslation(boolean reverse) {
19181928 }
19191929
19201930 protected void applyDestination (PdfDocument document ) {
1921- String destination = this .<String >getProperty (Property .DESTINATION );
1922- if (destination != null ) {
1931+ Object destination = this .<Object >getProperty (Property .DESTINATION );
1932+ if (destination == null ) {
1933+ return ;
1934+ }
1935+ String destinationName = null ;
1936+ PdfDictionary linkActionDict = null ;
1937+ if (destination instanceof String ) {
1938+ destinationName = (String )destination ;
1939+ } else if (CHECK_TUPLE2_TYPE .getClass ().equals (destination .getClass ())) {
1940+ // 'If' above is the only autoportable way it seems
1941+ Tuple2 <String , PdfDictionary > destTuple = (Tuple2 <String , PdfDictionary >)destination ;
1942+ destinationName = destTuple .getFirst ();
1943+ linkActionDict = destTuple .getSecond ();
1944+ }
1945+
1946+ if (destinationName != null ) {
19231947 int pageNumber = occupiedArea .getPageNumber ();
19241948 if (pageNumber < 1 || pageNumber > document .getNumberOfPages ()) {
19251949 Logger logger = LoggerFactory .getLogger (AbstractRenderer .class );
@@ -1935,10 +1959,19 @@ protected void applyDestination(PdfDocument document) {
19351959 array .add (new PdfNumber (occupiedArea .getBBox ().getX ()));
19361960 array .add (new PdfNumber (occupiedArea .getBBox ().getY () + occupiedArea .getBBox ().getHeight ()));
19371961 array .add (new PdfNumber (0 ));
1938- document .addNamedDestination (destination , array .makeIndirect (document ));
1962+ document .addNamedDestination (destinationName , array .makeIndirect (document ));
1963+ }
19391964
1940- deleteProperty (Property .DESTINATION );
1965+ final boolean isPdf20 = document .getPdfVersion ().compareTo (PdfVersion .PDF_2_0 ) >= 0 ;
1966+ if (linkActionDict != null && isPdf20 && document .isTagged ()) {
1967+ TagStructureContext context = document .getTagStructureContext ();
1968+ TagTreePointer tagPointer = context .getAutoTaggingPointer ();
1969+ PdfStructElem structElem = context .getPointerStructElem (tagPointer );
1970+ PdfStructureDestination dest = PdfStructureDestination .createFit (structElem );
1971+ linkActionDict .put (PdfName .SD , dest .getPdfObject ());
19411972 }
1973+
1974+ deleteProperty (Property .DESTINATION );
19421975 }
19431976
19441977 protected void applyAction (PdfDocument document ) {
@@ -1962,32 +1995,35 @@ protected void applyAction(PdfDocument document) {
19621995 protected void applyLinkAnnotation (PdfDocument document ) {
19631996 Logger logger = LoggerFactory .getLogger (AbstractRenderer .class );
19641997 PdfLinkAnnotation linkAnnotation = this .<PdfLinkAnnotation >getProperty (Property .LINK_ANNOTATION );
1965- if (linkAnnotation != null ) {
1966- int pageNumber = occupiedArea .getPageNumber ();
1967- if (pageNumber < 1 || pageNumber > document .getNumberOfPages ()) {
1968- String logMessageArg = "Property.LINK_ANNOTATION, which specifies a link associated with this element content area, see com.itextpdf.layout.element.Link." ;
1969- logger .warn (MessageFormatUtil .format (
1970- IoLogMessageConstant .UNABLE_TO_APPLY_PAGE_DEPENDENT_PROP_UNKNOWN_PAGE_ON_WHICH_ELEMENT_IS_DRAWN ,
1971- logMessageArg ));
1972- return ;
1973- }
1974- // If an element with a link annotation occupies more than two pages,
1975- // then a NPE might occur, because of the annotation being partially flushed.
1976- // That's why we create and use an annotation's copy.
1977- PdfDictionary oldAnnotation = (PdfDictionary ) linkAnnotation .getPdfObject ().clone ();
1978- linkAnnotation = (PdfLinkAnnotation ) PdfAnnotation .makeAnnotation (oldAnnotation );
1979- Rectangle pdfBBox = calculateAbsolutePdfBBox ();
1980- linkAnnotation .setRectangle (new PdfArray (pdfBBox ));
1981-
1982- PdfPage page = document .getPage (pageNumber );
1983- // TODO DEVSIX-1655 This check is necessary because, in some cases, our renderer's hierarchy may contain
1984- // a renderer from the different page that was already flushed
1985- if (page .isFlushed ()) {
1986- logger .error (MessageFormatUtil .format (
1987- IoLogMessageConstant .PAGE_WAS_FLUSHED_ACTION_WILL_NOT_BE_PERFORMED , "link annotation applying" ));
1988- } else {
1989- page .addAnnotation (linkAnnotation );
1990- }
1998+ if (linkAnnotation == null ) {
1999+ return ;
2000+ }
2001+
2002+ int pageNumber = occupiedArea .getPageNumber ();
2003+ if (pageNumber < 1 || pageNumber > document .getNumberOfPages ()) {
2004+ String logMessageArg = "Property.LINK_ANNOTATION, which specifies a link associated with this element content area, see com.itextpdf.layout.element.Link." ;
2005+ logger .warn (MessageFormatUtil .format (
2006+ IoLogMessageConstant .UNABLE_TO_APPLY_PAGE_DEPENDENT_PROP_UNKNOWN_PAGE_ON_WHICH_ELEMENT_IS_DRAWN ,
2007+ logMessageArg ));
2008+ return ;
2009+ }
2010+
2011+ // If an element with a link annotation occupies more than two pages,
2012+ // then a NPE might occur, because of the annotation being partially flushed.
2013+ // That's why we create and use an annotation's copy.
2014+ PdfDictionary newAnnotation = (PdfDictionary ) linkAnnotation .getPdfObject ().clone ();
2015+ linkAnnotation = (PdfLinkAnnotation ) PdfAnnotation .makeAnnotation (newAnnotation );
2016+ Rectangle pdfBBox = calculateAbsolutePdfBBox ();
2017+ linkAnnotation .setRectangle (new PdfArray (pdfBBox ));
2018+
2019+ PdfPage page = document .getPage (pageNumber );
2020+ // TODO DEVSIX-1655 This check is necessary because, in some cases, our renderer's hierarchy may contain
2021+ // a renderer from the different page that was already flushed
2022+ if (page .isFlushed ()) {
2023+ logger .error (MessageFormatUtil .format (
2024+ IoLogMessageConstant .PAGE_WAS_FLUSHED_ACTION_WILL_NOT_BE_PERFORMED , "link annotation applying" ));
2025+ } else {
2026+ page .addAnnotation (linkAnnotation );
19912027 }
19922028 }
19932029
0 commit comments