I have found two primary libraries for programmatically manipulating PDF files;  PdfBox and iText. These are both Java libraries, but I needed something I could use with C#. Well, as it turns out there is an implementation of each of these libraries for .NET, each with its own strengths and weaknesses.

Some Navigation Links:

PdfBox - .NET version

The .NET implementation of PdfBox is not a direct port - rather, it uses IKVM to run the Java version inter-operably with .NET. IKVM features an actual .NET implementation of a Java Virtual Machine, and a .net implementation of Java Class Libraries along with tools which enable Java and .NET interoperability.

PdfBox’s dependency on IKVK incurs a lot of baggage in performance terms. When the IKVM libraries load, and (I am assuming) the “’Virtual’ Java Virtual Machine” spins up, things slow way down until the load is complete. On the other hand, for some of the more common things one might want to do with a PDF programmatically, the API is (relatively) straightforward, and well documented.

When you run a project which uses PdfBox, you will notice a lag the first time PdfBox and IKVM are loaded. After that, things seem to perform sufficiently, at least for what I needed to do.

Side Note: iTextSharp

iTextSharp is a direct port of the Java library to .NET.

iTextSharp looks to be the more robust library in terms of fine-grained control, and is extensively documented in a book by one of the authors of the library, iText in Action (Second Edition). However, the learning curve was a little steeper for iText, and I needed to get a project out the door. I will examine iTextSharp in another post, because it looks really cool, and supposedly does not suffer the performance limitations of PdfBox.

Getting started with PdfBox

Before you can use PdfBox, you need to either build the project from source, or download the ready-to-use binaries. I just downloaded the binaries for version 1.2.1 from this helpful gentleman’s site, which, since they depend on IKVM, also includes the IKVM binaries. However, there are detailed instruction for building from source on the PdfBox site. Personally, I would start with the downloaded binaries to see if PdfBox is what you want to use first.

Important to note here: apparently, the PdfBox binaries are dependent upon the exact dependent DLL’s used to build them. See the notes on the PdfBox .Net Version page.

Once you have built or downloaded the binaries, you will need to set references to PdfBox and ALL the included IKVM binaries in your Visual Studio Project. Create a new Visual Studio project named “PdfBoxExamples” and add references to ALL the PdfBox and IKVM binaries. There are a lot.Deal with it. Your project references folder will look like the picture to the right when you are done.

The PdfBox API is quite dense, but there is a handy reference at the Apache Pdfbox site.  The PDF file format is complex, to say the least, so when you first take a gander at the available classes and methods presented by the PDF box API, it can be difficult to know where to begin. Also, there is the small issue that what you are looking at is a Java API, so some of the naming conventions are a little different. Also, the PdfBox API often returns what appear to be Java classes. This comes back to that .Net implementation of the Java Class libraries I mentioned earlier.

Things to Do with PdfBox

It seems like there are three common things I often want to do with PDF files: Extract text into a string or text file, split the document into one or more parts, or merge pages or documents together. To get started with using PdfBox we will look at extracting text first, since the set up for this is pretty straightforward, and there isn’t any real Java/.Net weirdness here.

Extracting Text from a PDF File

To do this, we will call upon two PdfBox namespaces (“Packages” in Java, loosely), and two Classes:

The namespace org.apache.pdfbox.pdmodel gives us access to the PDDocument class and the namespace org.apache.pdfbox.util gives us the PDFTextStripper class.

In your new PdfBoxExamples project, add a new class, name it “PdfTextExtractor" and add the following code:

The PdfTextExtractor Class
Hide   Copy Code
using System;
using org.apache.pdfbox.pdmodel;
using org.apache.pdfbox.util; namespace PdfBoxExamples
{
public class pdfTextExtractor
{
public static String PDFText(String PDFFilePath)
{
PDDocument doc = PDDocument.load(PDFFilePath);
PDFTextStripper stripper = new PDFTextStripper();
return stripper.getText(doc);
}
}
}

As you can see, we use the PDDocument class (from the org.apache.pdfbox.pdmodel namespace) and initialize is using the static .load method defined as a class member on PDDocument. As long as we pass it a valid file path, the .load method will return an instance of PDDocument, ready for us to work with.

Once we have the PDDocument instance, we need an instance of the PDFTextStripper class, from the namespace org.apache.pdfbox.util. We pass our instance of PDDocument in as a parameter, and get back a string representing the text contained in the original PDF file.

Be prepared. PDF documents can employ some strange layouts, especially when there are tables and/or form fields involved. The text you get back will tend not to retain the formatting from the document, and in some cases can be bizarre.

However, the ability to strip text in this manner can be very useful, For example, I recently needed to download an individual PDF file for each county in the state of Missouri, and strip some tabular data our of each one. I hacked together an iterator/downloader to pull down the files, and the, using a modified version of the text stripping tool illustrated above and some rather painful Regex, I was able to get what I needed.

Splitting the Pages of a PDF File

At the simplest level, suppose you had a PDF file and you wanted to split it into individual pages. We can use theSplitter class, again from the org.apache.pdf.util namespace. Add another class to you project, namedPDFFileSplitter, and copy the following code into the editor:

The PdfFileSplitter Class
Hide   Copy Code
using org.apache.pdfbox.pdmodel;
using org.apache.pdfbox.util; namespace PdfBoxExamples
{
public class PDFFileSplitter
{
public static java.util.List SplitPDFFile(string SourcePath,
int splitPageQty = 1)
{
var doc = PDDocument.load(SourcePath);
var splitter = new Splitter();
splitter.setSplitAtPage(splitPageQty); return (java.util.List)splitter.split(doc);
}
}
}

Notice anything strange in the code above? That’s right. We have declared a static method with a return type of java.util.List. WHAT? This is where working with PdfBox and more importantly, IKVM becomes weird/cool. Cool, because I am using a direct Java class implementation in Visual Studio, in my C# code. Weird, because my method returns a bizarre type (from a C# perspective, anyway) that I was unsure what to do with.

I would probably add to the above class so that the splitter persisted the split documents to disk, or change the return type of my method to object[], and use the .ToArray() method, like so:

The PdfFileSplitter Class (improved?)
Hide   Copy Code
public static object[] SplitPDFFile(string SourcePath,
int splitPageQty = 1)
{
var doc = PDDocument.load(SourcePath);
var splitter = new Splitter();
splitter.setSplitAtPage(splitPageQty); return (object[])splitter.split(doc).toArray();
}

In any case, the code in either example loads up the specified PDF file into a PDDocument instance, which is then passed to the org.apache.pdfbox.Splitter, along with an int parameter. The output in the example above is a Java ArrayList containing a single page from your original document in each element. Your original document is not altered by this process, by the way.

The int parameter is telling the Splitter how many pages should be in each split section. In other words, if you start with a six-page PDF file, the output will be three two-page files. If you started with a 5-page file, the output would be two two-page files and one single-page file. You get the idea.

Extract Multiple Pages from a PDF  Into a New File

Something slightly more useful might be a method which accepts an array of integers as a parameter, with each integer representing a page number within a group to be extracted into a new, composite document. For example, say I needed pages 1, 6, and 7 from a 44 page PDF pulled out and merged into a new document (in reality, I needed to do this for pages 1, 6, and 7 for each of about 200  individual documents). We might add a method to our PdfFileSplitter class as follows:

The ExtractToSingleFile Method
Hide   Copy Code
public static void ExtractToSingleFile(int[] PageNumbers,
string sourceFilePath, string outputFilePath)
{
var originalDocument = PDDocument.load(sourceFilePath);
var originalCatalog = originalDocument.getDocumentCatalog();
java.util.List sourceDocumentPages = originalCatalog.getAllPages();
var newDocument = new PDDocument(); foreach (var pageNumber in PageNumbers)
{
// Page numbers are 1-based, but PDPages are contained in a zero-based array:
int pageIndex = pageNumber - 1;
newDocument.addPage((PDPage)sourceDocumentPages.get(pageIndex));
}
newDocument.save(outputFilePath);
}

Below is a simple example to illustrate how we might call this method from a client:

Calling the ExtractToSingleFile Method:
Hide   Copy Code
public void ExtractAndMergePages()
{
string sourcePath = @"C:\SomeDirectory\YourFile.pdf";
string outputPath = @"C:\SomeDirectory\YourNewFile.pdf";
int[] pageNumbers = { 1, 6, 7 };
PDFFileSplitter.ExtractToSingleFile(pageNumbers, sourcePath, outputPath);
}

Limit Class Dependency on PdfBox

It is always good to limit dependencies within a project. In this case, especially, I would want to keep those odd Java class references constrained to the highest degree possible. In other words, where possible, I would attempt to either return standard .net types from my classes which consume the PdfBox API, or otherwise complete execution so that client code calling upon this class doesn’t need to be aware of IKVM, or funky C#/Java hybrid types.

Or, I would build out my own “PdfUtilities” library project, within which objects are free to depend upon and intermix this Java hybrid. However, I would make sure public methods defined within the library itself accepted and returned only standard C# types.

In fact, that is precisely what I am doing, and I’ll look at that in a following post.

Links to resources:

Working with PDF files in C# using PdfBox and IKVM的更多相关文章

  1. “Stamping” PDF Files Downloaded from SharePoint 2010

    http://blog.falchionconsulting.com/index.php/2012/03/stamping-pdf-files-downloaded-from-sharepoint-2 ...

  2. How to create PDF files in a Python/Django application using ReportLab

    https://assist-software.net/blog/how-create-pdf-files-python-django-application-using-reportlab CONT ...

  3. Base64转PDF、PDF转IMG(使用pdfbox插件)

    --添加依赖 <!-- https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox --><dependency>  ...

  4. Csharp:user WebControl Read Adobe PDF Files In Your Web Browser

    namespace GeovinDu.PdfViewer { [DefaultProperty("FilePath")] [ToolboxData("<{0}:Sh ...

  5. C# based on PdfSharp to split pdf files and get MemoryStream C#基于PdfSharp拆分pdf,并生成MemoryStream

    install-package PdfSharp -v 1.51.5185-beta using System; using PdfSharp.Pdf; using System.IO; using ...

  6. PHP class which generates PDF files from UTF-8 encoded HTML

    http://www.mpdf1.com/mpdf/index.php

  7. Converting PDF to Text in C#

    Parsing PDF files in .NET using PDFBox and IKVM.NET (managed code). Download source files - 82 kB [c ...

  8. 操作PDF文档功能的相关开源项目探索——iTextSharp 和PDFBox

    原文 操作PDF文档功能的相关开源项目探索——iTextSharp 和PDFBox 很久没自己写写心得日志与大家分享了,一方面是自己有点忙,一方面是自己有点懒,没有及时总结.因为实践是经验的来源,总结 ...

  9. 常用PDF文档开发库

    C++库: 1,PDF类库 PoDoFo   http://podofo.sourceforge.net/  PoDoFo 是一个用来操作 PDF 文件格式的 C++ 类库.它还包含一些小工具用来解析 ...

随机推荐

  1. Elasticsearch系列(4):基本搜索

    空搜索 搜索API的最基础的形式是没有指定任何查询的空搜索 ,它简单地返回集群中所有索引下的所有文档,如下命令: GET /_search 返回如下结果: 查询结果解释: 1,hits 返回结果中最重 ...

  2. 【代码笔记】Web-CSS-CSS Border(边框)

    一,效果图. 二,代码. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...

  3. Dynamics 365中配置和使用文件夹级别的跟踪(folder-level tracking)

    本人微信和易信公众号:微软动态CRM专家罗勇 ,回复274或者20180630可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong.me ...

  4. IBGP默认的TTL值为255

    结论: 1.IBGP默认的TTL值为255 组网图: 抓包内容: 1.在AR1和AR2之间抓包,只显示BGP包,显示内容如下:

  5. iOS---------- Safe Area Layout Guide before iOS 9.0

    如果你们的项目不做iOS9以下支持就打开main.storyboard    去除Use safe Area Layout 如果不考虑iOS9以下支持就按照下面的步骤 选中控制器,右边面板的Build ...

  6. Fragment 生命周期的详情

    Fragment每个生命周期方法的意义.作用(注意红色的不是生命周期方法):setUserVisibleHint():设置Fragment可见或者不可见时会调用此方法.在该方法里面可以通过调用getU ...

  7. Integer a= 127 与 Integer b = 128相关

    Integer a = 127; Integer b = 127; Integer c = 128; Integer d = 128; a == b 与 c == d 的比较结果是什么? a == b ...

  8. 面相服务的架构SOA

    SOA体系结构是基于服务组件模型,将应用程序的不同功能单元通过定义良好的接口契约联系起来,接口是采用中立方式进行定义的,独立于实现服务的硬件平台,操作系统和编程语言.使得构建在这样的系统中的服务可以以 ...

  9. jenkins+gitlab配置

    jenkins配置 插件配置 Jenkins要实现持续集成自动部署需要安装  gitlab  maven Publish Over SSH  Git等几个插件 查看已经安装的插件 jenkins上集成 ...

  10. 在java web项目中实现随项目启动的额外操作

    前言 在web项目中经常会遇到在项目启动初始,会要求做一些逻辑的实现,比如实现一个消息推送服务,实现不同类型数据同步的回调操作初始化,或则通知其他客户服务器本项目即将启动,等等.对于这种要求,目前个人 ...