Creating PDF Links

So, let’s say I have the following pages in a PDF:

M-0.1 - MECHANICAL GENERAL NOTES & SYMBOLS LEGEND
M-2.1 - MECHANICAL FLOOR PLAN
M-5.1 - MECHANICAL DETAILS

I then want to go through the entire document and anywhere that M-0.1 is written should be a link back to page 1, anywhere with M-2.1 would be to page 2 and anywhere with M-5.1 links to page 3.

This is obviously overly simplified for this example purpose, but should help me get going in the right direction.

Anyone have an idea on how to accomplish this? If it helps, I do have MBS Studio’s DynaPDF Pro license as well.

We have an example to find text with positions:
https://www.mbs-plugins.de/archive/2018-02-21/DynaPDF_Text_Position_Examples/monkeybreadsoftware_blog_xojo

So if you run this to find text blocks and know where your text is.

Or maybe use DynaPDFEditTextMBS class to find text and then just grab the coordinates (And not change text) and then put links on the page there.

If you’re using PDFDocument for that, you are in charge of calculating the coordinates for where the text is to be “drawn” in the page, adding then the link areas, using that coordinates, to the pages of interest.

Sorry, I should have explained the PDFs already exist.

Ok, so in trying to find text positions with the example, it really isn’t selecting words properly and there seems to be no way to filter it to certain text. On DynaPDFEditTextMBS, I can find the pattern, but I don’t quite understand how to grab the coordinates after using FindPattern

I think the DynaPDFEditTextMBS class can do that.

Function PrepareWrite(M as DynaPDFMatrixMBS, text as string, FillCS as Integer, FillColor as UInt32, StrokeCS as Integer, StrokeColor as UInt32, FontSize as double, x as double, y as double, w as double, h as double, font as DynaPDFFontMBS) Handles PrepareWrite as boolean
  
  Call pdf.WebLink(x, y, w, h, "https://www.dynaforms.com/")
  Return false
End Function

and we do a search and replace for same text:

Dim editor As New MyDynaPDFEditTextMBS(pdf)
editor.pdf = pdf

dim found as integer
dim pageCount as integer = pdf.GetPageCount
for i as integer = 1 to pageCount
  // DynaPDF error messages are already handled in the error callback function!
  if pdf.EditPage(i) then
    
    // disable font errors while editing. The class handles them
    
    pdf.IgnoreFontWarnings = true
    'The function FindPattern() searches for a string.
    
    found = editor.FindPattern("DynaPDF")
    
    'ReplacePattern() replaces the text with a new one or deletes it if the new string
    'is an empty string. The new text is written in red color so that you can better
    'find it. Take a look into the event PrepareWrite if you want to
    'write the text in the orignal color...
    
    editor.ReplacePattern("DynaPDF")
    pdf.IgnoreFontWarnings = false
    
    call pdf.EndPage
  end if
next

if you want to do it better, avoid the rewriting and do a first run to collect coordinates per page. Then start over and import pages again and do the addition of weblink, so the text doesn’t get rewritten.

Ok, that is essentially what I was trying before, but with M-3.1 as the pattern to find. And the links don’t come in at the right spot or size. Click here for an example PDF.

Something is obviously off with the coordinates.

Ok, so the issues I am having only occur if the page size is larger than 8.5x11, any suggestions?

I’ll take a look…

In my tests I get negative height for the box.

Seems like the matrix multiplications is needed:

Public Sub Transform(m as DynaPDFMatrixMBS, byref x as Double, byref y as double)
  dim tx as double = x
  x = tx * M.a + y * M.c + M.x
  y = tx * M.b + y * M.d + M.y
End Sub

This helper function is quite useful:

Function PrepareWrite(M as DynaPDFMatrixMBS, text as string, FillCS as Integer, FillColor as UInt32, StrokeCS as Integer, StrokeColor as UInt32, FontSize as double, x as double, y as double, w as double, h as double, font as DynaPDFFontMBS) Handles PrepareWrite as boolean
  
  Transform m, x, y
  
  w = 30
  h = 20
  
  Call pdf.HighlightAnnot(pdf.katHighlight, x, y, w, h, pdf.RGB(255, 0, 255), "", "", "")
  Call pdf.WebLink(x, y, w, h, "https://www.dynaforms.com/")
  
  System.DebugLog "found: "+Str(x)+"/"+Str(y)+" "+Str(w)+" "+Str(h)
  
  Return false
End Function

the highlight annotation in violet seems to be very helpful to show where it is found.
Also it looks like the pages are rotated, which doesn’t make it easier.
But for me the one edge meets the edge of the text at least, so maybe you can live with covering the area around a bit wider?

Function PrepareWrite(M as DynaPDFMatrixMBS, text as string, FillCS as Integer, FillColor as UInt32, StrokeCS as Integer, StrokeColor as UInt32, FontSize as double, x as double, y as double, w as double, h as double, font as DynaPDFFontMBS) Handles PrepareWrite as boolean
  
  Transform m, x, y
  
  x = x - 25
  y = y - 10
  w = 50
  h = 20
  
  Call pdf.HighlightAnnot(pdf.katHighlight, x, y, w, h, pdf.RGB(255, 0, 255), "", "", "")
  Call pdf.WebLink(x, y, w, h, "https://www.dynaforms.com/")
  
  System.DebugLog "found: "+Str(x)+"/"+Str(y)+" "+Str(w)+" "+Str(h)
  
  Return false
End Function

The pages as shown are the correct orientation for drawings. This is getting closer, but not quite there, at least this gives a starting point though. Unfortunately, we have to have the hyper links directly on the text, it can be slightly bigger, but not much, as if we go to big, the links will overlap in some cases, which we can’t have.

I really wish there was something simple like: find all instances of M-3.1 and add a link…lol

Ok, I mostly have it, just where it says see detail m-5.1 for example is a little off, but I can live with it.

Sub Open() Handles Open
  Dim pdf As New MyDynapdfMBS
  pdf.SetLicenseKey "Pro" // For this example you can use a Pro or Enterprise License
  
  
  Dim outfile As FolderItem=SpecialFolder.Temporary.Child("Temp" + UniqueID + ".pdf")
  
  call pdf.CreateNewPDF outfile 
  
  Dim filepath As FolderItem=SpecialFolder.Desktop.Child("MECHANICAL.pdf")
  
  
  
  // We import the contents only without conversion of pages to templates
  Call pdf.SetImportFlags(BitwiseOr(pdf.kifImportAll, pdf.kifImportAsPage))
  
  
  if pdf.OpenImportFile(filePath, pdf.kptOpen, "")<>0 then
    MsgBox "Input file """+ filePath.name+""" not found!"
    quit
  end if
  
  Call pdf.ImportPDFFile(1, 1.0, 1.0)
  call pdf.CloseImportFile
  
  Dim editor As New MyDynaPDFEditTextMBS(pdf)
  editor.pdf = pdf
  //bookmarks & pagefound
  Dim found As Integer
  Dim pageCount As Integer = pdf.GetPageCount
  Var PageNumbers() As String
  Var BookMarkCount As Integer = pdf.GetBookmarkCount - 1
  For i As Integer = 0 To BookMarkCount
    Var B As DynaPDFBookmarkMBS = pdf.GetBookmark(i)
    Var bname As String = b.Title
    System.DebugLog(bname)
    Var parts() As String = bname.Split(" ")
    If PageNumbers.IndexOf(parts(0).Trim) = -1 Then
      PageNumbers.Add(parts(0).Trim)
    End If
  Next i
  
  
  For Each PageNumber As String In PageNumbers
    System.DebugLog(PageNumber)
    ReferenceSheetNumber = PageNumbers.IndexOf(PageNumber) + 1
    For i As Integer = 1 To pageCount
      If i = ReferenceSheetNumber Then
        Continue For i
      End If
      CurrentSheetNumber = i
      // DynaPDF error messages are already handled in the error callback function!
      If pdf.EditPage(i) Then
        
        // disable font errors while editing. The class handles them
        
        pdf.IgnoreFontWarnings = True
        'The function FindPattern() searches for a string.
        
        found = editor.FindPattern(PageNumber)
        
        'ReplacePattern() replaces the text with a new one or deletes it if the new string
        'is an empty string. The new text is written in red color so that you can better
        'find it. Take a look into the event PrepareWrite if you want to
        'write the text in the orignal color...
        
        editor.ReplacePattern(PageNumber)
        pdf.IgnoreFontWarnings = False
        
        Call pdf.EndPage
      End If
    Next
  Next PageNumber
  
  
  Call pdf.CloseFile
  outFile.Delete
  
  
  outfile = SpecialFolder.Desktop.Child(filepath.NativePath.Replace(".pdf", UniqueID) + ".pdf")
  
  pdf = Nil
  pdf = New MyDynapdfMBS
  pdf.SetLicenseKey "Pro" // For this example you can use a Pro or Enterprise License
  
  Call pdf.CreateNewPDF outfile 
  
  //filepath = SpecialFolder.Desktop.Child("MECHANICAL.pdf")
  
  
  
  // We import the contents only without conversion of pages to templates
  Call pdf.SetImportFlags(BitwiseOr(pdf.kifImportAll, pdf.kifImportAsPage))
  
  If pdf.OpenImportFile(filePath, pdf.kptOpen, "")<>0 Then
    MsgBox "Input file """+ filePath.name+""" not found!"
    Quit
  End If
  
  Call pdf.ImportPDFFile(1, 1.0, 1.0)
  Call pdf.CloseImportFile
  
  
  
  
  
  
  For Each dic As Dictionary In LinksToBeAdded
    pdf.IgnoreFontWarnings = True
    Var x As Double = dic.Value("x")
    Var y As Double = dic.Value("y")
    Var w As Double = dic.Value("w")
    Var h As Double = dic.Value("h")
    Var RefSheet As Integer = dic.Value("RefSheet")
    Var txt As String = dic.Value("Text")
    Var page As Integer = dic.Value("page")
    If pdf.EditPage(page) Then
      Call pdf.HighlightAnnot(pdf.katHighlight, x, y, w, h, pdf.RGB(205, 0, 205), "", "", "")
      Call pdf.PageLink(x, y, w, h, RefSheet)
      Call pdf.EndPage
    End If
    pdf.IgnoreFontWarnings = False
  Next dic
  
  Call pdf.CloseFile
  
  outfile.Open
  
  Quit
  
End Sub

Function PrepareWrite(M as DynaPDFMatrixMBS, text as string, FillCS as Integer, FillColor as UInt32, StrokeCS as Integer, StrokeColor as UInt32, FontSize as double, x as double, y as double, w as double, h as double, font as DynaPDFFontMBS) Handles PrepareWrite as boolean
  Transform(m, x, y)
  //use text heights and widths
  
  w = FontSize/10
  h = 30
  y = y - 20
  x = x - (w/2)
  
  
  Call pdf.HighlightAnnot(pdf.katHighlight, x, y, w, h, pdf.RGB(205, 0, 205), "", "", "")
  Call pdf.PageLink(x, y, w, h, ReferenceSheetNumber)
  Var Dic As New Dictionary
  dic.Value("x") = x
  dic.Value("y") = y
  dic.Value("w") = w
  dic.Value("h") = h
  dic.Value("RefSheet") = ReferenceSheetNumber
  dic.Value("text") = Text
  dic.Value("page") = CurrentSheetNumber
  LinksToBeAdded.Add(dic)
  System.DebugLog "found: "+Str(x)+"/"+Str(y)+" "+Str(w)+" "+Str(h)
  
  Return False
End Function

Public Function RemoveSpecialCharacters(extends txt as String, AllowSpaces as Boolean = False) As String
  Var Results As String
  for x as integer = 1 to txt.Len
    Var tst as integer = asc(txt.Mid(x,1))
    Var addChar as Boolean = False
    if tst > 47 and  tst < 58 then
      addChar = True
    elseif tst > 64 and tst < 91 then
      addChar = True
    elseif tst > 96 and tst < 122 then
      addChar = True
    elseif tst = 32 then
      addChar = AllowSpaces
    end if
    if addChar then
      if Results = "" then
        Results = txt.Mid(x, 1)
      else
        Results = Results + txt.Mid(x, 1)
      end if
    end if
  next x
  Return Results
End Function

Public Function UniqueID() As String
  Var Results As String
  Results = UUIDMBS.randomUUID.ValueFormattedString.RemoveSpecialCharacters
  Return Results
End Function

Public Sub Transform(m as DynaPDFMatrixMBS, byref x as Double, byref y as double)
  Dim tx As Double = x
  Dim tx2 As Double = y
  
  x = tx * M.a + x * M.c + M.x
  y = tx2 * M.b + y * M.d + M.y
  
End Sub

I have found an issue, let’s say I have text E-1.0 and E-1.0P, the program gets confused. Unfortunately, I can’t just add a space to the end, as there isn’t always a space after trhe sheet number in the PDF.

I have put my test project at: 005 - Auto Hyperlink - Google Drive

I have managed to work around that problem, but some of the links are still in the wrong positions, see the Electrical_Links.pdf in the PDF Testing folder.