Working with PDF files in C# using PdfBox and IKVM
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:
- Example: Extract Text from PDF File
- Example: Split PDF
- Split Specific Pages and Merge Into New File
- Links to Resources Mentioned in this Article
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
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
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?)
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
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:
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:
- PdfBox.Net version on Apache
- IKVM.net Home Page
- Pdfbox .net binaries version 1.2.1 (includes required IKVM binaries)
- PdfBox API Overview
Working with PDF files in C# using PdfBox and IKVM的更多相关文章
- “Stamping” PDF Files Downloaded from SharePoint 2010
http://blog.falchionconsulting.com/index.php/2012/03/stamping-pdf-files-downloaded-from-sharepoint-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 ...
- Base64转PDF、PDF转IMG(使用pdfbox插件)
--添加依赖 <!-- https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox --><dependency> ...
- Csharp:user WebControl Read Adobe PDF Files In Your Web Browser
namespace GeovinDu.PdfViewer { [DefaultProperty("FilePath")] [ToolboxData("<{0}:Sh ...
- 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 ...
- PHP class which generates PDF files from UTF-8 encoded HTML
http://www.mpdf1.com/mpdf/index.php
- Converting PDF to Text in C#
Parsing PDF files in .NET using PDFBox and IKVM.NET (managed code). Download source files - 82 kB [c ...
- 操作PDF文档功能的相关开源项目探索——iTextSharp 和PDFBox
原文 操作PDF文档功能的相关开源项目探索——iTextSharp 和PDFBox 很久没自己写写心得日志与大家分享了,一方面是自己有点忙,一方面是自己有点懒,没有及时总结.因为实践是经验的来源,总结 ...
- 常用PDF文档开发库
C++库: 1,PDF类库 PoDoFo http://podofo.sourceforge.net/ PoDoFo 是一个用来操作 PDF 文件格式的 C++ 类库.它还包含一些小工具用来解析 ...
随机推荐
- 前端性能优化(css动画篇)
正巧看到在送书,于是乎找了找自己博客上记录过的一些东西来及其无耻的蹭书了~~~ 小广告:更多内容可以看我的博客 最近拜读了一下html5rocks上几位大神写的一篇关于CSS3动画性能优化的文章,学到 ...
- vue动画及其原理
1,vue动画的实现原理,主要是通过在不同时期给需要动画的dom元素加上css动画样式 我们以显示和隐藏动画为例 a, 需要动画的dom元素 b,点击时vue控制往vue中加的样式 2, 我们以两张 ...
- loj#2531. 「CQOI2018」破解 D-H 协议(BSGS)
题意 题目链接 Sol 搞个BSGS板子出题人也是很棒棒哦 #include<bits/stdc++.h> #define Pair pair<int, int> #defin ...
- cesium 之核心类 Viewer 简介篇
前言 cesium 官网的api文档介绍地址cesium官网api,里面详细的介绍 cesium 各个类的介绍,还有就是在线例子:cesium 官网在线例子,这个也是学习 cesium 的好素材. 简 ...
- MongoDB的存储结构及对空间使用率的影响
MongoDB的存储结构及对空间使用率的影响 使用MongoDB一段时间的同学肯定会发现,MongoDB往往会占用比实际数据大小多不少空间的问题.如果利用db.stats()命令去查看,会发现Mong ...
- Spark之谓词下推
谓词下推就是指将各个条件先应用到对应的数据上,而不是根据写入的顺序执行,这样就可以先过滤掉部分数据,降低join等一系列操作的数据量级,提高运算速度,如下图:
- selenium-获取元素属性(六)
获取元素属性很简单,使用 get_attribute 方法即可 如下图 获取具体的属性直接将该属性名当作参数传入即可 若是获取值,则获取的实则是该元素的 value,需要将 value 当参数传入 i ...
- Linux中输入输出重定向的问题
Linux 命令默认从标准输入设备(stdin)获取输入,将结果输出到标准输出设备(stdout)显示.一般情况下,标准输入设备就是键盘,标准输出设备就是终端,即显示器. 输出重定向:命令的输出不 ...
- LNMP时,出现502 Bad Gateway的错误提示
因为工作需要,要在ubuntu中安装LNMP环境,在这里,php是最新版本php7.1.一切都进展得很顺利,安装完成后,在浏览器中输入http://127.0.0.1/info.php,出现了502 ...
- c#操作SQL Server入门总结
我是一名c#新手.本文只是我是常学习的随笔. 一.下载SQL server软件 听说下载开发板是最好的(开发板如果只是用来学习.研究不算是侵权).在安装的时候,我也遇到了很多问题,在公司的电脑安装第一 ...