Docotic.Pdf 10 migration guide

This guide describes the breaking changes introduced in Docotic.Pdf 10.0 and provides examples showing how to implement previous behavior using the updated API.

The guide also lists obsolete properties and methods and describes the recommended migration paths.

Docotic.Pdf 10 migration guide

If anything in this guide is unclear, you need additional examples, or you encounter a migration scenario not covered here, please contact us.

Why the changes?

Evolving over the years, the Docotic.Pdf API grew into a very large one. But some of the conceptually similar features were implemented differently. There were cases of design decisions that can be improved. Some data structures promised too much and/or didn't map cleanly to PDF structures.

In the version 10.0 we decided to improve in all these areas.

What have changed?

Most changes in Docotic.Pdf 10 affect collections and the APIs that expose them.

Docotic.Pdf collections no longer implement ICollection<T> or ICollection. Several new collection types have been introduced, and some existing collections have been marked obsolete.

Classes that expose collections still provide access to their items, but the available methods and properties have changed.

Collections and object identity

API members that previously returned PdfCollection<T>, PdfList<T>, or types derived from them now return IEnumerable<T> or other types that implement IEnumerable<T>.

In some cases, the original members are marked obsolete and replaced with new members that return IEnumerable<T> or a type that implements this interface.

When a return type changes from PdfCollection<T> to IEnumerable<T>, collection-specific members such as Count, the indexer, and GetAt(int) are no longer available. Use LINQ methods to perform the corresponding operations:

  • PdfCollection<T>.Count -> IEnumerable<T>.Count()
  • PdfCollection<T>.this[int] -> IEnumerable<T>.ElementAt(int) or IEnumerable<T>.ToArray()[int]

The Count() and ElementAt(int) methods enumerate the sequence to obtain the requested information. If you need to access multiple elements by index, consider materializing the sequence into an array first.

On runtimes earlier than .NET 9, using ToList() instead of ToArray() may result in lower memory consumption.

Changes from PdfList<T> or a type derived from it to a type implementing IEnumerable<T> preserve the Count and indexing members, as well as methods for adding and removing items.

In all cases, the GetAt(int) method is removed. Use the indexer instead.

Object identity changes

Collections now create high-level objects from the underlying PDF structures each time an item is accessed or a collection is enumerated. In previous versions, these objects were cached.

As a result, accessing the same collection item multiple times does not guarantee that the same object instance is returned. Avoid relying on reference equality or object identity when working with objects obtained from collections.

This change also affects collection operations that rely on object comparison. The Contains(T) and IndexOf(T) methods are no longer available. For list-based collections, the Remove(T) method is also no longer available.

Update code that uses these methods to rely on positional logic instead.

Replaced and obsolete members

Previous versions of Docotic.Pdf exposed collections through members typed as ReadOnlyCollection<T>, PdfCollection<T>, or ICollection<T>. In version 10, many of these members are obsolete, while others have been replaced with new members that use more appropriate types.

Depending on the scenario, the new members return T[], IEnumerable<T>, or a specialized collection type. Review the migration instructions to identify the appropriate replacements.

Improved APIs

Docotic.Pdf 10 also introduces improved APIs for:

API changes by class

This section describes API changes in Docotic.Pdf 10, organized by class name in alphabetical order. Each subsection outlines changes to methods and properties and provides guidance on how to update your code to migrate to the new version.

Pdf3dStream

The Views property is now of type Pdf3dViewCollection instead of PdfList<Pdf3dView?>.

See Collections and object identity for migration guidance.

PdfAction

The behavior of PdfActionCollection, the return type of the NextActions property, has changed.

PdfActionCollection

PdfActionCollection was refactored from a concrete list into a lightweight enumerable and no longer derives from PdfList<PdfAction?>.

See Collections and object identity for migration guidance.

PdfArtifact

The return type of GetAttachmentEdges() is now string[]? instead of string[].

The method now returns null when no attachment edges are specified in the underlying PDF structure. In previous versions, it returned an empty array in such cases.

PdfCanvas

The GetTextData() method now returns IEnumerable<PdfTextData> instead of PdfCollection<PdfTextData>.

See Collections and object identity for migration guidance.

PdfClipRegion

The PdfCollection<PdfPath> IntersectedPaths property is now obsolete. Use the new IEnumerable<PdfPath> GetIntersectedPaths() method instead.

See Collections and object identity for migration guidance.

PdfComboBox

The following members have changed:

PdfDeviceNColorSpace

The ReadOnlyCollection<string> Names property is now obsolete. Use the new string[] GetNames() method instead.

PdfDocument

PdfDocument serves as the central entry point for working with many different aspects of a PDF file. The class is affected by several migration-related changes, including updates to the collection implementation and revisions to the APIs for bookmarks and layers.

The following members are affected:

PdfDocument.AddPage method

The AddPage() method is now obsolete. Use the Pages collection and its Add() method instead.

PdfDocument.AssociatedFiles property

The AssociatedFiles property is now of type PdfFileSpecificationCollection instead of PdfList<PdfFileSpecification?>.

See Collections and object identity for migration guidance.

PdfDocument.CopyPages methods

All CopyPages methods are now obsolete. Use the corrseponding methods from the Pages collection instead:

  • CopyPages(int, int) -> Pages.CopyRange(int, int)
  • CopyPages(int, int, PdfConfigurationOptions) -> Pages.CopyRange(int, int, PdfConfigurationOptions)
  • CopyPages(int[]) -> Pages.Copy(int[])

The CopyPages(PdfPage[]) method has no direct replacement. Update any code that identifies pages by object reference to use page positions in the collection instead.

PdfDocument.CreateLayer methods

The CreateLayer methods are obsolete. Use OptionalContent.Layers.Add to create a layer and add it to the document's layer collection. Access the collection through the OptionalContent property.

Replace the obsolete methods as follows:

  • CreateLayer(string) -> use OptionalContent.Layers.Add(string)
  • CreateLayer(string, bool, params PdfLayerIntent[]) -> use OptionalContent.Layers.Add(string, bool, params string[])

The replacement for the second overload accepts intent names as strings rather than PdfLayerIntent values. See PdfLayerIntent for details about migrating layer intents.

PdfDocument.ExtractPages methods

All ExtractPages methods are now obsolete. Use the corrseponding methods from the Pages collection instead:

  • ExtractPages(int, int) -> Pages.ExtractRange(int, int)
  • ExtractPages(int, int, PdfConfigurationOptions) -> Pages.ExtractRange(int, int, PdfConfigurationOptions)
  • ExtractPages(int[]) -> Pages.Extract(int[])

The ExtractPages(PdfPage[]) method has no direct replacement. Update any code that identifies pages by object reference to use page positions in the collection instead.

PdfDocument.GetPage method

The GetPage(int) method is now obsolete. Use the Pages collection and its indexer instead.

PdfDocument.IndexOf method

The IndexOf(PdfPage) method is now obsolete. Use the Pages collection and positional access instead.

PdfDocument.Insert method

The Insert(int) method is now obsolete. Use the Pages collection and its Insert(int) method instead.

PdfDocument.Layers property

The Layers property is now obsolete. Use OptionalContent.Layers to access the document's layer collection. This changes the collection type from PdfCollection<PdfLayer?> to PdfLayerCollection, which implements IEnumerable<PdfLayer?>.

See Collections and object identity for migration guidance.

PdfDocument.MovePage method

The MovePage(int, int) method is now obsolete. Use the Pages collection and its Move(int, int) method instead.

PdfDocument.MovePages methods

All MovePages methods are now obsolete. Use the corrseponding methods from the Pages collection instead:

  • MovePages(int, int, int) -> Pages.MoveRange(int, int, int)
  • MovePages(int[], int) -> Pages.Move(int[], int)

PdfDocument.OpenImage methods

The OpenImage methods themselves have not changed. However, there are behavioral changes in PdfImageFrames, the type returned by these methods.

PdfDocument.OutlineRoot property

The PdfOutlineItem OutlineRoot property is now obsolete and has been replaced by the PdfOutlineItemCollection Outlines property instead.

This change clarifies the difference between collection of outline items and an individual outline item. The API of PdfOutlineItemCollection more accurately reflects the underlying PDF structure and exposes only operations that are meaningful and efficient for a bookmark collection.

Behavioral changes

With OutlineRoot, the root outline item acted as a synthetic container:

  • OutlineRoot.Parent was always null.
  • Items in OutlineRoot.Children had OutlineRoot as their parent. With Outlines, there is no synthetic root item. All top-level bookmarks have null as their parent.
Members without replacements

The following PdfOutlineItem members do not apply to a bookmark collection and therefore have no equivalent in PdfOutlineItemCollection:

  • Parent
  • Page
  • PageIndex
  • Title
  • Action
  • Open
  • Color
  • Bold
  • Italic
Member mapping

The OutlineRoot.Children is now represented by Outlines itself. Here's how to enumerate top-level bookmarks:

// Before v10
foreach (var item in pdf.OutlineRoot.Children)
    Console.WriteLine(item.Title);

// v10 and later
foreach (var item in pdf.Outlines)
    Console.WriteLine(item.Title);

There is no direct replacement for OutlineRoot.ChildCount, but you can use LINQ instead: Outlines.Count().

OutlineRoot.RemoveAllChildren() maps to Outlines.Clear(), and OutlineRoot.RemoveChild(PdfOutlineItem) maps to Outlines.Remove(PdfOutlineItem).

The Outlines collection does not support index-based operations. For the reasons behind this change and migration alternatives, see the PdfOutlineItem.Children section.

Adding and inserting bookmarks

The PdfOutlineItemCollection.Add(string) method replaces all OutlineRoot.AddChild methods. Create a new bookmark with the desired title, then configure the returned PdfOutlineItem by setting its action, color, destination, and other properties as needed.

To replace the OutlineRoot.InsertChild methods, use PdfOutlineItemCollection.InsertBefore(PdfOutlineItem, string) or PdfOutlineItemCollection.InsertAfter(PdfOutlineItem, string). The approach is similar to Add: insert the new bookmark first, then configure the returned item.

One important difference is that the new API uses a reference item rather than an index. Instead of specifying the position of an existing bookmark, you specify the bookmark before or after which the new bookmark should be inserted.

PdfDocument.PageCount property

The PageCount property is now obsolete. Use the Pages collection and its Count property instead.

PdfDocument.PageLabels property

The PageLabels property is now of type PdfPageLabelMap instead of PdfPageLabels. This better reflects how page labels are stored internally in a PDF document. The new type exposes a similar set of methods and properties, but some members behave differently.

Member mapping

The following table shows how members of PdfPageLabels map to members of PdfPageLabelMap:

Mapping of old members to new ones:

  • PdfPageLabels PageLabels -> PdfPageLabelMap PageLabels
  • PdfPageLabels.AddRange overloads -> PdfPageLabelMap.Add, except for overloads that accept a lastPage parameter, which have no direct replacement
  • PdfPageLabels.Count -> use LINQ if needed: PageLabels.Count()

The following PdfPageLabels members are no longer supported:

The enumerator type of the PageLabels collection has changed from

IEnumerator<PdfPageLabelingRange> GetEnumerator()

to

IEnumerator<PdfPageLabelEntry> GetEnumerator()

This allows you to iterate over entries that pair the zero-based index of the first page in a labeling range with the labeling characteristics for that range.

Object identity-based operations

The PageLabels collection creates a new PdfPageLabelEntry instance, containing a new PdfPageLabel instance, each time the indexer is accessed or the collection is enumerated. Because each lookup returns different object instances, operations that depend on value or reference equality are no longer supported.

Update any code that uses Contains(PdfPageLabelingRange), IndexOf(PdfPageLabelingRange), or Remove(PdfPageLabelingRange) to use the indexer and/or enumeration instead.

Index-based operations

PdfPageLabelMap does not provide index-based operations. The indexer and methods that accept int parameters no longer treat those integers as positions within the collection.

For example, the indexer PageLabels[int] retains the same signature, but its semantics have changed. Previously, the parameter represented the position of a page-labeling range within the collection. Now, the parameter represents the index of the first page in a page-labeling range. The page index acts as a key, and the collection returns the corresponding labeling range.

There is no direct replacement for the PdfPageLabels.RemoveAt(int) method. Instead, enumerate the map, locate the entry at the desired position, retrieve its first-page index, and remove the corresponding labeling range by calling PdfPageLabelMap.Remove(int):

PdfPageLabelMap map = pdf.PageLabels;
var entry = map.ElementAt(index);
map.Remove(entry.FirstPageIndex);
AddRange methods

All PdfPageLabels.AddRange methods have been replaced by PdfPageLabelMap.Add methods. However, there is no direct replacement for AddRange overloads that accept a lastPage parameter.

These overloads allowed you to specify only ranges with non-default labels explicitly. Any ranges that used the default page labels (decimal Arabic numbers matching page indices) were added implicitly.

For example, in a 10-page document, the following code creates three labeling ranges:

  • lowercase Roman numerals for page indices 0..3
  • an implicitly added range with decimal Arabic numbering for page indices 4..6
  • decimal Arabic numbering with an "A-" prefix starting at page index 7
// Before v10
pdf.PageLabels.AddRange(7, PdfPageNumberingStyle.DecimalArabic, "A-", 8);
pdf.PageLabels.AddRange(0, 3, PdfPageNumberingStyle.LowercaseRoman);

Unlike PdfPageLabels, PdfPageLabelMap never adds labeling ranges implicitly. You must explicitly add every range. The preceding example becomes:

// v10 and later
pdf.PageLabels.Add(7, PdfPageNumberingStyle.DecimalArabic, "A-", 8);
pdf.PageLabels.Add(0, PdfPageNumberingStyle.LowercaseRoman);
pdf.PageLabels.Add(4, PdfPageNumberingStyle.DecimalArabic);

PdfPageLabels also implicitly added a labeling range starting at page index 0 if no such range had been added explicitly. PdfPageLabelMap does not do this. When configuring page labels, ensure that the collection either:

  • contains no labeling ranges, or
  • contains a labeling range that starts at page index 0.

PdfDocument.Pages property

The Pages property is now of type PdfPageCollection instead of a PdfCollection<PdfPage>.

See Collections and object identity for migration guidance.

The sequence returned by the enumerator of the new collection may include null entries when a page cannot be constructed from the underlying PDF data. In earlier versions, such pages appeared as empty PdfPage instances.

In version 10, the new PdfPageCollection type also replaces several page-related members previously exposed directly on PdfDocument. These members now reside on the Pages collection.

Mapping of old page-related members to new ones:

  • AddPage() -> Pages.Add()
  • CopyPages(PdfPage[]) -> not supported; use positional logic instead
  • CopyPages(int, int) -> Pages.CopyRange(int, int)
  • CopyPages(int, int, PdfConfigurationOptions) -> Pages.CopyRange(int, int, PdfConfigurationOptions)
  • CopyPages(int[]) -> Pages.Copy(int[])
  • ExtractPages(PdfPage[]) -> not supported; use positional logic instead
  • ExtractPages(int, int) -> Pages.ExtractRange(int, int)
  • ExtractPages(int, int, PdfConfigurationOptions) -> Pages.ExtractRange(int, int, PdfConfigurationOptions)
  • ExtractPages(int[]) -> Pages.Extract(int[])
  • GetPage(int) -> Pages[int]
  • IndexOf(PdfPage) -> not supported; use positional logic instead
  • InsertPage(int) -> Pages.Insert(int)
  • MovePage(int, int) -> Pages.Move(int, int)
  • MovePages(int, int, int) -> Pages.MoveRange(int, int, int)
  • MovePages(int[], int) -> Pages.Move(int[], int)
  • PageCount -> Pages.Count
  • RemovePage(PdfPage) -> not supported; use positional logic instead
  • RemovePage(int) -> Pages.RemoveAt(int)
  • RemovePages(PdfPage[]) -> not supported; use positional logic instead
  • RemovePages(int) -> Pages.RemoveFrom(int)
  • RemovePages(int, int) -> Pages.RemoveRange(int, int)
  • RemovePages(int[]) -> Pages.RemoveAt(int[])
  • SwapPages(int, int) -> Pages.Swap(int, int)

PdfDocument.RemovePage methods

The RemovePage(int) method is now obsolete. Use the Pages collection and its RemoveAt(int) method instead.

The RemovePage(PdfPage) method is also obsolete. There is no direct replacement for this overload. Update any code that identifies pages by object reference to use page positions in the collection instead.

PdfDocument.RemovePages methods

All RemovePages methods are now obsolete. Use the corrseponding methods from the Pages collection instead:

  • RemovePages(int) -> Pages.RemoveFrom(int)
  • RemovePages(int, int) -> Pages.RemoveRange(int, int)
  • RemovePages(int[]) -> Pages.RemoveAt(int[])

The RemovePages(PdfPage[]) method has no direct replacement. Update any code that identifies pages by object reference to use page positions in the collection instead.

PdfDocument.RemoveUnusedFontGlyphs methods

All RemoveUnusedFontGlyphs overloads now return PdfFont[] instead of PdfCollection<PdfFont>.

The following table shows how to migrate code that uses the returned collection:

  • PdfCollection<PdfFont>.Count -> fonts.Length
  • PdfCollection<PdfFont>.GetAt(int) -> array indexer (fonts[index])
  • PdfCollection<PdfFont>.Contains(PdfFont) -> Array.Exists(fonts, f => f == fontToFind)
  • PdfCollection<PdfFont>.IndexOf(PdfFont) -> Array.IndexOf(fonts, fontToFind)

Note that each call to a RemoveUnusedFontGlyphs method may create new PdfFont instances. Therefore, the returned array can contain new PdfFont instances instead of the instances referenced before the call, even when they refer to the same font.

PdfDocument.RemoveWidget method

The RemoveWidget(PdfWidget) method is now obsolete. Use the PdfWidget.Remove method instead.

Alternatively, if the widget belongs to a page, find it in the page's PdfPage.Widgets collection and remove it by calling the RemoveAt(int) method.

PdfDocument.SharedAttachments property

The SharedAttachments property is now of type PdfFileSpecificationMap instead of PdfFileSpecificationCollection. This better reflect the way the collection internally stored in PDF. The new type exposes a similar set of methods and properties, but several members now behave differently.

Mapping of old members to new ones:

  • PdfFileSpecificationCollection SharedAttachments -> PdfFileSpecificationMap SharedAttachments
  • Get(string) -> removed; use indexer access instead
  • Add(PdfFileSpecification) -> Add(string, PdfFileSpecification); see note below
  • Remove(PdfFileSpecification) -> not supported; see note below

The enumerator type for the SharedAttachments collection changed from IEnumerator<PdfFileSpecification> GetEnumerator() to IEnumerator<PdfFileSpecificationEntry> GetEnumerator(), allowing iteration over key/value pairs rather than values only.

The indexer SharedAttachments[string] keeps the same signature, but its operational semantics have changed. Previously, it searched the entire collection for an item whose PdfFileSpecification.Specification matched the provided string. Now it performs a direct lookup by key in the underlying PDF structure. It does not parse values to locate a matching pair. Because of this change, existing code may no longer obtain the expected value from the indexer if the key and the entry's PdfFileSpecification.Specification do not match.

The SharedAttachments.Add method now requires you to provide the key explicitly. Previously, the key was derived from the provided value's PdfFileSpecification.Specification, with the -1 suffix appended repeatedly whenever that key was already in use.

The SharedAttachments.Add method also overwrites any existing entry with the same key. Use SharedAttachments.Contains(string) to check whether a key already exists.

The SharedAttachments collection creates a new PdfFileSpecificationEntry with a new PdfFileSpecification instance each time an indexer is accessed or the collection is iterated. Because of this behavior, operations that rely on value comparison are no longer supported. Update any code that uses Remove(PdfFileSpecification) to rely on positional logic instead.

PdfDocument.SharedScripts property

The SharedScripts property is now of type PdfJavaScriptActionMap instead of PdfJavaScriptCollection. This change better reflects how document-level JavaScript actions are stored in PDF documents.

The new type provides similar functionality but no longer exposes index-based operations.

Member mapping

The following table shows how members of PdfJavaScriptCollection map to members of PdfJavaScriptActionMap:

  • PdfJavaScriptCollection SharedScripts -> PdfJavaScriptActionMap SharedScripts
  • Add(PdfJavaScriptAction) -> Add(string, PdfJavaScriptAction); see Add method
  • Count -> use LINQ if needed: Count()
  • Get(string) -> use the indexer instead: this[string]

The following PdfJavaScriptCollection members are no longer supported:

The enumerator type for the SharedScripts collection changed from

IEnumerator<PdfJavaScriptAction> GetEnumerator()

to

IEnumerator<PdfJavaScriptActionEntry> GetEnumerator()

This change allows enumeration of entries that associate script names with JavaScript actions.

Object identity-based operations

PdfJavaScriptActionMap creates a new PdfJavaScriptActionEntry and a new PdfJavaScriptAction instance each time an entry is retrieved through the indexer or during enumeration. Because of this behavior, operations that depend on object identity or instance equality are no longer supported.

Update any code that uses Contains(PdfJavaScriptAction), IndexOf(PdfJavaScriptAction), or Remove(PdfJavaScriptAction) to use action names, the indexer, and/or enumeration instead.

Index-based operations

PdfJavaScriptActionMap does not provide index-based operations. The recommended approach is to enumerate the collection rather than rely on positional access.

If indexed access is required during migration, you can achieve equivalent behavior using LINQ:

var action = SharedScripts.ElementAt(index).Action;

For scenarios that require multiple indexed lookups, consider materializing the collection first:

var entries = SharedScripts.ToArray();
var action = entries[index].Action;

There is no direct replacement for PdfJavaScriptCollection.RemoveAt(int). Instead, enumerate the map, locate the entry at the desired position, obtain its name, and remove the corresponding action using PdfJavaScriptActionMap.Remove(string):

PdfJavaScriptActionMap map = pdf.SharedScripts;
var entry = map.ElementAt(index);
map.Remove(entry.Name);
Add method

The PdfJavaScriptActionMap.Add method requires an explicit action name. Previously, PdfJavaScriptCollection.Add generated names automatically using the format JSn, where n started at 0 and increased for each added action.

You can use any string as the name. The name has no predefined semantic meaning; it is used only to identify and organize actions within the underlying PDF structure.

PdfDocument.SharedViews property

The SharedViews property now returns a PdfDocumentViewMap instead of a PdfDocumentViewCollection.

The new type provides similar functionality but exposes its items as PdfDocumentViewEntry instances rather than KeyValuePair<string, PdfDocumentView> values.

Member mapping

The following table shows how members of PdfDocumentViewCollection map to members of PdfDocumentViewMap:

  • PdfDocumentViewCollection SharedViews -> PdfDocumentViewMap SharedViews
  • Get(string) -> use the indexer instead: this[string]

The enumerator type for the SharedViews collection changed from

IEnumerator<KeyValuePair<string, PdfDocumentView>> GetEnumerator()

to

IEnumerator<PdfDocumentViewEntry> GetEnumerator()
Add method

The behavior of the Add method has changed.

PdfDocumentViewMap.Add replaces any existing entry with the same name. Previously, PdfDocumentViewCollection.Add threw an ArgumentException if an entry with the specified name already existed.

If you need to preserve the previous behavior, use the Contains method to check whether an entry with the specified name already exists before calling Add.

if (pdf.SharedViews.Contains(name))
    throw new ArgumentException();

pdf.SharedViews.Add(name, view);

PdfDocument.SwapPages method

The SwapPages(int, int) method is now obsolete. Use the Pages collection and its Swap(int, int) method instead.

PdfExponentialFunction

The PdfCollection<double> C0 and PdfCollection<double> C1 properties are now obsolete. Use the double[]? GetC0() and double[]? GetC1() methods instead. These methods returns null when the corresponding value is not explicitly specified in the PDF. In that case, use the default value described in the documentation.

The PdfCollection<double> Domain property is now obsolete. Use the double[] GetDomain() method instead.

The PdfCollection<double>? Range property is now obsolete. Use the double[]? GetRange() method instead.

PdfFileSpecificationCollection

The collection now implements IEnumerable<PdfFileSpecification?> instead of IEnumerable<PdfFileSpecification>. This allows the collection to be enumerated without exceptions even when some items cannot be created from their underlying PDF structures.

The collection introduces new members: Count, this[int], and RemoveAt(int), which enable positional operations.

The string indexer this[string] has been removed. Use the Get(string) method instead.

The collection may create a new PdfFileSpecification instance each time an index is accessed or Get(string) is called. Because of this behavior, operations that rely on value comparison are intentionally not supported. Update any code that uses Remove(PdfFileSpecification) to rely on positional logic instead.

PdfHideAction

The class no longer inherits collection-related members from PdfControlAction. Instead, it now derives directly from PdfAction and exposes its widget collection through the
PdfActionWidgetCollection Widgets property. In addition, you can now associate widgets with the action, not only controls.

Mapping of old members to new ones:

  • PdfCollection<PdfControl> Controls is now PdfActionWidgetCollection Widgets
  • ControlCount was removed. Use the Count property of the Widgets collection.
  • GetControl(int) was removed. Use the indexer of the Widgets collection.
  • AddControl(PdfControl) was removed. Use Widgets.Add(PdfWidget).
  • GetAt(int) was removed. Use indexer access instead.

The Widgets collection may construct a new PdfWidget instance each time an index is accessed. Because of this, operations that rely on value comparison are not supported. Update any code that uses Contains or IndexOf to rely on positional logic instead.

PdfImageFrames

PdfImageFrames now implements IEnumerable<PdfImageFrame> instead of PdfCollection<PdfImageFrame>. It still provides a Count property and an indexer.

See Collections and object identity for migration guidance.

PdfInkAnnotation

The PdfInkAnnotation class originally provided methods and properties for reading and updating the paths of an ink annotation. However, it did not support the modern path-construction model introduced in PDF 2.0.

Version 10 adds support for this newer model and updates the existing API so the class can work with both the earlier and PDF 2.0 approaches.

To define an ink annotation's path, use either the PdfInkAnnotation.InkList or PdfInkAnnotation.Path properties. Both expose a PdfInkPathSegmentCollection, but the meaning of the collection's items differs between the two models. See the documentation for each property for details about how its collection items are interpreted.

The PathCount property and the GetPath, AddPath, and RemovePathAt methods are now obsolete. Use the InkList collection and its corresponding methods and properties instead.

Mapping of old members to new ones:

  • PathCount -> use InkList.Count
  • GetPath(int) -> use the InkList indexer (InkList[int])
  • AddPath(IEnumerable<PdfPoint>) -> use InkList.Add(PdfPoint[])
  • RemovePathAt(int) -> use InkList.RemoveAt(int)

PdfLayer

The PdfLayerIntent[] GetIntents() method is now obsolete. Use string[]? GetIntentNames() instead.

Unlike GetIntents(), the new method returns null when the corresponding value is not explicitly specified in the PDF. In this case, use the default value described in the documentation.

In addition to typical intent names, the new method can also return custom, developer-specific intent names.

PdfLayerIntent

The PdfLayerIntent type is now obsolete. Layer intents are now represented by intent names (string[]), and all methods and properties that use PdfLayerIntent should no longer be used.

Replace PdfLayerIntent values with their corresponding intent names. You can obtain the name of an existing value by calling ToString().

PdfListBox

The following members have changed:

Additionally, the PdfCollection<PdfListItem> SelectedItems property is now obsolete. Use the IEnumerable<PdfListItem> GetSelectedItems() method instead.

PdfMarkedContent

The GetObjects() method now returns IEnumerable<PdfPageObject> instead of PdfCollection<PdfPageObject>.

See Collections and object identity for migration guidance.

PdfOutlineItem

The PdfOutlineItem API has been revised to simplify access to child bookmarks and improve collection handling. Operations on an outline item are now more clearly separated from operations on its child bookmark collection.

The following members are affected:

PdfOutlineItem.Action property

Version 10 refines the role of Action and introduces the new View property. Action now exposes only explicit PDF actions, while View provides access to document view destinations.

Changes when reading PDFs

In versions prior to 10, when an outline item specified a document view, the Action property returned a synthesized PdfGoToAction representing that view.

Starting with version 10, Action returns only actions that are explicitly defined in the PDF. If an outline item specifies a document view instead of an action, Action returns null.

Use the new View property to access the document view associated with an outline item.

Changes when writing PDFs

In previous versions, assigning a PdfGoToAction to Action caused Docotic.Pdf to save the destination as a document view (PdfDocumentView) rather than as a Go-To action. This optimization reduced the PDF size while preserving the same behavior: activating the outline item navigated to a location within the current document.

Starting with version 10, Docotic.Pdf no longer performs this conversion. The value assigned to Action is now written to the PDF unchanged.

Replacing PdfGoToAction with View

If an outline item should navigate to a location within the same document, use the new View property instead of assigning a PdfGoToAction to Action. This produces the same compact PDF representation as in previous versions.

In the examples below, pdf is a PdfDocument and item is a PdfOutlineItem.

// Before v10
item.Action = pdf.CreateGoToPageAction(pageIndex, 0);
 
// v10 and later
item.View = pdf.CreateView(pageIndex);

PdfOutlineItem.AddChild methods

All AddChild methods are now obsolete. To add child outline items, use the Children collection and call the PdfOutlineItemCollection.Add method instead.

A single Add method replaces all obsolete AddChild overloads. It accepts only the title of the outline item to create and returns the newly created PdfOutlineItem. You can then configure additional properties, such as the action or destination view, on the returned instance.

The following example shows how to replace the overload that accepts a PdfAction:

// Before v10
var child = outlineItem.AddChild(title, action);

// v10 and later
var child = outlineItem.Children.Add(title);
child.Action = action;

The following example shows how to replace the overload that accepts a PdfDocumentView:

// Before v10
var child = outlineItem.AddChild(title, view);

// v10 and later
var child = outlineItem.Children.Add(title);
child.View = view;

The following example shows how to replace the overload that accepts a PdfPage:

// Before v10
var child = outlineItem.AddChild(title, page);

// v10 and later
var child = outlineItem.Children.Add(title);
child.View = doc.CreateView(page);

The following example shows how to replace the overload that accepts a page index:

// Before v10
var child = outlineItem.AddChild(title, pageIndex);

// v10 and later
var child = outlineItem.Children.Add(title);
child.View = doc.CreateView(pageIndex);

PdfOutlineItem.Children property

The Children property is now of type PdfOutlineItemCollection instead of a PdfCollection<PdfOutlineItem>.

As a result, several index-based operations previously provided by PdfCollection<PdfOutlineItem> are no longer available, including Count, the indexer, and GetAt(int). This change is intentional. In PDF documents, sibling bookmarks are stored as a doubly linked list rather than an array. Therefore, operations based on item positions require traversing the list and cannot be implemented efficiently.

See Collections and object identity for migration guidance.

In most scenarios, it is preferable to iterate the collection directly rather than rely on item indexes. This avoids unnecessary traversal and more accurately reflects how bookmarks are represented internally in a PDF document.

If an index is required during iteration, .NET 9 and later provide the Index() extension method:

foreach (var (index, child) in outlineItem.Children.Index())
{
    if (index % 2 == 0)
    {
        // Process every even-indexed child item.
    }
}

For projects targeting earlier .NET versions, use the indexed overload of Select:

foreach (var item in outlineItem.Children.Select((child, index) => new { child, index }))
{
    if (item.index % 2 == 0)
    {
        // Process every even-indexed child item.
    }
}

Please note that PdfOutlineItemCollection provides methods for modifying the collection, including adding, inserting, removing, and clearing child bookmarks.

PdfOutlineItem.ChildCount property

The ChildCount property is now obsolete. Avoid depending on the number of child items whenever possible. Iterating through child outline items is the recommended approach in most scenarios.

// Before v10
for (int i = 0; i < outlineItem.ChildCount; i++)
{
    var childOutlineItem = outlineItem.GetChild(i);
    // ...
}
 
// v10 and later
foreach (var childOutlineItem in outlineItem.Children)
{
    // ...
}

If you need the number of child items, use LINQ:

int childCount = outlineItem.Children.Count();

Note that Children.Count() is an O(n) operation because it enumerates all child items.

If you only need to check whether the collection contains any items, use Children.Any() instead:

bool hasChildren = outlineItem.Children.Any();

PdfOutlineItem.GetChild method

The GetChild method is now obsolete. The recommended approach is to use the Children collection and iterate over its items rather than relying on child indexes.

If you need indexed access for migration purposes, you can obtain the same behavior using LINQ:

var child = item.Children.ElementAt(index);

For scenarios that require multiple index-based lookups, consider materializing the collection first:

var children = item.Children.ToArray();
var child = children[index];

However, iterating over the Children collection remains the preferred approach, as it avoids unnecessary materialization and aligns with the intended usage of the API.

The method no longer guarantees that repeated calls for the same child return the same object instance. As a result, object identity and reference equality are not guaranteed.

PdfOutlineItem.IndexOfChild methods

All IndexOfChild methods are now obsolete. Instead of relying on child indexes, use the Children collection and iterate through its items as needed.

PdfOutlineItem.InsertChild methods

All InsertChild methods are now obsolete. To insert child outline items, use the Children collection and call either PdfOutlineItemCollection.InsertBefore or PdfOutlineItemCollection.InsertAfter.

When possible, it is recommended to replace index-based insertions with relative positioning. Instead of inserting an item at a specific index, insert it before or after an existing outline item. To append a child item to the end of the collection, use PdfOutlineItemCollection.Add.

For migration purposes, you can use code similar to the following:

// Before v10
var child = outlineItem.InsertChild(index, title);

// v10 and later
if (index >= outlineItem.Children.Count())
    return outlineItem.Children.Add(title);

var existingChild = outlineItem.Children.ElementAt(index);
return outlineItem.Children.InsertBefore(existingChild, title);

Note that this migration approach requires additional traversals of the Children collection to locate the target item.

Both InsertBefore and InsertAfter replace all obsolete InsertChild overloads. These methods accept only the title of the outline item to create and return the new PdfOutlineItem. You can then configure additional properties, such as the action or destination view, on the returned item.

See the AddChild methods section for examples of configuring actions and destination views on inserted outline items.

PdfOutlineItem.Page property

The Page property is now obsolete. Its value was derived from the Action and View properties.

Retrieving a page from a view (see PdfDocumentView.Page) currently requires enumerating all document pages and can be slow for large documents.

If you need to determine the destination page for an outline item, you can use the following method (exception handling omitted for brevity):

public static PdfPage? GetDestinationPage(PdfDocument pdf, PdfOutlineItem item)
{
    var view = item.View;
    if (view is null)
    {
        var action = item.Action;
        if (action is PdfGoToAction gotoAction)
            view = gotoAction.GetView().View;
    }

    if (view is null)
        return null;

    var page = view.Page;
    if (page != null)
        return page;

    return pdf.Pages[view.PageIndex];
}

PdfOutlineItem.PageIndex property

The PageIndex property is now obsolete. Its value was derived from the Action and View properties.

Retrieving a page from a view (see PdfDocumentView.Page) currently requires enumerating all document pages and can be slow for large documents. Determining the index of the resolved page requires an additional traversal of the page collection.

If you need to determine the destination page index for an outline item, you can use the following method (exception handling omitted for brevity):

private static int GetDestinationPageIndex(PdfDocument doc, PdfOutlineItem item)
{
    var view = item.View;
    if (view is null)
    {
        var action = item.Action;
        if (action is PdfRemoteGoToAction remoteGotoAction)
            view = remoteGotoAction.GetView().View;
        else if (action is PdfGoToAction gotoAction)
            view = gotoAction.GetView().View;
    }

    if (view is null)
        return -1;

    if (view.PageIndex >= 0)
        return view.PageIndex;

    var viewPageDictString = view.Page?.Dictionary.ToString();
    if (viewPageDictString != null)
    {
        for (int i = 0; i < doc.Pages.Count; i++)
        {
            var pageDictString = doc.Pages[i].Dictionary.ToString();
            if (viewPageDictString == pageDictString)
                return i;
        }
    }

    return -1;
}

PdfOutlineItem.Parent property

In previous versions, the Parent property returned the same cached PdfOutlineItem instance. Starting with version 10, each call creates a new PdfOutlineItem object from the underlying PDF structure.

Update any code that relies on reference equality or assumes that repeated calls to Parent return the same object instance.

PdfOutlineItem.RemoveAllChildren method

The RemoveAllChildren method is now obsolete. To remove all child items, use the Children collection and call its Clear method instead (PdfOutlineItemCollection.Clear).

PdfOutlineItem.RemoveChild method

The RemoveChild method is now obsolete. To remove a child item, use the Children collection and call its Remove method instead.

PdfOutlineItem.RemoveChildAt method

The RemoveChildAt method is now obsolete. Child items should no longer be accessed by index. Instead, locate the child item in the Children collection and remove it using the collection's Remove method.

For migration purposes, replace calls to RemoveChildAt with:

var child = outlineItem.Children.ElementAt(index);
outlineItem.Children.Remove(child);

Note that this replacement requires an additional traversal of the Children collection to locate the item before it can be removed.

PdfPage

The PdfPage class exposes many collections through its methods and properties, and therefore it is significantly affected by the updates to the collection implementations. A large number of its collection-related members have been revised to align with the new unified collection model.

The following members are affected:

PdfPage.AssociatedFiles property

The property now returns a PdfFileSpecificationCollection instead of PdfList<PdfFileSpecification?>.

See Collections and object identity for migration guidance.

PdfPage.ControlCount property

The ControlCount property is now obsolete. Use the GetControls() method together with the LINQ Count() method to obtain the number of controls associated with a page.

PdfPage.Controls property

The Controls property is now obsolete. Instead of accessing a PdfCollection<PdfControl> through this property, use the new GetControls() method to retrieve the controls associated with a page.

PdfPage.GetChars method

The GetChars method now returns IEnumerable<PdfTextData> instead of PdfCollection<PdfTextData>.

See Collections and object identity for migration guidance.

PdfPage.GetControl method

The GetControl method is now obsolete. The recommended approach is to use GetControls() and iterate through the returned controls instead of relying on control indexes.

If you need indexed access for migration purposes, you can achieve equivalent behavior using LINQ:

var control = page.GetControls().ElementAt(index);

For scenarios that require multiple index-based lookups, consider materializing the collection first:

var controls = page.GetControls().ToArray();
var control = controls[index];

However, iterating over the controls returned by GetControls() remains the preferred approach because it avoids unnecessary materialization and aligns with the intended usage of the API.

The method also no longer guarantees that repeated requests for the same control return the same object instance. Consequently, object identity and reference equality should not be relied upon.

PdfPage.GetObjects methods

All GetObjects() overloads now return IEnumerable<PdfTextData> instead of PdfCollection<PdfTextData>.

See Collections and object identity for migration guidance.

PdfPage.GetPaintedImages method

The GetPaintedImages method now returns IEnumerable<PdfPaintedImage> instead of PdfCollection<PdfPaintedImage>.

See Collections and object identity for migration guidance.

PdfPage.GetSortedControls method

The GetSortedControls method is now obsolete. Use the GetControls method and sort the returned controls as needed.

To reproduce the same behavior as GetSortedControls, use the following LINQ query:

var sortedControls = page.GetControls()
    .OrderBy(w => w.BoundingBox.Top)
    .ThenBy(w => w.BoundingBox.Left)
    .ToArray();

The query sorts controls by their position on the page, from top to bottom and, within the same row, from left to right.

PdfPage.GetSortedWidgets method

The GetSortedWidgets method is now obsolete. Use the Widgets collection and sort the returned widgets as needed.

To reproduce the same behavior as GetSortedWidgets, use the following LINQ query:

var sortedWidgets = page.Widgets
    .OfType<PdfWidget>()
    .OrderBy(w => w.BoundingBox.Top)
    .ThenBy(w => w.BoundingBox.Left)
    .ToArray();

The query sorts widgets by their position on the page, from top to bottom and, within the same row, from left to right. It also filters out null entries from the Widgets collection. Such entries can occur when a widget cannot be constructed.

PdfPage.GetWidget method

The GetWidget(int) method is now obsolete. To access a widget by index, use the Widgets collection and its Widgets[int] indexer instead.

PdfPage.GetWords method

The GetWords method now returns IEnumerable<PdfTextData> instead of PdfCollection<PdfTextData>.

See Collections and object identity for migration guidance.

PdfPage.GetXObject method

The GetXObject method is now obsolete. The recommended approach is to use the XObjects collection and iterate over its items rather than relying on XObject indexes.

If you need indexed access for migration purposes, you can obtain the same behavior using LINQ:

var xobj = page.XObjects.Where(entry => entry.XObject is not null).ElementAt(index).XObject!;

For scenarios that require multiple index-based lookups, consider materializing the collection first:

var xobjects = page.XObjects.Where(entry => entry.XObject is not null).ToArray();
var xobj = xobjects[index];

However, iterating over the XObjects collection remains the preferred approach, as it avoids unnecessary materialization and aligns with the intended usage of the API.

The method no longer guarantees that repeated calls for the same XObject return the same object instance. As a result, object identity and reference equality are not guaranteed.

PdfPage.WidgetCount property

The WidgetCount property is now obsolete. Use the Widgets collection and its Count property instead.

PdfPage.Widgets property

The behavior of PdfWidgetCollection, the return type of the Widgets property, has changed.

PdfPage.XObjectCount property

The XObjectCount property is now obsolete. Avoid depending on the number of XObjects whenever possible. Iterating through XObjects is the recommended approach in most scenarios.

// Before v10
for (int i = 0; i < page.XObjectCount; i++)
{
    var xobj = page.GetXObject(i);
    // ...
}
 
// v10 and later
foreach (var entry in page.XObjects)
{
    var xobj = entry.XObject;
    // ...
}

If you need the number of XObjects, use LINQ:

int xobjCount = page.XObjects.Count();

Note that XObjects.Count() is an O(n) operation because it enumerates all XObjects.

If you only need to check whether the collection contains any items, use XObjects.Any() instead:

bool hasXObjects = page.XObjects.Any();

PdfPage.XObjects property

The XObjects property is now of type PdfXObjectMap instead of PdfList<PdfXObject>. This change better reflects how XObjects are stored in a page's PDF resources.

The new type provides similar functionality but does not support index-based operations.

Another important difference is that the collection semantics have changed. Previously, the collection exposed only unique PdfXObject instances. It now exposes all name/XObject pairs found in the underlying PDF structures.

As a result, some PDF files may contain more entries in XObjects than before. This occurs when the same XObject is referenced multiple times under different names. In such cases, PdfXObjectMap exposes each name/XObject pair as a separate entry, even when multiple entries refer to the same underlying XObject.

Member mapping

The following table shows how members of PdfList<PdfXObject> map to members of PdfXObjectMap:

  • PdfList<PdfXObject> XObjects -> PdfXObjectMap XObjects
  • Add(PdfXObject) -> no direct replacement; see Adding items
  • Count -> use LINQ if needed: Count()

The following PdfList<PdfXObject> members are no longer supported:

The enumerator type for the XObjects collection changed from

IEnumerator<PdfXObject> GetEnumerator()

to

IEnumerator<PdfXObjectEntry> GetEnumerator()

This change allows you to enumerate entries that associate XObject names with PdfXObject instances.

Object identity-based operations

PdfXObjectMap creates a new PdfXObjectEntry and a new PdfXObject instance each time an entry is retrieved through the indexer or during enumeration. Because of this behavior, operations that depend on object identity or instance equality are no longer supported.

Update any code that uses Contains(PdfXObject), IndexOf(PdfXObject), or Remove(PdfXObject) to work with XObject names and use the indexer and/or enumeration instead.

Index-based operations

PdfXObjectMap does not provide index-based operations. The recommended approach is to enumerate the collection rather than rely on positional access.

If indexed access is required during migration, you can achieve equivalent behavior using LINQ:

var xobj = page.XObjects.ElementAt(index).XObject;

For scenarios that require multiple indexed lookups, consider materializing the collection first:

var entries = page.XObjects.ToArray();
var xobj = entries[index].XObject;

There is no direct replacement for PdfList<PdfXObject>.RemoveAt(int). Instead, enumerate the map, locate the entry at the desired position, obtain its name, and remove the corresponding XObject using PdfXObjectMap.Remove(string):

PdfXObjectMap map = page.XObjects;
var entry = map.ElementAt(index);
map.Remove(entry.Name);
Adding items

PdfXObjectMap does not provide methods for explicitly adding XObjects. Instead, XObjects are added automatically when you draw them on a page using one of the PdfCanvas.DrawXObject methods.

The library assigns names to automatically added XObjects using the format Xn, where n starts at 0 and increments for each newly added XObject.

PdfPath

The PdfCollection<PdfSubpath> Subpaths property is now obsolete. Use the new IEnumerable<PdfSubpath> GetSubpaths() method instead.

See Collections and object identity for migration guidance.

PdfPublicKeyEncryptionInfo

The PdfCollection<PdfRecipientInfo> Recipients property is now obsolete. Use the new IEnumerable<PdfRecipientInfo> GetRecipients() method instead.

See Collections and object identity for migration guidance.

PdfResetFormAction

The class no longer inherits collection-related members from PdfControlAction. Instead, it now derives directly from PdfAction and exposes its control collection through the
PdfControlCollection Controls property.

Mapping of old members to new ones:

  • PdfCollection<PdfControl> Controls is now PdfControlCollection Controls
  • ControlCount was removed. Use the Count property of the Controls collection.
  • GetControl(int) was removed. Use the indexer of the Controls collection.
  • AddControl(PdfControl) was removed. Use Controls.Add(PdfControl).
  • GetAt(int) was removed. Use indexer access instead.

The Controls collection may construct a new PdfControl instance each time an index is accessed. Because of this, operations that rely on value comparison are not supported. Update any code that uses Contains or IndexOf to rely on positional logic instead.

PdfRichMediaAnnotation

The changes in this class affect how rich media annotations are created, configured, and accessed.

PdfRichMediaAnnotation.Assets property

The Assets property is now of type PdfFileSpecificationMap instead of PdfFileSpecificationCollection. This better reflect the way the collection internally stored in PDF. The new type exposes a similar set of methods and properties, but several members now behave differently.

Mapping of old members to new ones:

  • PdfFileSpecificationCollection Assets -> PdfFileSpecificationMap Assets
  • Get(string) -> removed; use indexer access instead
  • Add(PdfFileSpecification) -> Add(string, PdfFileSpecification); see note below
  • Remove(PdfFileSpecification) -> not supported; see note below

The enumerator type for the Assets collection changed from IEnumerator<PdfFileSpecification> GetEnumerator() to IEnumerator<PdfFileSpecificationEntry> GetEnumerator(), allowing iteration over key/value pairs rather than values only.

The indexer Assets[string] keeps the same signature, but its operational semantics have changed. Previously, it searched the entire collection for an item whose PdfFileSpecification.Specification matched the provided string. Now it performs a direct lookup by key in the underlying PDF structure. It does not parse values to locate a matching pair. Because of this change, existing code may no longer obtain the expected value from the indexer if the key and the entry's PdfFileSpecification.Specification do not match.

The Assets.Add method now requires you to provide the key explicitly. Previously, the key was derived from the provided value's PdfFileSpecification.Specification, with the -1 suffix appended repeatedly whenever that key was already in use.

The Assets.Add method also overwrites any existing entry with the same key. Use Assets.Contains(string) to check whether a key already exists.

The Assets collection creates a new PdfFileSpecificationEntry with a new PdfFileSpecification instance each time an indexer is accessed or the collection is iterated. Because each lookup returns a different object instance, operations that rely on value or instance equality are no longer supported. Update any code that uses Remove(PdfFileSpecification) to operate on the string key instead.

PdfRichMediaAnnotation.Configurations property

The PdfCollection<PdfRichMediaConfiguration?> Configurations property is now obsolete. Use the new IEnumerable<PdfRichMediaConfiguration> GetConfigurations() method instead.

See Collections and object identity for migration guidance.

PdfRichMediaConfiguration

The PdfCollection<PdfRichMediaInstance?> Instances property is now obsolete. Use the new IEnumerable<PdfRichMediaInstance> GetInstances() method instead.

See Collections and object identity for migration guidance.

PdfSpotColor

The ReadOnlyCollection<double> Components property is now obsolete. Use the new double[] GetComponents() method instead.

PdfStringDrawingOptions

The GetCharacterWidths() method now returns IEnumerable<double> instead of ICollection<double>?. An empty enumeration now indicates that character widths from the current canvas font are used without any additional displacement. Previously, the method returned null to signal the same behavior.

The SetCharacterWidths(IEnumerable<double>) method was updated accordingly; previously it accepted an ICollection<double> parameter.

PdfSubmitFormAction

PdfSubmitFormAction no longer inherits collection-related members from PdfControlAction. Its controls are now exposed through the PdfControlCollection Controls property, and the class derives directly from PdfAction.

This change mirrors the one made to PdfResetFormAction. See the PdfResetFormAction section for details and migration guidance.

PdfSubpath

The PdfCollection<PdfPathSegment> Segments property is now obsolete. Use the new IEnumerable<PdfPathSegment> GetSegments() method instead.

See Collections and object identity for migration guidance.

PdfTextData

The GetCharacters and GetCharCodes methods now return IEnumerable<T> instead of PdfCollection<T>.

See Collections and object identity for migration guidance.

PdfWidget

The AssociatedFiles property is now of type PdfFileSpecificationCollection instead of PdfList<PdfFileSpecification?>.

See Collections and object identity for migration guidance.

PdfWidgetCollection

PdfWidgetCollection now implements IEnumerable<PdfWidget?> instead of PdfCollection<PdfWidget>. Despite the change, it still provides the core positional members: Count, the indexer, Clear, and RemoveAt. The former GetAt method has been removed in favor of indexer access.

The Remove(PdfWidget) method has also been removed. Because the collection may construct a new PdfWidget instance each time an element is accessed, operations that rely on value comparison are not supported. Update any code that uses Remove(PdfWidget), Contains(PdfWidget), or IndexOf(PdfWidget) to rely on positional logic instead.

PdfXObject

The changes to PdfXObject introduce a dedicated collection type for associated files and change the return type of the GetObjects() methods to IEnumerable<PdfTextData>.

PdfXObject.AssociatedFiles property

The AssociatedFiles property has changed from PdfList<PdfFileSpecification?> to PdfFileSpecificationCollection.

See Collections and object identity for migration guidance.

PdfXObject.GetObjects methods

All GetObjects() overloads now return IEnumerable<PdfTextData> instead of PdfCollection<PdfTextData>.

See Collections and object identity for migration guidance.

XmpArray

The XmpArray class now implements IEnumerable<XmpValue> and provides collection operations directly. The Values property is obsolete; use the corresponding XmpArray members instead.

Mapping of old members to new ones:

  • Values.Count -> use Count
  • Values.GetAt(int) -> use the indexer
  • Values.Add(XmpValue) -> use Add(XmpValue)
  • Values.Remove(int) -> use RemoveAt(int)
  • Values.Clear() -> use Clear()
  • Values.Contains(XmpValue) -> not supported; see note below
  • Values.IndexOf(XmpValue) -> not supported; see note below
  • Values.Remove(XmpValue) -> not supported; see note below

Each access through the indexer may create a new XmpValue instance. Because of this, operations that rely on value identity or equality are not supported. Update any code that uses Contains(XmpValue), IndexOf(XmpValue), or Remove(XmpValue) to rely on positional logic instead.

XmpMetadata

The Schemas property is now of type XmpSchemaCollection instead of XmpSchemaList. This change removes all collection-management members that relied on value comparison, such as Contains(XmpSchema), IndexOf(XmpSchema), and Remove(XmpSchema).

The new type also no longer provides a string indexer, GetAt(index), or Contains(string). Alternatives are available and listed below.

Mapping of old members to new ones:

  • XmpSchemaList Schemas -> XmpSchemaCollection Schemas
  • Schemas.GetAt(int) -> removed; use indexer access instead
  • Schemas.Contains(XmpSchema) -> not supported; see note below
  • Schemas.IndexOf(XmpSchema) -> not supported; see note below
  • Schemas.Remove(XmpSchema) -> not supported; see note below
  • Schemas.Remove(int) -> use Schemas.RemoveAt(index)
  • the Schemas[string] indexer -> use Schemas.Get(string)
  • Schemas.Contains(string) -> removed; call Schemas.Get(string) and check for null

The Schemas collection may construct a new XmpSchema instance each time an index is accessed. Because of this behavior, operations that rely on value comparison are not supported. Update any code that uses Contains, IndexOf, or Remove to rely on positional logic instead.

XmpSchema

The Properties property is now of type XmpPropertyMap instead of XmpPropertyList. This change reflects how XMP properties are actually stored inside PDF files: as a keyed map rather than an ordered list. The new type exposes a similar set of methods and properties, but several members now behave differently.

In XmpPropertyMap, each entry includes:

  • the property name
  • the property value
  • the namespace prefix
  • the namespace URI

Each XmpPropertyMap instance stores the prefix and URI of its owner. This allows the map to resolve entries referenced by name only, using the owner's namespace information. In XmpPropertyList, this namespace information was not stored internally and was optional on XmpProperty objects.

From XmpPropertyList to XmpPropertyMap

After the type change, several API members were removed or replaced.

Index-based API removed:

  • GetAt(index) -> removed; the new type is key-based, not index-based
  • Remove(index) -> removed; use Remove(string) instead

Value-based API removed. These relied on object identity or equality, which no longer works because entries are recreated on each lookup:

  • Contains(XmpProperty) -> not supported
  • IndexOf(XmpProperty) -> not supported
  • Remove(XmpProperty) -> not supported

These methods replaced by indexer semantics:

  • Get(string) -> removed; use the indexer instead
  • Set(string, XmpValue) -> removed; use the indexer instead

The Add/Set overloads with the description parameter were removed. Descriptions for custom properties in PDF/A extension schemas must now be added directly to the extension schema, not through the property map.

  • Add(string, string, string) -> Add(string, string)
  • Add(string, XmpValue, string) -> Add(string, XmpValue)
  • Set(string, XmpValue, string) -> removed; use indexer

Behavioral changes

The indexer this[string] and Contains(string) keep their signatures, but their semantics changed.

Previously they searched the entire collection for a property whose XmpProperty.Name matched the string. Now they perform a direct key lookup, where the key is implicitly constructed from:

  • the provided name
  • the schema's namespace prefix
  • the schema's namespace URI

The indexer now returns XmpValue? only, without namespace information.

The Properties collection creates a new XmpPropertyEntry with a new XmpPropertyDescriptor instance each time an indexer is accessed or the collection is iterated. Because each lookup returns a different object instance, operations that depend on value or instance equality are no longer supported. Update any code that uses Contains(XmpProperty), IndexOf(XmpProperty), or Remove(XmpProperty) to operate on the string key instead.

XmpString

The Qualifiers property is now of type XmpPropertyMap instead of XmpPropertyList. For details about the API and behavioral changes introduced by this type change, see the XmpSchema section.

XmpString differs in how qualifier namespaces are handled. Unlike properties in an XmpSchema, string qualifiers do not retain a namespace prefix or namespace URI. The same string value can be added to multiple arrays or structures, so it cannot reliably carry a single owner namespace. Because of this, the collection throws an exception if you attempt to add a qualifier with an explicitly specified namespace.

When saving to an XMP document, the qualifiers associated with an XmpString are written using the namespace prefix of the containing XMP structure, such as an XmpSchema or XmpStructure.

XmpStructure

The Properties property is now of type XmpPropertyMap instead of XmpPropertyList. For details about the API and behavioral changes introduced by this type change, see the XmpSchema section.

Page management

In version 10, page management is centered around the PdfPageCollection type, accessible through the PdfDocument.Pages property. Several methods and properties previously exposed directly by PdfDocument have been moved to this collection, and some operations have changed.

Page operations

Use the PdfDocument.Pages collection to access pages and perform operations such as adding, inserting, removing, copying, extracting, moving, and swapping pages. The PdfDocument.PageCount property has also been replaced by Pages.Count.

For example, use PdfDocument.Pages[int] instead of PdfDocument.GetPage(int), and PdfDocument.Pages.RemoveAt(int) instead of PdfDocument.RemovePage(int).

See the PdfDocument API changes section for the complete list of affected members and migration instructions.

Working with page positions

The new PdfPageCollection may create a new PdfPage instance each time a page is accessed. As a result, operations that rely on comparing page objects are no longer supported.

Some methods that previously accepted PdfPage objects also have no direct replacement. Update such code to identify and manipulate pages by their positions in the collection instead.

Page enumeration

The new collection's enumerator may return null entries when a page cannot be constructed from the underlying PDF data. Previously, such pages appeared as empty PdfPage instances. Review code that enumerates pages to account for this change.

Bookmark-related changes

Version 10 introduces a redesigned API for working with PDF bookmarks (outlines). The new API separates bookmark collection management from individual bookmark properties, reduces the number of method overloads, and removes operations that cannot be implemented efficiently with the underlying PDF structure.

The changes affect PdfDocument, PdfOutlineItem, and the new PdfOutlineItemCollection type. The main changes are grouped below.

Bookmark collections

The obsolete PdfDocument.OutlineRoot property has been replaced by PdfDocument.Outlines, which provides direct access to top-level bookmarks without a synthetic root item. Similarly, the PdfOutlineItem.Children property now returns a PdfOutlineItemCollection that supports adding, inserting, removing, and clearing child bookmarks.

The PdfOutlineItem.AddChild and PdfOutlineItem.InsertChild methods have also been replaced by collection methods. These methods create a bookmark and return the newly created item, which you can then configure as needed.

Index-based operations

The new collection API does not provide index-based access to bookmarks. Instead, iterate through collections directly or materialize them when indexed access is required.

Bookmark destinations and actions

The PdfOutlineItem.Action property now represents only explicitly defined PDF actions. The new PdfOutlineItem.View property provides access to document view destinations.

The PdfOutlineItem.Page and PdfOutlineItem.PageIndex properties are now obsolete. Use PdfOutlineItem.View or PdfOutlineItem.Action, as appropriate, to determine a bookmark's destination.

Object identity

The new API may create a new PdfOutlineItem instance each time a bookmark is accessed. Code should not rely on reference equality or assume that repeated calls return the same object instance.

Layer-related changes

The optional content API has been revised to provide a more flexible way to manage layers and their configuration. PdfDocument.OptionalContent provides access to a document's optional content properties, including its layer collection.

The PdfDocument.Layers property and PdfDocument.CreateLayer methods are obsolete. Use PdfDocument.OptionalContent.Layers to access the collection of layers and create new layers. See PdfDocument.Layers property and PdfDocument.CreateLayer methods for migration details.

Layer intents are now represented by intent names (string[]) rather than the PdfLayerIntent type, which is obsolete. The PdfLayer.GetIntents() method has been replaced by PdfLayer.GetIntentNames(). See PdfLayer and PdfLayerIntent for details.

Combo box and list box controls

Combo box and list box controls now use a specialized item collection type, PdfListItemCollection, to manage their items. The methods previously used to add or clear items are now obsolete, and the new collection provides the corresponding operations.

These changes affect PdfComboBox and PdfListBox.

AddItem and AddItems methods

All AddItem and AddItems methods in PdfComboBox and PdfListBox are now obsolete. To add items, use the Items collection and call the corresponding PdfListItemCollection.Add method.

Mapping of old members to new ones:

  • AddItem(PdfListItem) -> Items.Add(PdfListItem)
  • AddItem(string) -> Items.Add(string)
  • AddItems(PdfListItem[]) -> Items.Add(IEnumerable<PdfListItem>)
  • AddItems(IEnumerable<PdfListItem>) -> Items.Add(IEnumerable<PdfListItem>)
  • AddItems(string[]) -> Items.Add(IEnumerable<string>)
  • AddItems(IEnumerable<string>) -> Items.Add(IEnumerable<string>)

ClearItems method

The ClearItems method in both PdfComboBox and PdfListBox is now obsolete. To remove all items, call PdfListItemCollection.Clear on the Items collection instead.

Items property

The Items property in both classes is now of type PdfListItemCollection instead of PdfCollection<PdfListItem>.

See Collections and object identity for migration guidance.

XMP metadata changes

The API for working with XMP metadata has been revised to better reflect the structure of XMP documents and to provide collection types suited to the underlying data.

The changes affect XmpArray, XmpMetadata, XmpSchema, XmpString, and XmpStructure.

Collection type changes

Several properties now expose new collection types that better reflect the underlying data:

  • XmpSchemaCollection replaces XmpSchemaList in XmpMetadata.Schemas.
  • XmpPropertyMap replaces XmpPropertyList in the Properties properties of XmpSchema and XmpStructure and in the Qualifiers property of XmpString.

The XmpArray class also provides collection operations directly, making the obsolete Values property unnecessary.

Changes to collection access

Several collection members have been removed or replaced:

  • Some collections no longer provide GetAt(index) or index-based removal.
  • XmpPropertyMap uses property names as keys. Its indexer replaces the previous Get(string) and Set(string, XmpValue) methods.
  • The string indexer on XmpMetadata.Schemas has been replaced by Get(string).
  • XmpArray now provides an indexer and collection-management methods directly.

See the sections for XmpArray, XmpMetadata, XmpSchema, and XmpString for the complete member mappings.

Changes to collection behavior

Some collection operations that previously relied on object identity or equality are no longer supported.

The updated collections may create new objects when entries are accessed or enumerated. Consequently, operations that accept an object instance may no longer work as expected.

Use positional operations for arrays and key-based operations for properties instead. For example, use XmpArray.RemoveAt(index) to remove an array item or remove a property by its string key.

Namespace handling changes

XmpPropertyMap stores the namespace prefix and URI of its owning schema or structure. Property lookup uses the property name together with this namespace information, so callers do not need to specify the namespace for each lookup.

String qualifiers follow different rules. An XmpString qualifier does not retain its own namespace prefix or URI, so attempting to add a qualifier with an explicitly specified namespace throws an exception. When the XMP document is saved, the qualifier is written using the namespace prefix of its containing structure.

See the XmpSchema and XmpString sections for details.

Photo of Sergey Bobrovsky
Written by

Sergey is a PDF expert and the co-founder of Bit Miracle. He has been developing PDF libraries since 2005. Docotic.Pdf is his best library to date. Customers praise it as a high-quality, robust component that is well-designed and memory-efficient.



Visit our LinkedIn page | Telegram channel | Google Group