Skip to content
cjhaas blog

Basically a place that Chris can post solutions to problems so he can easily find them later

cjhaas blog

Basically a place that Chris can post solutions to problems so he can easily find them later

Convert iTextSharp Hyperlink from remote webpage to local page number

Posted on January 27, 2012 By [email protected]

This post is in response to a comment here.

Let’s say you have a PDF with hyperlinks pointing to URLs like http://www.bing.com and you want to make these instead point to a page internal to the PDF. (Personally I can’t think of why this would be needed but someone apparently has this need.)

We’ll use the PDF annotation code that I posted on Stack Overflow here and modify it just a little bit. The code below is written in VB.Net 2010 and targets iTextSharp 5.1.2.0. See the individual code comments for specifics. If you have any questions you can leave a comment here but its probably faster to post your code and problems on Stack Overflow and just link to this post.

First, we’ll create some global variables to work with:

    ''//Folder that we are working in
    Private Shared ReadOnly WorkingFolder As String = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Hyperlinked PDFs")
    ''//Pdf with sample hyperlinks
    Private Shared ReadOnly BaseFile As String = Path.Combine(WorkingFolder, "Base.pdf")
    ''//Pdf with adjusted hyperlinks
    Private Shared ReadOnly FinalFile As String = Path.Combine(WorkingFolder, "Final.pdf")

Next we’ll create a sample PDF that we can modify URLs with later. Nothing really special here, should be self-explanatory hopefully.

    Private Shared Sub CreateSamplePdf()
        ''//Create our output directory if it does not exist
        Directory.CreateDirectory(WorkingFolder)

        ''//Create our sample PDF
        Using Doc As New iTextSharp.text.Document(PageSize.LETTER)
            Using FS As New FileStream(BaseFile, FileMode.Create, FileAccess.Write, FileShare.Read)
                Using writer = PdfWriter.GetInstance(Doc, FS)
                    Doc.Open()

                    ''//Turn our hyperlinks blue
                    Dim BlueFont As Font = FontFactory.GetFont("Arial", 12, iTextSharp.text.Font.NORMAL, iTextSharp.text.BaseColor.BLUE)

                    ''//Create 10 pages with simple labels on them
                    For I = 1 To 10
                        Doc.NewPage()
                        Doc.Add(New Paragraph(String.Format("Page {0}", I)))
                        ''//On the first page add some links
                        If I = 1 Then
                           ''//Add an external link
                            Doc.Add(New Paragraph(New Chunk("Go to website", BlueFont).SetAction(New PdfAction("http://www.bing.com/", False))))

                            ''//Go to a specific hard-coded page number
                            Doc.Add(New Paragraph(New Chunk("Go to page 5", BlueFont).SetAction(PdfAction.GotoLocalPage(5, New PdfDestination(0), writer))))
                        End If
                    Next
                    Doc.Close()
                End Using
            End Using
        End Using
    End Sub

Lastly we’ll write some code to modify all of the external hyperlinks. The key here is to update the annotation’s dictionary reference for /S. A remote URL has a /URI (NOTE: the letter I and not L, “eye” not “el”) and we need to change this to /GOTO. The second trick is that the destination (/D) is an array, of which the first item is an indirect reference to the page that you want to go to and the second item is a fitting option.

    Private Shared Sub ListPdfLinks()

        ''//Setup some variables to be used later
        Dim R As PdfReader
        Dim PageCount As Integer
        Dim PageDictionary As PdfDictionary
        Dim Annots As PdfArray

        ''//Open our reader
        R = New PdfReader(BaseFile)
        ''//Get the page cont
        PageCount = R.NumberOfPages

        ''//Loop through each page
        For I = 1 To PageCount
            ''//Get the current page
            PageDictionary = R.GetPageN(I)

            ''//Get all of the annotations for the current page
            Annots = PageDictionary.GetAsArray(PdfName.ANNOTS)

            ''//Make sure we have something
            If (Annots Is Nothing) OrElse (Annots.Length = 0) Then Continue For

            ''//Loop through each annotation
            For Each A In Annots.ArrayList

                ''//Convert the itext-specific object as a generic PDF object
                Dim AnnotationDictionary = DirectCast(PdfReader.GetPdfObject(A), PdfDictionary)

                ''//Make sure this annotation has a link
                If Not AnnotationDictionary.Get(PdfName.SUBTYPE).Equals(PdfName.LINK) Then Continue For

                ''//Make sure this annotation has an ACTION
                If AnnotationDictionary.Get(PdfName.A) Is Nothing Then Continue For

                ''//Get the ACTION for the current annotation
                Dim AnnotationAction = DirectCast(AnnotationDictionary.Get(PdfName.A), PdfDictionary)

                ''//Test if it is a URI action. NOTE: URI and not URL
                If AnnotationAction.Get(PdfName.S).Equals(PdfName.URI) Then
                    ''//Remove the old action, I don't think this is actually necessary but I do it anyways
                    AnnotationAction.Remove(PdfName.S)
                    ''//Add a new action that is a GOTO action
                    AnnotationAction.Put(PdfName.S, PdfName.GOTO)
                    ''//The destination is an array containing an indirect reference to the page as well as a fitting option
                    Dim NewLocalDestination As New PdfArray()
                    ''//Link it to page 5
                    NewLocalDestination.Add(DirectCast(R.GetPageOrigRef(5), PdfObject))
                    ''//Set it to fit page
                    NewLocalDestination.Add(PdfName.FIT)
                    ''//Add the array to the annotation's destination (/D)
                    AnnotationAction.Put(PdfName.D, NewLocalDestination)
                End If
            Next
        Next

        ''//The above code modified an im-memory representation of a PDF, the code below writes these changes to disk
        Using FS As New FileStream(FinalFile, FileMode.Create, FileAccess.Write, FileShare.None)
            Using Doc As New Document()
                Using writer As New PdfCopy(Doc, FS)
                    Doc.Open()
                    For I = 1 To R.NumberOfPages
                        writer.AddPage(writer.GetImportedPage(R, I))
                    Next
                    Doc.Close()
                End Using
            End Using
        End Using
    End Sub
Uncategorized iTextSharp

Post navigation

Previous post
Next post

Comments (13)

  1. Jeremy (from Australia) says:
    January 29, 2012 at 6:02 pm

    PERFECT !! Thank you so much Chris. The reason I need this is because I am combining several pdf documents into one document with an index page – which currently exists with hyperlinks pointing to the external pdf document locations. Now that all the documents are contained in one document I need to change the links so that they go to the appropriate page in the combined document.

    Reply
  2. Jeremy (from Australia) says:
    February 2, 2012 at 9:39 am

    (Comment ported from old site)

    Hi Chris, I have another ItextSharp problem – if you have the time and interest I’d appreciate any help. I am adding a non-pdf file attachment using PdfAnnotation.CreateFileAttachment(…Then creating a chunk of text and settingAnnotation on the chunk. Then putting on a paperclip icon for the user to double-click so that they can open the non-pdf file. The users have asked if I could instead create a “one click” hotspot hyperlink to the attached file. There’s nothing in the ItextInAction book and ITextSupport haven’t been helpful. I’ve been googling and Paulo Soares replied “That’s possible with an /AA dictionary and trigger events.” to a similar question but no code example was shown. Once again I’d appreciate any help or hints….

    Reply
    1. Chris Haas says:
      February 2, 2012 at 9:41 am

      So basically you want to turn a double-click into a single click, right?

      Reply
  3. Jeremy (from Australia) says:
    February 2, 2012 at 5:01 pm

    In a nutshell – yes.

    Reply
    1. Chris Haas says:
      February 3, 2012 at 8:12 am

      Sorry Jeremy, you’ve got me stumped on this one for now! I know how to add an /AA but I don’t know how to add one that launches a file attachment. If you do find out let me know!

      Reply
  4. Jeremy (from Australia) says:
    February 2, 2012 at 5:50 pm

    With the hotspot “hand” if possible to simulate hyperlink behaviour.

    Reply
  5. Jeremy says:
    February 6, 2012 at 8:36 pm

    Thanks anyway Chris. They’ll just have to live with the double-click šŸ™‚

    Reply
  6. Giuseppe Zarbo says:
    April 11, 2012 at 7:42 am

    Hi Chris,
    your post is very interesting!But I have a problem, how can I change a link of an existent anchor?
    Thanks

    Reply
    1. Chris Haas says:
      April 11, 2012 at 8:07 am

      See my post here for changing hyperlinks in PDFs

      Reply
  7. Giuseppe Zarbo says:
    April 11, 2012 at 8:16 am

    Hi, yes I’m watching your example:
    if (AnnotationAction.Get(PdfName.S).Equals(PdfName.URI))
    {
    AnnotationAction.Put(PdfName.URI, new PdfString(“http://www.bing.com/”));
    }
    but I need to do this:
    if (AnnotationAction.Get(PdfName.S).Equals(PdfName.GOTO))
    {
    AnnotationAction.Put(PdfName.GOTO, ???);
    }

    I start from html (a template) to generate a pdf document.
    In my html if I have an anchor when is converted to pdf is work fine. someText is converted to an anchor in pdf document.
    I want to change #nameTag, but I know that is different from html, so I think I have to change the object referred into AnnotationAction.
    Any ideas?

    Thanks

    Reply
  8. Giuseppe Zarbo says:
    April 25, 2012 at 8:01 am

    Any news?
    Thanks

    Reply
  9. brianCT says:
    June 13, 2012 at 3:47 pm

    Hi Chris I was reading a question in Stackoverflow and you wrote an very useful answer.
    if you have the time and interest I’d appreciate any help.

    I’m working on a project where i need to edit pdf’s before display it

    I need

    – add a watermark
    – edit permissions ( lock for avoid ‘copy/paste’, ‘save as’ and ‘print’ )
    – edit viewer preferences

    And i did it… and work fine except for one thing, the links in the original file does not work in the new file… any idea?

    NOTE: Actually, this is my code ( i’m using itextsharp )

    private void loadPdf()
    {
    if (Request.QueryString.HasKeys())
    {
    if (Request.QueryString.GetKey(0) == “thepath” && Request.QueryString.GetKey(1) == “isprintable” && Request.QueryString.GetKey(2) == “type”)
    {
    #region kuak
    Document doc = new Document();
    PdfReader pdfReader = new PdfReader(Request.QueryString[“thepath”]);
    using (MemoryStream memoryStream = new MemoryStream())
    {
    PdfWriter pdfWriter = PdfWriter.GetInstance(doc, memoryStream);
    pdfWriter.ViewerPreferences = PdfWriter.PageModeUseOutlines;
    //pdfWriter.ViewerPreferences = PdfWriter.PageLayoutTwoColumnLeft; /// Despliega el docuemnto en pares de hojas
    pdfWriter.ViewerPreferences = PdfWriter.PageLayoutOneColumn;
    pdfWriter.ViewerPreferences = PdfWriter.HideToolbar;
    //pdfWriter.ViewerPreferences = PdfWriter.HideWindowUI; /// quita los scrollbars y el panel de la derecha qur contiene los bookmarks y las buskedas dentro del pdf
    if (Request.QueryString[“isprintable”] == “n”)
    {
    pdfWriter.ViewerPreferences = PdfWriter.HideMenubar;
    System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
    pdfWriter.SetEncryption(null, encoding.GetBytes(“mYpAssss”), 0, PdfWriter.STRENGTH40BITS);
    }
    doc.Open();
    PdfContentByte pdfContentByte = pdfWriter.DirectContent;
    doc.AddDocListener(pdfWriter);
    for (int page = 1; page <= pdfReader.NumberOfPages; page++)
    {
    //doc.SetPageSize(pdfReader.GetPageSize(page));
    doc.SetPageSize(pdfReader.GetPageSizeWithRotation(page));
    doc.NewPage();
    PdfImportedPage pdfImportedPage = pdfWriter.GetImportedPage(pdfReader, page);
    int rot = pdfReader.GetPageRotation(page);
    if (rot == 90 || rot == 270)
    pdfContentByte.AddTemplate(pdfImportedPage, 0, -1.0F, 1.0F, 0, 0, pdfReader.GetPageSizeWithRotation(page).Height);
    else
    pdfContentByte.AddTemplate(pdfImportedPage, 1.0F, 0, 0, 1.0F, 0, 0);
    string theId = findId();
    if (isWatermarkNeeded(theId))
    {
    #region ADD TEXT WATERMARK
    //pdfContentByte.BeginText();
    //iTextSharp.text.Rectangle pageSize = pdfReader.GetPageSizeWithRotation(page);
    //BaseFont baseFont = BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, System.Text.Encoding.ASCII.EncodingName, false);
    //pdfContentByte.SetFontAndSize(baseFont, 200);
    //BaseColor baseColor = new BaseColor(255, 0, 0, 20);
    //pdfContentByte.SetColorFill(baseColor);
    //float textAngle = (float)GetHypotenuseAngleInDegreesFrom(pageSize.Height, pageSize.Width);
    //pdfContentByte.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "DRAFT", 350, pageSize.Height / 2, textAngle);
    //pdfContentByte.EndText();
    #endregion
    #region ADD IMAGE WATERMARK

    string fechaExp = "Este documento vence: " + GetExpirationDate(theId).ToShortDateString();
    pdfContentByte.BeginText();
    //iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(Server.MapPath("~/images/watermark3.png"));
    iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(ImageCheck.CreatePicture(@"C:\Users\myUser\Desktop\watermark.png", fechaExp).ToArray());
    img.SetAbsolutePosition(0, 0);
    pdfContentByte.AddImage(img);
    pdfContentByte.EndText();
    #endregion
    }

    }
    pdfReader.Close();
    doc.Close();
    byte[] content = memoryStream.ToArray();
    Response.ContentType = "application/pdf";
    Response.AddHeader("content-length", content.Length.ToString());
    Response.BinaryWrite(content);
    }
    #endregion
    }
    else
    {
    //hay querystring pro no corresponden con los que se necesita
    }
    }
    else
    {
    //no se enviaron los querystring
    }

    }

    Reply
  10. Vijay Krishna says:
    July 6, 2015 at 12:37 am

    i want c# coding for the above code

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Recent Posts

  • Google open redirect
  • How to use AI to write code
  • Doctrine/Symfony MariaDB DSN connection string
  • Creating a portable copy of pdftotext from source
  • Gravity Forms shortcode getting extra line breaks when used with ACF

Recent Comments

  • jose luis on #2 – VB.Net iTextSharp Tutorial – Add an image to a document
  • Eliezer Castanon on iTextSharp slightly smarter text extraction strategy
  • javad on How to recompress images in a PDF using iTextSharp
  • MANOUS3784 on Flock is awesome
  • Sang on Flock is awesome

Archives

  • June 2026
  • October 2025
  • November 2023
  • September 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023
  • December 2022
  • September 2022
  • April 2022
  • October 2021
  • September 2021
  • April 2021
  • January 2021
  • October 2020
  • August 2020
  • June 2020
  • May 2020
  • December 2019
  • November 2019
  • October 2019
  • July 2019
  • May 2019
  • December 2018
  • October 2018
  • July 2018
  • November 2017
  • October 2017
  • August 2017
  • July 2017
  • June 2017
  • May 2017
  • April 2017
  • March 2017
  • February 2017
  • January 2017
  • September 2015
  • December 2014
  • November 2014
  • October 2014
  • September 2014
  • August 2014
  • July 2014
  • November 2013
  • May 2013
  • April 2013
  • March 2013
  • January 2013
  • November 2012
  • October 2012
  • July 2012
  • March 2012
  • January 2012
  • October 2011
  • September 2011
  • July 2011
  • February 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • August 2010
  • June 2010
  • April 2010
  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • July 2009
  • June 2009
  • May 2009
  • April 2009

Categories

  • Accessibility
  • Advanced Custom Fields
  • Authorize.Net
  • BWP Minify
  • Composer
  • Crappy Google Search Results of the Day
  • CSS
  • Doctrine
  • Drupal
  • Drush
  • Elasticsearch
  • Fun links of the day
  • Google Analytics
  • Gravity Forms
  • HHVM
  • HTML
  • iTextSharp
  • JavaScript
  • Linux
  • mysql
  • nginx
  • Optimization
  • PDF
  • PdfPTable
  • PHP
  • Plugins
  • Ramblings
  • Random things I learned
  • Redis
  • Security
  • simplesamlphp
  • SQL Server
  • SSH
  • SSL/TLS/HTTPS
  • Stack Overflow
  • SVG
  • Symfony
  • Synology
  • Uncategorized
  • Unicode
  • Varnish
  • Vendi Best Practice
  • VIP
  • Weird Google Search Results
  • Windows
  • WordPress
  • WP-CLI

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org
©2026 cjhaas blog | WordPress Theme by SuperbThemes