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.

Document Structure

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.

Destination Class Hierarchy

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 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.

Bookmarks

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:

Annotation Class Hierarchy

Class Annotation has the following members that are common to all annotation types:

Annotation members

Public properties
PageThe page that contains this annotation. Read-only.
Left, Bottom, Right, Top, Width and HeightThe position and size in page space. Right and Top are read-only.
BorderWidthWidth of the border. 0 means no border.
BorderColorColor of the border (must be RgbColor for non-widget annotation).
BorderStyleStyle of the border (Solid, Dashed, etc.)
InvisibleThe annotation is not visible on screen.
LockedWhether the annotation is locked in the viewer application.
PrintThe annotation is printed.
Public methods
FlattenThe page that contains this annotation. Read-only.
AcceptSupports 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.

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
HighlightStyleThe visual effect that is used when the mouse is pressed inside the annotation area.
MouseUpActionsThe 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, InReplyToRespectively, 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.
TextThe text of this mark-up. This text may contain simple formatting tags such as bold.
PopupThe pop-up that displays the text of this mark-up.
OpacityThe opacity of this mark-up (0 means fully transparent, 255 means fully opaque).
Author, CreationDate and SubjectThe 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
IconNameViewer applications have predefined icons for at least the following names: Comment, Key, Note, Help, NewParagraph, Paragraph and Insert.
StateModelState model of this mark-up. Existing state models:

  • Marked: according to this model, a mark-up is marked or not.
  • Review: according to this model, a mark-up has one of the following states: None, Accepted, Rejected, Cancelled or Completed.
  • Migration: according to this model, a mark-up has one of the following states: None, Confirmed or NotConfirmed.
  • None. This mark-up has no state model.
MarkedMeaningful if state model is Marked.
ReviewStateMeaningful if state model is Review.
MigrationStateMeaningful if state model is Migration.

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
OpenWhether the pop-up should initially be displayed open.
MarkupThe 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

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

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

Document, fields, pages and widgets

The following diagram shows the Widget inheritance hierarchy.

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

FieldWidgetRemarks
SignatureFieldSignatureWidgetThe 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.
PushButtonFieldPushButtonWidgetThe 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.).
CheckBoxFieldCheckBoxWidgetThe CheckBoxWidget has an additional appearance to specify the check mark (diamond, check, start, etc.).
RadioButtonFieldRadioButtonWidgetJust 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, Code3of9BarcodeFieldWidgetThese 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
FieldThe field that is associated with this widget. Read-only.
Font, FontSize, TextColor, HorizontalAlignment, VerticalAlignmentProperties that specify how text is displayed.
Invisible, Orientation, BackgroundColorAppearance properties.
PersistencyAllows 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, LostFocusActionsActions to be executed when the widget gets or loses keyboard focus.
MouseDownActions, MouseUpActions, MouseEnterActions, MouseExitActionsActions 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)

Text Field As Created By Code Sample

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

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) DesignerProduces XFA versionRequires Adobe PDF ReaderRequires PDFKit.NET
6.02.06.022.0
7.02.16.022.0
7.0 BETA2.27.052.0
7.12.47.052.0
8.02.58.02.0
8.12.68.13.0
8.1.22.78.23.0
ES 8.22.89.03.0
No public release3.09.1Not 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

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 FalseIsSigned == false;

IsSigned True Verify FalseIsSigned == true; Verify() == false;

IsSigned True Verify True DocumentModifiedAfterSigning False Not TrustedIsSigned == true; Verify() == true;

IsSigned True Verify True DocumentModifiedAfterSigning True Not TrustedIsSigned == true; Verify() == true;

IsSigned True Verify True DocumentModifiedAfterSigning False TrustedIsSigned == true; Verify() == true;

IsSigned True Verify True DocumentModifiedAfterSigning True TrustedIsSigned == 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

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

Page space versus Visual page space

Base class Shape

All shapes inherit directly or indirectly from Shape.

Partial Shapes Class Hierarchy

Partial shapes class hierarchy

ContentShape

ContentShape adds the following properties to specify transformation, blend mode and opacity.

Public properties specific to ContentShape

Public properties
Opacity0 means fully transparent. 255 means fully opaque.
BlendModeDefines how this content shape blends with its background. Default is Inherit (meaning: the same as its parent).
TransformThe 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
TextThe text that is displayed.
MeasuredWidth, MeasuredHeightRead-only properties that reflect the size of the text with the current settings.
Pen, Brush, Font, FontSize, Italic, BoldAppearance properties.
ReadDirectionRead direction (left-to-right or right-to-left).
BoundingBoxThe 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, HeightWidth and Height of the shape collection with respect to the parent coordinate space. This parent is either a ShapeCollection or a Canvas.
VirtualWidth, VirtualHeightThese 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.
ClipClip 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
PenThis Pen is used to stroke this path shape.
BrushThis 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

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
PathsA collection of FreeHandPath objects. You build a freehand shape by adding paths to this collection.
FillRuleDefines 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:

Sample

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:

RectangleShape1

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:

RectangleShape2

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

RectangleShape3

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

RectangleShape4

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:

Image Clipped By ClipShape

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.
Download PDFKit.NET 5.0
We will send you a download link
Why do we ask your email address?
We send tips that speed up your evaluation
We let you know about bug fixes
You can always unsubscribe with one click
We never share your address with a 3rd party
Thank you for your download

We have sent an email with a download link.