好的,基於@Mark Storer這裏有一些入門代碼。第一種方法創建一個包含10頁的樣本PDF,並在第一頁上創建一些鏈接跳轉到PDF的不同部分,以便我們可以使用某些內容。第二種方法打開在第一種方法中創建的PDF並遍歷每個註釋,試圖找出註釋鏈接到哪個頁面並將其輸出到TRACE窗口。該代碼在VB中,但如果需要,應該很容易轉換爲C#。它的目標是iTextSharp 5.1.1.0。
如果我有機會,我可能會嘗試進一步採取這種做法,實際分割和重新鏈接的東西,但我現在沒有時間。
Option Explicit On
Option Strict On
Imports iTextSharp.text
Imports iTextSharp.text.pdf
Imports System.IO
Public Class Form1
''//Folder that we are working in
Private Shared ReadOnly WorkingFolder As String = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Hyperlinked PDFs")
''//Sample PDF
Private Shared ReadOnly BaseFile As String = Path.Combine(WorkingFolder, "Sample.pdf")
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
''//Go to pages relative to this page
Doc.Add(New Paragraph(New Chunk("First Page", BlueFont).SetAction(New PdfAction(PdfAction.FIRSTPAGE))))
Doc.Add(New Paragraph(New Chunk("Next Page", BlueFont).SetAction(New PdfAction(PdfAction.NEXTPAGE))))
Doc.Add(New Paragraph(New Chunk("Prev Page", BlueFont).SetAction(New PdfAction(PdfAction.PREVPAGE)))) ''//This one does not make sense but is here for completeness
Doc.Add(New Paragraph(New Chunk("Last Page", BlueFont).SetAction(New PdfAction(PdfAction.LASTPAGE))))
''//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
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
''//I do not completely understand this but I think this turns an Indirect Reference into an actual object, but I could be wrong
''//Anyway, 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 named actions such as /FIRST, /LAST, etc
If AnnotationAction.Get(PdfName.S).Equals(PdfName.NAMED) Then
Trace.Write("GOTO:")
If AnnotationAction.Get(PdfName.N).Equals(PdfName.FIRSTPAGE) Then
Trace.WriteLine(1)
ElseIf AnnotationAction.Get(PdfName.N).Equals(PdfName.NEXTPAGE) Then
Trace.WriteLine(Math.Min(I + 1, PageCount)) ''//Any links that go past the end of the document should just go to the last page
ElseIf AnnotationAction.Get(PdfName.N).Equals(PdfName.LASTPAGE) Then
Trace.WriteLine(PageCount)
ElseIf AnnotationAction.Get(PdfName.N).Equals(PdfName.PREVPAGE) Then
Trace.WriteLine(Math.Max(I - 1, 1)) ''//Any links the go before the first page should just go to the first page
End If
''//Otherwise see if its a GOTO page action
ElseIf AnnotationAction.Get(PdfName.S).Equals(PdfName.GOTO) Then
''//Make sure that it has a destination
If AnnotationAction.GetAsArray(PdfName.D) Is Nothing Then Continue For
''//Once again, not completely sure if this is the best route but the ACTION has a sub DESTINATION object that is an Indirect Reference.
''//The code below gets that IR, asks the PdfReader to convert it to an actual page and then loop through all of the pages
''//to see which page the IR points to. Very inneficient but I could not find a way to get the page number based on the IR.
''//AnnotationAction.GetAsArray(PdfName.D) gets the destination
''//AnnotationAction.GetAsArray(PdfName.D).ArrayList(0) get the indirect reference part of the destination (.ArrayList(1) has fitting options)
''//DirectCast(AnnotationAction.GetAsArray(PdfName.D).ArrayList(0), PRIndirectReference) turns it into a PRIndirectReference
''//The full line gets us an actual page object (actually I think it could be any type of pdf object but I have not tested that).
''//BIG NOTE: This line really should have a bunch more sanity checks in place
Dim AnnotationReferencedPage = PdfReader.GetPdfObject(DirectCast(AnnotationAction.GetAsArray(PdfName.D).ArrayList(0), PRIndirectReference))
Trace.Write("GOTO:")
''//Re-loop through all of the pages in the main document comparing them to this page
For J = 1 To PageCount
If AnnotationReferencedPage.Equals(R.GetPageN(J)) Then
Trace.WriteLine(J)
Exit For
End If
Next
End If
Next
Next
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
CreateSamplePdf()
ListPdfLinks()
Me.Close()
End Sub
End Class
嗨,馬克感謝您的幫助。我正在分析這個文件。你能否給我提供一個示例代碼。因爲我需要儘快完成並交付。 –
@MarkStorer - 步驟1.1是我迷失的地方,由於iTextSharp的對象和糟糕的文檔,PDF參考並沒有什麼幫助。我可以找到所有'subtype =/link'註解,但從那裏有不同的類型,它們的鍵/元素是不同的。我在這裏編輯和擴展了這個問題:http://stackoverflow.com/questions/5579051/read-internal-link-annotation-using-itextsharp –