使用ITextSharp提取和更新现有PDF中的链接

我需要将几个(阅读:很多)PDF文件发布到networking上,但是其中很多都有硬编码的文件://链接和非公开位置的链接。 我需要阅读这些PDF文件,并将链接更新到适当的位置。 我已经开始使用itextsharp编写应用程序来读取目录和文件,findPDF并遍历每个页面。 接下来我需要做的是find链接,然后更新不正确的链接。

string path = "c:\\html"; DirectoryInfo rootFolder = new DirectoryInfo(path); foreach (DirectoryInfo di in rootFolder.GetDirectories()) { // get pdf foreach (FileInfo pdf in di.GetFiles("*.pdf")) { string contents = string.Empty; Document doc = new Document(); PdfReader reader = new PdfReader(pdf.FullName); using (MemoryStream ms = new MemoryStream()) { PdfWriter writer = PdfWriter.GetInstance(doc, ms); doc.Open(); for (int p = 1; p <= reader.NumberOfPages; p++) { byte[] bt = reader.GetPageContent(p); } } } } 

坦率地说,一旦我得到了页面内容,我相当喜欢iTextSharp。 我已经阅读了sourceforge上的itextsharp示例,但实际上并没有find我正在寻找的东西。

任何帮助将不胜感激。

谢谢。

如果你不知道PDF格式的内部结构和iText / iTextSharp的抽象/实现,这个有点复杂。 您需要了解如何使用PdfDictionary对象并通过其PdfName键查找事物。 一旦你得到了,你可以阅读PDF格式的规范,并很容易地打开文件。 如果您注意,我已经在适当的地方将PDF规范的相关部分包含在括号内。

无论如何,PDF中的链接将作为注释存储( PDF Ref 12.5 )。 注解是基于页面的,因此您需要先单独获取每个页面的注释数组。 有很多不同types的注释,所以你需要检查每一个的SUBTYPE ,看看它是否设置为LINK12.5.6.5 )。 每个链接都应该有一个与之相关的ACTION字典( 12.6.2 ),并且你想检查这个动作的S键来查看它是什么types的动作。 有一堆可能的,链接的具体可能是内部链接或打开文件链接或播放声音链接或其他( 12.6.4.1 )。 您只查看URItypes的链接(请注意字母I而不是字母L )。 URI操作( 12.6.4.7 )有一个保存要导航到的实际地址的URI密钥。 (还有一个图像映射的IsMap属性,我实际上无法想象任何人使用。)

呼。 还在读书? 下面是一个完整的工作VS 2010 C#WinForms应用程序基于我的post在这里针对iTextSharp 5.1.1.0。 这段代码有两个主要的function:1)创build一个带有指向Google.com的链接的示例PDF,2)用bing.com链接replace该链接。 代码应该是相当好的评论,但随意提出任何问题,你可能有。

 using System; using System.Text; using System.Windows.Forms; using iTextSharp.text; using iTextSharp.text.pdf; using System.IO; namespace WindowsFormsApplication1 { public partial class Form1 : Form { //Folder that we are working in private static readonly string WorkingFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Hyperlinked PDFs"); //Sample PDF private static readonly string BaseFile = Path.Combine(WorkingFolder, "OldFile.pdf"); //Final file private static readonly string OutputFile = Path.Combine(WorkingFolder, "NewFile.pdf"); public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { CreateSamplePdf(); UpdatePdfLinks(); this.Close(); } private static void CreateSamplePdf() { //Create our output directory if it does not exist Directory.CreateDirectory(WorkingFolder); //Create our sample PDF using (iTextSharp.text.Document Doc = new iTextSharp.text.Document(PageSize.LETTER)) { using (FileStream FS = new FileStream(BaseFile, FileMode.Create, FileAccess.Write, FileShare.Read)) { using (PdfWriter writer = PdfWriter.GetInstance(Doc, FS)) { Doc.Open(); //Turn our hyperlink blue iTextSharp.text.Font BlueFont = FontFactory.GetFont("Arial", 12, iTextSharp.text.Font.NORMAL, iTextSharp.text.BaseColor.BLUE); Doc.Add(new Paragraph(new Chunk("Go to URL", BlueFont).SetAction(new PdfAction("http://www.google.com/", false)))); Doc.Close(); } } } } private static void UpdatePdfLinks() { //Setup some variables to be used later PdfReader R = default(PdfReader); int PageCount = 0; PdfDictionary PageDictionary = default(PdfDictionary); PdfArray Annots = default(PdfArray); //Open our reader R = new PdfReader(BaseFile); //Get the page cont PageCount = R.NumberOfPages; //Loop through each page for (int i = 1; i <= PageCount; i++) { //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 == null) || (Annots.Length == 0)) continue; //Loop through each annotation foreach (PdfObject A in Annots.ArrayList) { //Convert the itext-specific object as a generic PDF object PdfDictionary AnnotationDictionary = (PdfDictionary)PdfReader.GetPdfObject(A); //Make sure this annotation has a link if (!AnnotationDictionary.Get(PdfName.SUBTYPE).Equals(PdfName.LINK)) continue; //Make sure this annotation has an ACTION if (AnnotationDictionary.Get(PdfName.A) == null) continue; //Get the ACTION for the current annotation PdfDictionary AnnotationAction = (PdfDictionary)AnnotationDictionary.Get(PdfName.A); //Test if it is a URI action if (AnnotationAction.Get(PdfName.S).Equals(PdfName.URI)) { //Change the URI to something else AnnotationAction.Put(PdfName.URI, new PdfString("http://www.bing.com/")); } } } //Next we create a new document add import each page from the reader above using (FileStream FS = new FileStream(OutputFile, FileMode.Create, FileAccess.Write, FileShare.None)) { using (Document Doc = new Document()) { using (PdfCopy writer = new PdfCopy(Doc, FS)) { Doc.Open(); for (int i = 1; i <= R.NumberOfPages; i++) { writer.AddPage(writer.GetImportedPage(R, i)); } Doc.Close(); } } } } } } 

编辑

我应该注意到,这只是改变了实际的联系。 文档中的任何文本都不会被更新。 注释是在文本的顶部绘制的,但是并没有真正绑定到底下的文本。 这完全是另一个话题。

注意到如果Action是间接的,它将不会返回一个字典,你将会有一个错误:

 PdfDictionary AnnotationAction = (PdfDictionary)AnnotationDictionary.Get(PdfName.A); 

在可能的间接字典的情况下:

 PdfDictionary Action = null; //Get action directly or by indirect reference PdfObject obj = Annotation.Get(PdfName.A); if (obj.IsIndirect) { Action = PdfReader.GetPdfObject(obj); } else { Action = (PdfDictionary)obj; } 

在这种情况下,您必须调查返回的字典以找出URI的位置。 与间接/启动字典一样,URI位于PRIndirectReferencetypes的/ F项目中,/ Type为/ FileSpec,URI位于/ F

添加了用于处理间接和启动操作的代码和空注释字典:

 PdfReader r = new PdfReader(@"d:\kb2\" + f); for (int i = 1; i <= r.NumberOfPages; i++) { //Get the current page var PageDictionary = r.GetPageN(i); //Get all of the annotations for the current page var Annots = PageDictionary.GetAsArray(PdfName.ANNOTS); //Make sure we have something if ((Annots == null) || (Annots.Length == 0)) continue; foreach (var A in Annots.ArrayList) { var AnnotationDictionary = PdfReader.GetPdfObject(A) as PdfDictionary; if (AnnotationDictionary == null) continue; //Make sure this annotation has a link if (!AnnotationDictionary.Get(PdfName.SUBTYPE).Equals(PdfName.LINK)) continue; //Make sure this annotation has an ACTION if (AnnotationDictionary.Get(PdfName.A) == null) continue; var annotActionObject = AnnotationDictionary.Get(PdfName.A); var AnnotationAction = (PdfDictionary)(annotActionObject.IsIndirect() ? PdfReader.GetPdfObject(annotActionObject) : annotActionObject); var type = AnnotationAction.Get(PdfName.S); //Test if it is a URI action if (type.Equals(PdfName.URI)) { //Change the URI to something else string relativeRef = AnnotationAction.GetAsString(PdfName.URI).ToString(); AnnotationAction.Put(PdfName.URI, new PdfString(url)); } else if (type.Equals(PdfName.LAUNCH)) { //Change the URI to something else var filespec = AnnotationAction.GetAsDict(PdfName.F); string url = filespec.GetAsString(PdfName.F).ToString(); AnnotationAction.Put(PdfName.F, new PdfString(url)); } } } //Next we create a new document add import each page from the reader above using (var output = File.OpenWrite(outputFile.FullName)) { using (Document Doc = new Document()) { using (PdfCopy writer = new PdfCopy(Doc, output)) { Doc.Open(); for (int i = 1; i <= r.NumberOfPages; i++) { writer.AddPage(writer.GetImportedPage(r, i)); } Doc.Close(); } } } r.Close();