Developer Guide
This developer guide will help you understand the PDFKit.NET 4.0 class library. It walks through the main concepts and illustrates them with code samples. This guide is not a type reference. Instead, this is included in the evaluation download.
Features
PDFKit.NET is a 100% managed (verifiable) .NET class library for creating and manipulating PDF documents. It consists of just a single assembly that can be xcopy-deployed. It has no dependencies other than the .NET framework. Central to PDFKit.NET is a consistent and highly intuitive object model consisting of classes like Document, PageCollection, Page, Canvas, Shape, Bookmark, Annotation, Field, etc. The focus of the development team is always to ease the task of integrating our class libray in a larger application. These are PDFKit.NET’s primary features:
- Fill and flatten PDF forms
- Split and assemble PDF documents
- Apply security settings such as passwords
- Stamp content on new and existing pages
- Support for CMYK and Grayscale colorspace
- Read, Create and Modify Actions, Sticky Notes and Links
- Active JavaScript interpretation (for formatted and calculated fields)
- Adobe LiveCycle Designer compatibility (static forms only)
- Extract and search text
- Digitally signing and verification
- Native .NET 2.0 support, including 64-bit support
What’s new in 4.0?
The following features have been added in 3.0:
- We added a WPF-only build without a reference to System.Drawing
- You can now save to a PDF/A-1b compliant document
- We added support for QR barcodes.
- The ImageShape now supports loading of JBIG2 images.
- .NET 1.1 is no longer supported
Code Samples in this Guide
All non—trivial code samples are available as ready-to-run Visual Studio .NET projects. These samples are included in the installation package which can be downloaded from our website.
Just unzip to any folder and open codesample.sln in either Visual Studio.NET 2003 or 2005 (will convert first). Each code sample is available as a separate project inside this solution. If the code sample is part of the solution, the name of the corresponding project is included in the caption line of the code sample.
Upgrading from PDFKit.NET 4.0
PDFKit.NET 4.0 is fully backwards compatible. Simply replace the assembly and recompile.
Please send us Feedback!
Please help us improve this document. If you have any questions, suggestions or find anything in this document is incorrect or missing then let us know at support@tallcomponents.com.
Visit us Online
Visit our web site: http://www.tallcomponents.com.
Naming Convention
We try to adhere as much as possible to Microsoft’s “Design Guidelines for Developing Class Libraries”. The guideline can be found here:
https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
Document structure
The top-level structure of a PDF document is shown below.
You can either open an existing document or create a new document as shown in the following code sample:
using System;
using System.IO;
using TallComponents.PDF;
// create a new empty document
Document document1 = new Document();
// open an existing document
using ( FileStream file = new FileStream(
"report.pdf",
FileMode.Open, FileAccess.Read ) )
{
Document document2 = new Document( file );
}
Code sample: Create a new document or open an existing document
Pages
The property Pages of class Document allows you to enumerate, remove and insert pages. Chapter Actions discusses pages in more detail.
Fields
The property Fields of class Document allows you to enumerate, remove and insert form fields. Fields are discussed in more detail in Forms.
Bookmarks
A PDF document has a bookmarks tree which is also known as the document outline or table of contents. The top-level bookmarks can be accessed through the Document.Bookmarks property. From here you can enumerate the entire tree. You can also add, insert and remove bookmarks and modify bookmark properties. Bookmarks are discussed in more detail in Navigation.
Viewer Preferences
The viewer preferences define how a PDF reader should display a PDF document initially. Viewer preferences are discussed in more detail in Navigation.
JavaScript
Just like you can embed JavaScript in an HTML page for things like input validation and dynamically changing element properties, you can embed JavaScript in a PDF document for similar purposes. In fact, if you set the input mask on a text field, some JavaScript will be inserted on the fly to make this happen.
Document Info
The document info consists of simple properties such as Author, Creator and Subject. The following snippet dumps part of the document info of a given document to the Console:
// open file
using ( FileStream file = new FileStream(
"my.pdf", FileMode.Open, FileAccess.Read ) )
{
// open PDF document
Document document = new Document( file );
// dump info to console
Console.WriteLine("author: {0}", document.DocumentInfo.Author );
Console.WriteLine("subject: {0}", document.DocumentInfo.Subject );
Console.WriteLine("title: {0}", document.DocumentInfo.Title );
}
Code sample: Dump the PDF document info to the console (DumpInfo)
Metadata
Metadata schemas are embedded as XML documents. This data is often inserted by PDF producers to preserve metadata such as high-level layout information. A metadata schema is a collection of name-value pairs. A value is of type MetadataValue which is an abstract base class. The only concrete specializations are MetadataString and UnsupportedMetadataValue. We will add support for other types than string in future updates.
Security
The Document.Security property gives you access to the security settings of the document. The property is of type Security which is an abstract base class. The only concrete derived class is PasswordSecurity. We anticipate to provide more and allow you to write custom security handlers – hence this approach. You can assign securty settings like this:
// open file
using ( FileStream fileIn = new FileStream(
"guide.pdf",
FileMode.Open, FileAccess.Read ) )
{
// open source PDF
Document document = new Document( fileIn );
// assign owner and user passwords
PasswordSecurity security = new PasswordSecurity();
security.OwnerPassword = "1234";
security.UserPassword = "GoodNews";
document.Security = security;
// save as a new PDF document
using ( FileStream fileOut = new FileStream(
"guide_protected.pdf",
FileMode.Create, FileAccess.Write ) )
{
document.Write( fileOut );
}
}
Code sample: Save PDF document with passwords (AssignPassword)
Navigation
In addition to the standard navigation means of a PDF reader application, PDF allows you to include navigation elements such as bookmarks and links. Central to navigation is the Destination concept. A Destination encapsulates all information that a reader application needs to jump to a location inside or outside a PDF document.
Destination
A destination can either be internal or remote. An internal destination is a location inside the current PDF document. A remote destination points to a location inside another PDF document. Finally, a destination can be named. A named destination can be resolved to an internal destination through the Document.NamedDestinations property. This is a collection that maps names to internal destinations. Destination is an abstract base class. InternalDestination, RemoteDestination and NamedDestination are concrete specializations. See class hierarchy below.
Internal destination
The following code snippet adds a link to the first page that points to the third page.
// create the internal destination object:
InternalDestination destination = new InternalDestination();
destination.Page = document.Pages[2];
destination.PageDisplay = PageDisplay.FitEntire;
// add the destination to a link via a GoToAction:
GoToAction action = new GoToAction( destination );
Link link = new Link( left, top, width, height );
link.MouseUpActions.Add( action );
// add the link to the first page:
document.Pages[0].Links.Add( link );
Code sample: Add a cross-reference link inside the same document (InternalDest)
Remote destination
The following code snippet adds a link to the first page that points to the third page of another PDF document. It also instructs the PDF reader to open the document in a new window.
// create the remote destination:
RemoteDestination destination = new RemoteDestination();
destination.FileSpecification = @"c:\temp\Upgrading to PDFKit.NET 2.0.pdf";
destination.PageIndex = 2; // third page
destination.PageDisplay = PageDisplay.FitEntire;
destination. WindowBehavior = WindowBehavior.NewWindow;
// add the destination to a link via a GoToAction:
GoToAction action = new GoToAction( destination );
Link link = new Link( left, top, width, height );
link.MouseUpActions.Add( action );
// add the link to the first page:
document.Pages[0].Links.Add( link );
Code sample: Add a link to an external PDF document (RemoteDest)
Named destination
The following code snippet dumps all named destination in a given document and reports to what page the associated internal destination points.
foreach ( string name in document.NamedDestinations.Names )
{
// resolve name to internal destination
InternalDestination destination = document.NamedDestinations[ name ];
Console.WriteLine( "{0} points to page {1}",
name, destination.Page.Index );
}
Code sample: Dump named destinations
Links
Links are areas that navigate the PDF reader to another location. In PDF, links are implemented as Link annotations. A link annotation occupies a rectangle on a page and when clicked a sequence of PDF actions is executed. Although this can be any sequence of any actions, it is most often a single GoToAction that points to a given Destination. Code sample Add a cross-reference link inside the same document and Code sample Add a link to an external PDF document demonstrate how to create a link and associate it with a destination.
Bookmarks
A PDF document has a bookmarks tree which is also known as the document outline or table of contents. See image to the right. The top-level bookmarks can be accessed through the Document.Bookmarks property. From here you can enumerate the entire tree. You can also add, insert and remove bookmarks and modify bookmark properties.
The following code splits a document into parts according to its top-level bookmarks. Each part is named according to the title of the bookmark.
BookmarkCollection bookmarks = document.Bookmarks;
for ( int index=0, fromPage=0; index<bookmarks.Count; index++ )
{
// determine last page of next part
int toPage = -1;
Bookmark bookmark = null;
if ( index == bookmarks.Count-1 )
{
// last bookmark - append all remaining pages
toPage = document.Pages.Count;
}
else
{
// not the last bookmark - append up to the next bookmark
bookmark = bookmarks[index+1];
GoToAction action = bookmark.Actions[0] as GoToAction;
if ( null != action )
{
InternalDestination destination = action.Destination as
InternalDestination;
if ( null != destination )
{
toPage = destination.Page.Index;
}
}
}
// create a new part
if ( -1 != toPage && null != bookmark )
{
Document part = new Document();
for ( int pageIndex=fromPage; pageIndex<toPage; pageIndex++ )
{
part.Pages.Add( document.Pages[pageIndex].Clone() );
}
using ( FileStream fileOut = new FileStream(
bookmark.Title + ".pdf", FileMode.Create, FileAccess.Write ) )
{
part.Write( fileOut );
}
fromPage = toPage;
}
}
Code sample: Split a document according to its top-level bookmarks (SplitByBookmark)
Viewer Preferences
The viewer preferences define how a PDF reader should display a PDF document initially. The following code snippet shows you how to open, change and save a PDF so that the next time you open it, it will be displayed without menu and toolbar.
// open file
using ( FileStream fileIn = new FileStream(
"guide.pdf",
FileMode.Open, FileAccess.Read ) )
{
// open source PDF
Document document = new Document( fileIn );
// assign custom viewer preferences
ViewerPreferences preferences = new ViewerPreferences();
preferences.HideMenubar = true;
preferences.HideToolbar = true;
document.ViewerPreferences = preferences;
// save as a new PDF document
using ( FileStream fileOut = new FileStream(
"guide_nomenunotoolbar.pdf",
FileMode.Create, FileAccess.Write ) )
{
document.Write( fileOut );
}
}
Code sample: Save PDF document with modified viewer preferences (ViewerPrefs)
Annotations
An annotation is a rectangular area on a PDF page that the user can interact with. Examples are ‘links’, ‘sticky notes’ and ‘widgets’. All annotation classes and related types live in the TallComponents.PDF.Annotations namespace and nested namespaces.
Annotation
Annotation is the abstract base class of all annotation classes. The figure below shows the class hierarchy of the annotations that we currently support:
Class Annotation has the following members that are common to all annotation types:
Annotation members
Public properties | |
---|---|
Page | The page that contains this annotation. Read-only. |
Left, Bottom, Right, Top, Width and Height | The position and size in page space. Right and Top are read-only. |
BorderWidth | Width of the border. 0 means no border. |
BorderColor | Color of the border (must be RgbColor for non-widget annotation). |
BorderStyle | Style of the border (Solid, Dashed, etc.) |
Invisible | The annotation is not visible on screen. |
Locked | Whether the annotation is locked in the viewer application. |
The annotation is printed. |
Public methods | ||
---|---|---|
Flatten | The page that contains this annotation. Read-only. | |
Accept | Supports the Visitor pattern. |
Widget
As a special case, a widget annotation (or widget) is the visual representation of a field that provides interactive access to the field value. In theory, a field can have multiple widgets. Widgets will be discussed in more detail in Forms.
Link
The link annotation is a clickable area that executes a sequence of actions. To jump to a destination you add a single action of type GoToAction. To jump to a URL you add a single action of type UriAction. Of course you can add any number of actions of any type.
In addition to the members inherited from Annotation, Link has the following specific members:
Members specific to Link
Public properties | ||
---|---|---|
HighlightStyle | The visual effect that is used when the mouse is pressed inside the annotation area. | |
MouseUpActions | The sequence of actions to execute when the mouse button is released in the Link area. |
Code sample Add a cross-reference link inside the same document shows how to add a link to a page that points to another page. Code sample Add a link to an external PDF document shows how to add a link to a page that points to an external PDF document.
Markup
Markup annotations are used to mark-up a document. In a typical mark-up scenario, the author of a document asks a peer to review the document. The peer will add comments at specific location in the document, e.g. to mark-up a typo or to mark-up a figure that is not clear enough. Mark-ups can take several visual forms of which a simple textual note is the most common one.
Markup is the abstract base class of all markup annotations. It has a number of properties that are common to all markup annotations.
Memebers specific to Markup
Public properties | |
---|---|
Replies, InReplyTo | Respectively, all mark-ups that reply to this mark-up and the mark-up to which this mark-up replies. These two properties allow you to create a complete thread of mark-ups. InReplyTo is read-only. |
Text | The text of this mark-up. This text may contain simple formatting tags such as bold. |
Popup | The pop-up that displays the text of this mark-up. |
Opacity | The opacity of this mark-up (0 means fully transparent, 255 means fully opaque). |
Author, CreationDate and Subject | The author, creation date and subject of this mark-up. |
Note
A note is the most common mark-up. The class Note is a concrete specialization of the abstract base class Markup. It has the following specific members:
Members specific to Note
Public properties | |
---|---|
IconName | Viewer applications have predefined icons for at least the following names: Comment, Key, Note, Help, NewParagraph, Paragraph and Insert. |
StateModel | State model of this mark-up. Existing state models:
|
Marked | Meaningful if state model is Marked. |
ReviewState | Meaningful if state model is Review. |
MigrationState | Meaningful if state model is Migration. |
Popup
This is a special annotation that displays the text of a markup as a pop-up message. The pop-up allows the user to edit the text. A pop-up is associated with a markup through its Markup property. A markup is associated with a popup through its Popup property. Note that the displayed text is a property of the markup, not of the pop-up.
In addition to the members inherited from Annotation, Popup has the following specific members:
Members specific to Popup
Public properties | |
---|---|
Open | Whether the pop-up should initially be displayed open. |
Markup | The Markup to which this pop-up belongs. Read-only. |
The following code sample creates a new document with a single page. The page has a note with an open pop-up noting that the page is empty.
// create a new document with a single empty page
Document document = new Document();
Page page = new Page( PageSize.Letter );
document.Pages.Add( page );
// add a new note
Note note = new Note( 300, 300, 10, 10 );
note.Text = "This page is empty.";
note.IconName = "Note";
page.Markups.Add( note );
// attach an open popup to the note
Popup popup = new Popup( 320, 350, 200, 100 );
popup.Open = true;
note.Popup = popup;
// save the new document
sing ( FileStream file = new FileStream(
@"..\..\note.pdf",
FileMode.Create, FileAccess.Write ) )
{
document.Write( file );
}
Code sample: Create a new document with a single page, a note and a popup (AddNote)
Created from Code sample Create a new document with a single page, a note and a popup
Forms
PDF documents that include fillable fields are referred to as PDF forms. A typical example is an IRS tax form such as the W-4.
Fields
In general, a PDF form has different types of fields. E.g. text fields to enter you first and last name, a group of radio buttons to select your marital status and a checkbox to indicate whether you filed the same tax form last year.
The class TallComponents.PDF.Forms.Fields.Field represent a PDF form field. It is an abstract base class that has the following inheritance hierarchy:
Fields hierarchy (UnknowmField and UnknowBarcodeField omitted)
All fields in a PDF document can be accessed through the Document.Fields property. It returns an instance of type TallComponents.PDF.Forms.Fields.FieldCollection. You may be surprised that the collection of fields is contained by a Document instead of by a Page. This is explained in the next section.
Widgets
A widget is an annotation that provides the visual representation of a field. It also allows the user to interact with the field, e.g. to enter a text or select a date from a calendar control. In general, a field can have multiple widgets but in most cases it only has one. A field is contained by a document. A widget is contained by a page.
Document, fields, pages and widgets
The following diagram shows the Widget inheritance hierarchy.
Widget inheritance hierarchy
As said, a field is associated with one or more widgets. The following table shows what type of field corresponds to what type of widget.
Correspondence between Field and Widget type
Field | Widget | Remarks |
---|---|---|
SignatureField | SignatureWidget | The SignatureWidget has additional properties to customize the appearance, e.g. display text only, image only, both. Display the date or not, display the reaon or not, etc. |
PushButtonField | PushButtonWidget | The PushButtonWidget has additional properties that allow you to specify a label, an icon and how the label and the icon should laid out (label above icon, icon only, label only, etc.). |
CheckBoxField | CheckBoxWidget | The CheckBoxWidget has an additional appearance to specify the check mark (diamond, check, start, etc.). |
RadioButtonField | RadioButtonWidget | Just like the CheckBoxWidget, the RadioButtonWidget has a property to specify the appearance of the chek mark. In addition, it has a property Option that represents one of the radio button options that can be selected. |
DateTimeField, DropDownListField, ImageField, ListBoxField, NumericField, PasswordField, TextField, Code128BarcodeField, Code2of5InterleavedBarcodeField, Code3of9BarcodeField | Widget | These fields are all visually represented by the same Widget class. |
All widget types have a common base class Widget. Widget in turn inherits from Annotation, see Annotations. The table below lists the members that are specific to Widget annotations.
Public Widget members
Public properties | |
---|---|
Field | The field that is associated with this widget. Read-only. |
Font, FontSize, TextColor, HorizontalAlignment, VerticalAlignment | Properties that specify how text is displayed. |
Invisible, Orientation, BackgroundColor | Appearance properties. |
Persistency | Allows you to specify how a widget is persisted when the document is written. You can leave a widget unchanged, you can remove it and finally it can be flattened. This means that after writing the document, the widget is replaced by static, non-interactive content. |
GotFocusActions, LostFocusActions | Actions to be executed when the widget gets or loses keyboard focus. |
MouseDownActions, MouseUpActions, MouseEnterActions, MouseExitActions | Actions to be executed per mouse event. |
The following code snippet creates new document with a single page and a text field:
// create new document
Document document = new Document();
Page page = new Page( PageSize.Letter );
document.Pages.Add( page );
// add a new text field and add it to the new document
TextField textField = new TextField( "text1" );
textField.Value = "Hello!";
document.Fields.Add( textField );
// add a new widget and connect the widget to the text field and the page
Widget widget = new Widget( 200, 400, 200, 50 );
widget.TextColor = RgbColor.Red;
widget.BorderColor = RgbColor.Black;
widget.BorderStyle = BorderStyle.Solid;
widget.BorderWidth = 2;
textField.Widgets.Add( widget );
page.Widgets.Add( widget );
// save the new document
using ( FileStream file = new FileStream(
@"..\..\textfield.pdf",
FileMode.Create, FileAccess.Write ) )
{
document.Write( file );
}
Code sample: Create a new document with a single page and a text field (AddTextField)
Textfield as created by Code sample Create a new document with a single page and a text field
Fill Fields
Filling a form field is as simple as setting the Value property of a field. Depending on the type of field, special XxxValue properties exist. Here are some code samples that show how to fill form fields:
using ( FileStream fileIn = new FileStream(
@"fields.pdf", FileMode.Open, FileAccess.Read ) )
{
Document document = new Document( fileIn );
TextField textField = document.Fields[ "Text1" ] as TextField;
textField.Value = "Hello";
CheckBoxField checkBox = document.Fields[ "Check Box2" ] as CheckBoxField;
checkBox.CheckBoxValue = CheckState.On;
RadioButtonField radioButton = document.Fields[ "Radio Button4" ]
as RadioButtonField;
radioButton.RadioButtonValue = radioButton.Options[1]; // second option
ListBoxField listBox = document.Fields[ "List Box7" ] as ListBoxField;
listBox.ListBoxValue =
new ListOption[] { listBox.Options[1] }; // second option
DropDownListField dropDown = document.Fields[ "Combo Box8" ]
as DropDownListField;
dropDown.DropDownListValue = dropDown.Options[1]; // second option
using ( FileStream fileOut = new FileStream(
@"out.pdf", FileMode.Create, FileAccess.Write ) )
{
document.Write( fileOut );
}
}
Code sample: Fill fields (FillFields)
Form Data
At each point a field has a value. This can be text entered into a text field or the signature state of a signature field. The collection of all values of all fields of a document is referred to as form data. Form data can be persisted in different formats (e.g The Acrobat JavaScript Scripting Reference mentions a form data format called ‘XFD’. We have no idea what this format is and we suspect that it is a typo (from XFDF which is included as well)), the most known being Form Data Format (FDF). The class FormData is the abstract base class of all classes that each represent one format of persisted form data. The following inheritance hierarchy shows the different FormData classes.
FormData inheritance hierarchy
Export
Exporting form data in any of the supported format is really easy. Simply call the method Document.Export and specify what format you want:
using ( FileStream pdfFile = new FileStream(
"fw4.pdf", FileMode.Open, FileAccess.Read ) )
{
Document document = new Document( pdfFile );
// fill two fields
TextField firstName = document.Fields["f1_09(0)"] as TextField;
firstName.Value = "Chris";
TextField lastName = document.Fields["f1_10(0)"] as TextField;
lastName.Value = "Sharp";
// export as FDF
FdfFormData fdf = document.Export( FormDataFormat.Fdf ) as FdfFormData;
fdf.Path = "fw4.pdf";
// save FDF
using ( FileStream fdfFile = new FileStream(
"fw4.fdf", FileMode.Create, FileAccess.Write ) )
{
fdf.Write( fdfFile );
}
}
Code sample: Export form data as FDF (ExportFdf)
Import
Importing existing form data into a PDF document is just as easy. Simply create a FormData instance from persisted form data (can be in memory) and pass this instance to Document.Import:
// open PDF file
using ( FileStream fileIn = new FileStream(
"fw4.pdf", FileMode.Open, FileAccess.Read ) )
{
Document document = new Document( fileIn );
// open FDF file
using ( FileStream fdfFile = new FileStream(
"fw4.fdf", FileMode.Open, FileAccess.Read ) )
{
FdfFormData fdfData = new FdfFormData( fdfFile );
// import FDF data
document.Import( fdfData );
// save modified PDF file
using ( FileStream fileOut = new FileStream(
"fw4_afterimport.pdf", FileMode.Create, FileAccess.Write ) )
{
document.Write( fileOut );
}
}
Code sample: Import form data from FDF (ImportFdf)
Adobe LiveCycle Designer Support
Dynamic XFA documents are not supported. If you open such a document with, an UnsupportPdfException is thrown. Static XFA documents are supported.
Numeric field, DateTime field, Image field and Barcode field are represented by the corresponding classes in the TallComponents.PDF.Forms.Fields namespace. These field types cannot be created. It is not possible to add these field types to an existing non-XFA (classic) PDF document. It is not possible to add new fields/widgets to static XFA document.
The property Document.DocumentType reflects the type of PDF document. Possible values are: Classic, StatcXfa and DynamicXfa. The enum value DynamicXfa will never be returned because an exception is already thrown at construction time.
To summarize, we support Adobe LiveCycle Designer document with the following restrictions:
- We support static XFA only (no dynamic XFA).
- We do not support repeated subforms. See ‘repeat subform for each data item’ flag in Binding properties.
- You cannot add new fields/widgets.
- You cannot remove fields by calling the FieldCollection.Remove() method. You can however remove fields by setting widget.Persistency to WidgetPersistency.Flatten or WidgetPersistency.Remove.
- When adding a page from a XFA document to an other document, the XFA fields are automatically converted to classic types (e.g. NumericField will be TextField).
- We do not support data-bindings. The default binding must be ‘Normal’. You can set field values through property ValueField.Value.
- We do not support events and script objects/variables.
The following table describes the relationship between the different versions of the Adobe LiveCycle Designer product, the different versions of the XFA format and the different versions of the Adobe PDF Reader and which ones are supported by PDFKit.NET 2.0 and 3.0.
Adobe (LiveCycle) Designer | Produces XFA version | Requires Adobe PDF Reader | Requires PDFKit.NET |
---|---|---|---|
6.0 | 2.0 | 6.02 | 2.0 |
7.0 | 2.1 | 6.02 | 2.0 |
7.0 BETA | 2.2 | 7.05 | 2.0 |
7.1 | 2.4 | 7.05 | 2.0 |
8.0 | 2.5 | 8.0 | 2.0 |
8.1 | 2.6 | 8.1 | 3.0 |
8.1.2 | 2.7 | 8.2 | 3.0 |
ES 8.2 | 2.8 | 9.0 | 3.0 |
No public release | 3.0 | 9.1 | Not supported yet |
Digital Signatures
Digitally signed PDF documents (or messages in general) include two parties: the sender and the recipient. The sender applies a digital signature using a private key. It is assumed that only the sender has access to the private key and therefore it represents the identity of the sender. Using the public key, the recipient can confirm with high confidence that the signature was applied with the sender’s private key and so by the only person who has access to the private key. The recipient can also be confident that the document has not been modified since it was signed.
Signature Encodings
This application allows you to sign, verify and validate signature fields using all standard signature encodings. These encodings are:
- adbe.x509.rsa_sha1 (PKCS #1)
- adbe.pkcs7.detached (PKCS #7)
- adbe.pkcs7.sha1 (PKCS #7)
Verification
Verification is the process of determining whether the data that has been signed, has not been changed after it has been signed. If the signed data has not been modified after signing, the signature is said to be verified. The following code opens a PDF document and dumps the verification status of each signature field:
using ( FileStream file = new FileStream(
"f1040a_signed.pdf", FileMode.Open, FileAccess.Read ) )
{
Document document = new Document( file );
foreach ( Field field in document.Fields )
{
// retrieve signature field
SignatureField signature = field as SignatureField;
if ( null != signature && signature.IsSigned )
{
// verifiy signature using standard signature handler
bool verified = signature.Verify();
Console.WriteLine( "signature {0} {1} been verified",
signature.FullName,
verified ? "has" : "has NOT" );
}
}
}
Code sample: Verify all fields (Verify)
You may wonder why we chose a parameterless method Verify, instead of a more elegant getter Verified. The reason for having a method is that we offer 2 more overloads that let you pass a custom signature handler or a signature handler factory.
Updates
It is possible to change a PDF document, e.g. by filling out fields, and to save the changes incrementally. If the document was signed prior to saving the incremental changes, then the signature field still verifies successfully because the data that has been signed has not changed. The property SignatureField.DocumentModifiedAfterSigning lets you check whether incremental changes have been added to the document as an update.
Save changes incrementally as an update
Figure 6 -10 shows how changes are saved incrementally as an update. Update 1 remains unchanged while Update 2 includes all changes. If Update 1 was signed, that signature will still verify successfully after saving because the update has not changed. After saving, the property SignatureField.DocumentModifiedAfterSigning returns false, while it returns true prior to saving.
Validation
While verification is an objective process, validation is not because it involves trust. After verification succeeds, the next step is to decide whether the signer can be trusted (or whether the identity is known). If so, the signature is said to be validated, otherwise it is not. This step is to be executed by your own code by inspecting the SignatureField.Certificates property.
Signing
The following code sample shows how to digitally sign a PDF document. The signature field is named “SignHere”. The certificate is stored inside the file “ChrisSharp.pfx”. This file was exported from the Windows key store. The password of the key store is “Sample”.
// open PDF form with signature field
using ( FileStream inFile = new FileStream(
"f1040a.pdf", FileMode.Open, FileAccess.Read ) )
{
Document document = new Document(inFile);
// open certicate store.
Pkcs12Store store = null;
using (FileStream file = new FileStream(
"ChrisSharp.pfx", FileMode.Open, FileAccess.Read))
{
store = new Pkcs12Store( file, "Sample" );
}
// let the class factory decide which type should be used.
SignatureHandler handler = StandardSignatureHandler.Create( store );
// sign signature field
SignatureField field = document.Fields[ "SignHere" ] as SignatureField;
field.SignatureHandler = handler;
// set optional info.
field.ContactInfo = "+31 (0)77 4748677";
field.Location = "The Netherlands";
field.Reason = "I fully agree!";
// write signed document to disk. signing requires read-write file access
using ( FileStream outFile = new FileStream(
"f1040a_signed.pdf", FileMode.Create, FileAccess.ReadWrite ) )
{
document.Write( outFile );
}
}
Code sample: Digitally sign a PDF document (Sign)
Signature Field States
A signature field can have different states. These are shown in the next figures.
IsSigned == false;
IsSigned == true; Verify() == false;
IsSigned == true; Verify() == true;
IsSigned == true; Verify() == true;
IsSigned == true; Verify() == true;
IsSigned == true; Verify() == true; DocumentModifiedAfterSigning ==true; trusted
Actions
PDF allows you to associate actions with events. These actions are provided through the Action class and its specializations.
The following actions are supported:
- FormAction: Submit or clear form data
- GoToAction: Jump to a destination in the same document or to a page in another PDF document
- HideAction: Show or hide an annotation
- ImportDataAction: Import an FDF file
- JavaScriptAction: Perform scripted operations against the document (Professional edition only)
- LaunchAction: Launch an application
- NamedAction: Execute a named action
- UriAction: Jump to a URI
Each action type is discussed in detail below.
Events
It is the responsibility of the PDF reader to execute the actions that are associated with an event whenever the event occurs. The following properties are of type Action. The properties correspond to events.
- Document.OpenActions: These actions are executed after the document has been opened
- Document.AfterPrint: These actions are executed after the document has been printed
- Document.AfterSave: These actions are executed after the document has been saved
- Document.BeforeClose: These actions are executed before the document is closed
- Document.BeforePrint: These actions are executed before the document is printed
- Document.BeforeSave: These actions are executed before the document has been saved
- Link.MouseUpActions: These actions are executed when the mouse button is released while the pointer is inside the link
- Widget.GotFocusActions: These actions are executed when the widget receives keyboard focus
- Widget.LostFocusActions: These actions are executed when the widget loses keyboard focus
- Widget.MouseDownActions: These actions are executed when the mouse button is pressed while the pointer is inside the widget
- Widget.MouseEntrerActions: These actions are executed when the mouse pointer enters the widget
- Widget.MouseExitActions: These actions are executed when the mouse pointer leaves the widget
- Widget.MouseUpActions: These actions are executed when the mouse button is released while the pointer is inside the widget
- Bookmark.Actions: These actions are executed when the bookmark is selected
Note that all properties are of type ActionCollection. All actions in the collection are executed in order.
FormAction
FormAction is the abstract base class of the following actions:
- ResetFormAction: reset all form fields in the document to their default value
- SubmitFormAction: Submits the form data to a given destination. The URL and submit options such as the format can be specified as properties of this action.
Note that all properties are of type ActionCollection. All actions in the collection are executed in order.
GoToAction
The GoToAction jumps to a destination. The destination is set by assigning the GoToAction.Destination property.
The following types of destinations exist.
Internal destination
The InternalDestination points to a page inside the same document. It is possible to specify the exact location of the viewer, the zoom factor and the way that the page is displayed.
Remote destination
The RemoteDestination points to a page in an external PDF document. It is specified by the path of the PDF document and a page index. You can also specify additional settings such as zoom factor and the exact scroll position after jumping to the location.
Named destination
Class Document has a property called NamedDestinations which is a collection of name-destination pairs. The NamedDestination class lets you specify a destination by specifying the name of a destination in this collection.
HideAction
This action hides or shows one or more annotations or fields.
ImportDataAction
This action lets you import an FDF file by specifying the path.
JavaScriptAction
This action executes JavaScript against the current document. A PDF document is fully scriptable on the client side. Typical usage includes setting a text field value after clicking a button or showing/hiding a field after entering/leaving a form field. Here is the full JavaScript reference.
The following code sample generates a PDF document so that a text field is filled with the current date just before it is printed. This way, you can always see on the print out when it was printed. (How to add fields to a PDF document is discussed in Section Field Shapes.)
Document document = new Document();
JavaScriptAction action = new JavaScriptAction(
string.Format(
"this.getField('printed').value = 'Printed: {0}'", DateTime.Now));
document.BeforePrintAction = action;
using (FileStream file = new FileStream(
@"..\..\out.pdf", FileMode.Create, FileAccess.Write))
{
document.Write(file);
}
Code sample: Add a BeforePrintAction to a document in C#
Document Level JavaScript
The property Document.JavaScripts lets you declare document level JavaScript. This typically contains common functions and constants that can be reused from other JavaScript actions. See JavaScript actions for a more detailed discussion. The following code sample shows how to add a JavaScript function at document level:
JavaScript javaScript = new JavaScript("function foo() { return 10; }");
document.JavaScripts.Add( javaScript );
Code sample: Add a document-level JavaScript function in C#
The document level javascript will need to be run at least once before the definitions in it become available to other javascript code. Existing document level javascript will be run automatically when the document gets opened (if allowed by the ScriptBehavior property), so existing definitions will be available automatically. If you add new definitions however, and you want to use these immediately without saving and reopening the document, you will need to run the document level javascript code explicitly. In order to do so, please execute the following code before executing any other javascript code that relies on it.
foreach (JavaScript javaScript in document.JavaScripts)
{
javaScript.Run(document);
}
Code sample: Explicitly running document level javascript in C# in order to declare new functions.
LaunchAction
This action lets you launch an application. The path property of this class may point either to the application itself or to a document which is then opened by the associated application.
LaunchAction
Adobe has specified a number of named actions. You can specify these through this class by setting the NamedAction.Name property to any of the following values:
- NextPage: go to the next page
- PrevPage: go to the previous page
- FirstPage: go to the first page
- LastPage: go to the last page
UriAction
The UriAction jumps to a given URI such as http://www.tallcomponents.com.
Appending, Splitting and Imposition
Using this application it is possible to construct new documents from pages from existing documents. This way you can split existing documents into multiple new document, you assemble new documents from multiple existing documents and you can create new pages that are composed of multiple new or existing pages.
Append
Appending pages to a document is extremely simple. The following code shows how a new output document is created from an assortment of new and existing pages. Note that 3 different methods for adding a range of pages are shown.
using ( FileStream file1 = new FileStream(
@"..\..\..\f1040a.pdf", FileMode.Open, FileAccess.Read ) )
using ( FileStream file2 = new FileStream(
@"..\..\..\fw4.pdf", FileMode.Open, FileAccess.Read ) )
using ( FileStream file3 = new FileStream(
@"..\..\..\ResellerGuide.pdf", FileMode.Open, FileAccess.Read ) )
{
Document document = new Document();
// add all pages from f1040a.pdf (using foreach)
Document document1 = new Document( file1 );
foreach ( Page page in document1.Pages )
{
document.Pages.Add( page.Clone() );
}
// add all pages from fw4.pdf (using AddRange)
Document document2 = new Document( file2 );
document.Pages.AddRange( document2.Pages.CloneToArray() );
// add all pages from ResellerGuide.pdf (using page index)
Document document3 = new Document( file3 );
for ( int i=0; i<document3.Pages.Count; i++ )
{
document.Pages.Add( document3.Pages[i].Clone() );
}
// save new document with all pages
using ( FileStream file = new FileStream(
@"..\..\out.pdf", FileMode.Create, FileAccess.Write ) )
{
document.Write( file );
}
}
Code sample: Combine all pages of multiple documents in one document (Append)
Split
Splitting a document into multiple documents really boils down to the same kind of code that you use to append pages to a document. The following code shows how a document is split in chunks of 5 or less pages:
using ( FileStream fileIn = new FileStream(
@"ResellerGuide.pdf", FileMode.Open, FileAccess.Read ) )
{
// open source document
Document documentIn = new Document( fileIn );
int n = documentIn.Pages.Count;
for ( int i=0; i<n; i+=5 )
{
// create a page array of the next 5 (or less) pages
Page[] pages = new Page[ Math.Min( i+5, n ) - i ];
for ( int j=0; j<pages.Length; j++ )
{
pages[j] = documentIn.Pages[i+j].Clone();
}
// create a new document and add the range of pages
Document document = new Document();
document.Pages.AddRange( pages );
using ( FileStream fileOut = new FileStream(
string.Format( @"..\..\out{0}-{1}.pdf", i+1, i+pages.Length ),
FileMode.Create, FileAccess.Write ) )
{
// save next document
document.Write( fileOut );
}
}
}
Code sample: Split a document in chunks of 5 or less pages (Split)
Imposition
Figure below shows a typical imposition scenario known as 2-up.
Typical imposition scenario 2-up
The following code sample shows how to implement the 2-up scenario.
using ( FileStream fileIn = new FileStream(
"ResellerGuide.pdf", FileMode.Open, FileAccess.Read ) )
{
// open source document
Document documentIn = new Document( fileIn );
PageCollection pages = documentIn.Pages;
// set new width and height
double height = pages[0].Width;
double width = pages[0].Height;
// create new document
Document document = new Document();
for ( int i=0; i<pages.Count; i+=2 )
{
Page page = new Page( width, height );
// add left page as page shape
PageShape pageShape1 = new PageShape(
pages[i], 0, 0, width / 2, height, true, 0,
PageBoundary.MediaBox );
page.Overlay.Add( pageShape1 );
if ( i+1 == pages.Count ) break;
// add right page as page shape
PageShape pageShape2 = new PageShape(
pages[i+1], width / 2, 0, width / 2, height, true, 0,
PageBoundary.MediaBox );
page.Overlay.Add( pageShape2 );
document.Pages.Add( page );
}
// save new document
using ( FileStream fileOut = new FileStream(
"out.pdf", FileMode.Create, FileAccess.Write ) )
{
document.Write( fileOut );
}
}
Code sample: Create 2-up document (2up)
Drawing with Shapes
In this application you can add new graphics to a new or existing PDF page. You do this by adding Shape objects to a Canvas object that is associated with a Page. Shape is an abstract base class and we offer a collection of concrete specializations such as TextShape, ImageShape and PageShape.
Canvas
A canvas is the top-level container of shape objects. Each page has 4 canvas objects that are accessed through the following Page properties: Overlay, VisualOverlay, Underlay and VisualUnderlay.
When you add shapes to Overlay or VisualOverlay, they appear on top of the existing content. In other words, shapes in these canvasses may occlude the existing content.
When you add shapes to Underlay or VisualUnderlay, they appear behind the existing content. In other words, shapes in these canvasses may be occluded by the existing content. There is a catch that the existing content of a page has an opaque white area spanning the entire page. In this case you will not see any shapes in Underlay or VisualUnderlay. You may be surprised by this because you assume that the white opaque area is actually empty page area.
The distinction between VisualOverlay and Overlay proper lies in the fact that VisualOverlay accounts for any cropping or rotation of the page. The same holds for VisualUnderlay and Underlay. This is shown is the figure below. The bounding box in visual page space is the intersection of the media box and the crop box.
Page space versus Visual page space
Base class Shape
All shapes inherit directly or indirectly from Shape.
Partial shapes class hierarchy
ContentShape
ContentShape adds the following properties to specify transformation, blend mode and opacity.
Public properties specific to ContentShape
Public properties | |
---|---|
Opacity | 0 means fully transparent. 255 means fully opaque. |
BlendMode | Defines how this content shape blends with its background. Default is Inherit (meaning: the same as its parent). |
Transform | The geometric transformation applied to this content shape. Default is no transformation. |
Destination
The ContentShape.Transform property lets you apply any geometric transform to a ContentShape. It is modelled after the WPF equivalent. This is described in more detail in Section Transforming content shapes.
TextShape
TextShape lets you draw single-line text. The X and Y properties that are inherited from Shape, correspond to the lower-left corner. The following table lists the public properties of TextShape.
Public properties specific to TextShape
Public properties | |
---|---|
Text | The text that is displayed. |
MeasuredWidth, MeasuredHeight | Read-only properties that reflect the size of the text with the current settings. |
Pen, Brush, Font, FontSize, Italic, Bold | Appearance properties. |
ReadDirection | Read direction (left-to-right or right-to-left). |
BoundingBox | The bounding box after rotation is applied. |
MultilineTextShape
MultilineTextShape lets you draw multi-line text with mixed formatting.
The content of the multi-line text shape is specified as a collection of fragments. Each fragment has properties that specify both content and appearance. All fragments will be displayed concatenated and broken across lines as needed.
As opposed to the single-line TextShape you can set the Width. This determines where lines break. In addition you can set the Height property but this is only useful if the multi-line text shape is auto-sized. This is the case if and only if there is exactly one fragment that has a zero font size.
Barcodes
Currently we offer 3 barcode shapes: Code128BarcodeShape, Code2of5BarcodeShape and Code3of9BarcodeShape. They all inherit from the abstract base class OneDimensionalBarcodeShape, which in turn inherits from the abstract base class BarcodeShape, which in turn inherits from Shape. We anticipate adding 2-dimensional barcode shapes, hence this hierarchy.
ShapeCollection
There is a special shape called ShapeCollection which is a group of shape objects. Because a ShapeCollection is a Shape, it allows nesting of shapes to any depth. The purpose of ShapeCollection becomes clear if we look at the members that are specific to ShapeCollection.
Public properties specific to ShapeCollection
Public properties (typical collection members omitted) | |
---|---|
Width, Height | Width and Height of the shape collection with respect to the parent coordinate space. This parent is either a ShapeCollection or a Canvas. |
VirtualWidth, VirtualHeight | These are the width and height of the shape collection as seen by the child shapes. If these are different than Width and Height, a scaling is applied. VirtualWidth and VirtualHeight decouple the dimensions of the child shapes from the actual size of the parent shape collection. |
Clip | Clip the child shapes to the Width and Height. |
PathShape
PathShape is the abstract base class of all shapes that involve stroking and filling. The table below shows the public members that are specific to PathShape.
Public properties specific to PathShape
Public properties | |
---|---|
Pen | This Pen is used to stroke this path shape. |
Brush | This Brush is used to fill the closed region of this path shape. |
Below is the class hierarchy starting with the PathShape.
PathShape and derived classes
FreeHandShape
The FreeHandShape allow you to build an arbitray curve composed of straight lines and bezier curves. The table below shows the members that are specific to FreeHandShape.
Public properties of FreeHandShape
Public properties | |
---|---|
Paths | A collection of FreeHandPath objects. You build a freehand shape by adding paths to this collection. |
FillRule | Defines according to which fill rule the interior is filled. Can be either FillRule.NonzeroWindingNumber or FillRule.EvenOdd. |
The following code shows a typical usage of the FreeHandShape:
FreeHandShape freeHandShape = new FreeHandShape();
page.Overlay.Add(freeHandShape);
freeHandShape.Pen = new Pen(GrayColor.Black);
freeHandShape.Brush = new SolidBrush(RgbColor.Red);
FreeHandPath path = new FreeHandPath();
path.Closed = true;
freeHandShape.Paths.Add(path);
path.Segments.Add(new FreeHandStartSegment(150, 150));
path.Segments.Add(new FreeHandLineSegment(400, 150));
path.Segments.Add(new FreeHandBezierSegment(250, 200, 300, 550, 400, 400));
Code sample: FreeHandShape
The result looks like this:
Docking
Sometimes you do not want to or you cannot specify exact positions. Instead you want shapes to be stacked or docked in some direction. The top-level class Shape has a property Dock of type DockStyle. The default value is DockStyle.None meaning that the shape is positioned at exact coordinates. Setting the Dock property to any of the other values turns on docking behavior that is similar to the how Winforms controls can be docked.
Transforming content shape
The ContentShape.Transform property lets you apply a geomatric transformation to a ContentShape object. It lets you position, rotate, scale and skew content shapes or apply any combination of these.
The following code draws an untransformed RectangleShape. The grid has a 30 pts unit.
RectangleShape rect1 = new RectangleShape(60, 120);
rect1.Pen = new Pen(RgbColor.Blue, 3);
rect1.Brush = new AxialGradientBrush(
RgbColor.Red, RgbColor.Green, 0, 0, 0, rect1.Height);
shapes.Add(rect1);
Code sample: Draw an untransformed rectangle
This looks as follows:
The following code adds a translation:
RectangleShape rect2 = new RectangleShape(60, 120);
rect2.Transform = new TranslateTransform(30, 60);
rect2.Pen = new Pen(RgbColor.Blue, 3);
rect2.Brush = new AxialGradientBrush(
RgbColor.Red, RgbColor.Green, 0, 0, 0, rect2.Height);
shapes.Add(rect2);
Code sample: Translate a rectangle shape
This looks as follows:
The following code adds a rotation:
RectangleShape rect3 = new RectangleShape(60, 120);
TransformCollection transforms = new TransformCollection();
rect3.Transform = transforms;
transforms.Add(new TranslateTransform(30, 60));
transforms.Add(new RotateTransform(30));
rect3.Pen = new Pen(RgbColor.Blue, 3);
rect3.Brush = new AxialGradientBrush(
RgbColor.Red, RgbColor.Green, 0, 0, 0, rect3.Height);
shapes.Add(rect3);
Code sample: Translate and then rotate a rectangle
Note that the rectangle is rotated around the origin of the parent, not around its own origin. If you want to rotate it such that the lower-left corner of the rotated rectangle lies at [30,60], then you would need to inverse the transformation order and use the following code:
RectangleShape rect4 = new RectangleShape(60, 120);
TransformCollection transforms = new TransformCollection();
rect4.Transform = transforms;
transforms.Add(new RotateTransform(30));
transforms.Add(new TranslateTransform(30, 60));
rect4.Pen = new Pen(RgbColor.Blue, 3);
rect4.Brush = new AxialGradientBrush(
RgbColor.Red, RgbColor.Green, 0, 0, 0, rect4.Height);
shapes.Add(rect4);
Code sample: Rotate and then translate a rectangle
Clipping
Clipping is done through the ClipShape property. You add it to a shape collection just like the other shapes. It only clips shapes that are added to the same collection after the ClipShape. You can have multiple clip shapes in a single collection. The ClipShape is defined just like the FreeHandShape using a FreeHandPath.
The following code creates a clipping path and clips an image:
ImageShape image = new ImageShape(@"..\..\flowers.jpg");
Page page = new Page(image.Width, image.Height);
document.Pages.Add(page);
ShapeCollection shapes = new ShapeCollection(page.Width, page.Height);
page.Overlay.Add(shapes);
ClipShape clip = new ClipShape();
FreeHandPath path = new FreeHandPath();
clip.Paths.Add(path);
path.Segments.Add(new FreeHandStartSegment(150, 150));
path.Segments.Add(new FreeHandLineSegment(400, 150));
path.Segments.Add(new FreeHandBezierSegment(250, 200, 300, 550, 400, 400));
shapes.Add(clip);
shapes.Add(image);
Code sample: Clipping an image using a ClipShape
It looks as follows:
ShapeCollection.Clip
If this property is set, then everything outside the boundaries of the ShapeCollection is clipped.
Extract Graphics
As of version 3.0, it is possible to extract the existing graphics on a page as a collection of ContentShape objects. This collection and its items can be navigated, inspected, modified and persisted. This allows you to actually change the PDF content.
Page.CreateShapes
The central method to extracting graphics is Page.CreateShapes. The following code copies all content except the images from an existing document to a new document:
static void Main(string[] args)
{
using (FileStream fileIn = new FileStream(
"in.pdf", FileMode.Open, FileAccess.Read))
{
Document pdfIn = new Document(fileIn);
Document pdfOut = new Document();
foreach ( Page page in pdfIn.Pages)
pdfOut.Pages.Add(copy(page));
using (FileStream fileOut = new FileStream(
"out.pdf", FileMode.Create, FileAccess.Write))
{
pdfOut.Write(fileOut);
}
}
}
static Page copy(Page page)
{
Page newPage = new Page(page.Width, page.Height);
ShapeCollection shapes = page.CreateShapes();
purge(shapes);
newPage.Overlay.Add(shapes);
return newPage;
}
static void purge(ShapeCollection shapes)
{
for(int i=0; i<shapes.Count; i++)
{
Shape shape = shapes[i];
if (shape is ImageShape) { shapes.RemoveAt(i); i--; }
if (shape is ShapeCollection) purge(shape as ShapeCollection);
}
}
Code sample: Copy content except images from an existing document to a new document
Shape types
Only the following shape types are returned:
- Structure shapes: ShapeCollection, ClipShape and LayerShape
- Path shapes: FreeHandShape and LineShape
- Graphics shapes: TextShape and ImageShape
Note that multiline text is not converted to a MultilineTextShape object and that paths are not converted to EllipseShape or RectangleShape objects.
Transformations
The Transform property of each ContentShape is set to an object of type TransformCollection. The items inside this collection are always of type MatrixTransform. Each transformation inside a PDF document is preserved as a single MatrixTransform so they are not collapsed.
Restrictions
The following features are not preserved while extracting graphics since they are not yet supported by the Shape object model:
- Complex transparencies will be reduced to a single opacity value of type double (ContentShape.Opacity).
- When modifying the TextShape.Text property, you need to be aware that if it uses a subsetted font, it may not be able to display the new text. This is the case if characters are used for which no representation exists in the subsetted font.
- Shading types FunctionBasedShading (Type1), FreeFormGouraudShaded (Type4), LatticeFormGouraudShaded (type5), CoonsPatchMeshes (type6) and TensorProductPatchMeshes (type7) shadings are not supported. These are mapped to no fill.
- The functions of shading types Axial Shading (type2) and RadialShading(type3) are converted to an array of color stops to achieve a similar visual effect.
- Not all PDF color spaces are supported. Especially CIE based color spaces, DeviceN, Indexed and Pattern. These are converted to RGB to achieve a similar visual effect.

We have sent an email with a download link. Alternatively, you may want to use the NuGet package manager to install our library.
Use the NugetID and start right away, or download the package and install it handmatically