v25.1 Release Notes
- 14 minutes to read
License Keys — New in v25.1
This new release requires every developer/build machine to store a DevExpress license key associated with your account.
DevExpress Unified Installer (Windows) automatically obtains your License Key and places it into its correct location. Run the installer in Repair/Register mode to update your key when necessary. If you used our Unified Installer before license keys were introduced (prior to v25.1), you should not experience any changes when installing or updating DevExpress products.
If you bypass the DevExpress Unified Installer, you can download your license key and register it in the system manually (via a file on disk or an environment variable). Review detailed instructions in the following help topic: Online NuGet Feeds, CI/CD, and Other Installation Methods (Windows, macOS, Linux).
New Presentation API Library
Our new PowerPoint Presentation API library is now available (v25.1.4+). As you would expect, our Presentation API library allows you to create, read, edit, convert, merge, split, and print Microsoft PowerPoint files within any .NET-powered application.
Note
The DevExpress Presentation API Library is currently available as a Community Technology Preview (CTP).
The DevExpress Presentation API library supports the following platforms and operating systems:
- NET 8/9, .NET Framework 4.6.2+
- Windows, Linux, macOS
- Azure, AWS, Docker
The PowerPoint Presentation API library ships within the DevExpress Office File API and DevExpress Universal subscriptions. A valid license is required to use this library in production code. For additional information, please visit our Buy page.
The PowerPoint Presentation API library allows you to import and save presentation files using the PPTX file format (of course, you can also print/export files to PDF). It ships with the following built-in capabilities:
- Add, clone, rearrange, resize, hide, or delete presentation slides.
- Create, customize, and apply different slide layouts.
- Access and update slide master content.
- Merge multiple presentations or copy slides from one presentation to another.
- Split one presentation to multiple presentations (slide-by-slide).
- Extract or assign text to a specific paragraph, shape, slide, speaker note, or entire presentation.
- Format text, paragraphs, shapes, and slide backgrounds.
- Manage presentation metadata.
To get started with the DevExpress Presentation API, install the DevExpress.Docs.Presentation
NuGet package (v25.1.4+) from Nuget.org or the local\personal DevExpress NuGet feed. Add the DevExpress.Docs.Presentation
namespace to your project file and create a Presentation instance.
The following code snippet loads a presentation and exports it to a PDF file:
using DevExpress.Docs.Presentation;
//...
// Load a presentation
Presentation presentation = new Presentation(File.ReadAllBytes("mypresentation.pptx"));
// Export to PDF
presentation.ExportToPdf(new FileStream(@"D:\exported-document.pdf", FileMode.Create));
PDF Document API
PDF Document Compression
Preserve Compressed Object Streams when Saving
In previous versions, our PDF processing engine supported compressed object streams only when reading documents. When saving, the DevExpress PDF Document API library wrote PDF object streams in an uncompressed form.
To reduce file size when saving PDF documents using the DevExpress PDF Document API library, v25.1 ships with a new compression engine designed to preserve the original/compressed state of PDF object streams. Our compression mechanism automatically applies to various document structures (text, PDF forms, fonts, and more) and allows you to modify/save PDF files more efficiently.
Compressed object streams are only preserved for documents imported using LoadDocument methods. If you copy a page from one document to another or merge multiple documents into a single document, the object streams for appended pages/documents will be uncompressed.
Image Compression API
v25.1 includes APIs designed to compress document image size. These include our new PdfDocumentProcessor.OptimizeDocument method and the PdfImageCompressionOptions class (to specify desired compression settings). With PdfImageCompressionOptions, you can set target JPEG quality (in percent), specify downsampling DPI, and choose interpolation mode for document images. Call the PdfDocumentProcessor.OptimizeDocument method before saving your document to optimize/reduce output PDF file size. This new option can also help improve printing performance for PDF documents with images.
using (PdfDocumentProcessor pdfProcessor = new PdfDocumentProcessor()) {
pdfProcessor.LoadDocument("Example.pdf");
var options = new PdfImageCompressionOptions() {
JpegQuality = 40,
DownsamplingDpi = 100,
InterpolationMode = DevExpress.Drawing.DXInterpolationMode.HighQualityBicubic,
};
pdfProcessor.OptimizeDocument(options);
pdfProcessor.SaveDocument("Example_Compressed.pdf");
}
Note
Image compression efficiency depends on the number of images in a source document, original size, and physical size on a PDF page.
PDF Redaction API
v25.1 includes new APIs to create and manage redaction annotations. With this capability, you can hide/remove sensitive or private content from your documents and add a colored text overlay across the redacted area.
The redaction process involves two steps:
- Create a redaction annotation with desired appearance in the PDF page area (of value if you wish to hide content but retain it for other users).
- Clear content using the annotation and apply the overlay with specified appearance settings. In this instance, hidden content will be removed.
Create a Redaction Annotation
Redaction is available using the PDF Facade API. To create a redaction annotation, you must:
- Obtain a PdfDocumentFacade instance.
- Retrieve the required PDF page from the PdfDocumentFacade.Pages collection.
- Call the PdfPageFacade.AddRedactAnnotation method. This method returns a PdfRedactAnnotationFacade object you can use to modify the appearance of the redaction annotation: background color, overlay text, text font settings (font name, color, size, and style), text repetition and alignment:
PdfDocumentProcessor pdfProcessor = new PdfDocumentProcessor();
pdfProcessor.LoadDocument("Demo.pdf");
PdfDocumentFacade documentFacade = pdfProcessor.DocumentFacade;
PdfRectangle pageCropBox = pdfProcessor.Document.Pages[0].CropBox;
PdfRectangle redactBounds =
new PdfRectangle(0, pageCropBox.Height-50, 200, pageCropBox.Height);
// Add a redaction annotation at the top left corner of the first document page
PdfRedactAnnotationFacade redactAnnotation =
documentFacade.Pages[0].AddRedactAnnotation(redactBounds);
redactAnnotation.Author = "Jane Doe";
// Setup the appearance of the redaction annotation
redactAnnotation.FillColor = new PdfRGBColor(0, 0, 0);
redactAnnotation.FontColor = new PdfRGBColor(1, 1, 1);
redactAnnotation.FontName = "Calibri";
redactAnnotation.FontSize = 0; // enables font auto-size
redactAnnotation.OverlayText = "Classified";
redactAnnotation.TextJustification = PdfTextJustification.Centered;
redactAnnotation.RepeatText = false;
// Save the document with the redaction annotation and send it for review
pdfProcessor.SaveDocument("output_to_review.pdf");
Apply Redaction Annotations
New APIs allows you to apply the following annotations:
Annotation Type | Method |
---|---|
Single redaction annotation | PdfRedactAnnotationFacade.Apply |
All redaction annotations for a specific page | PdfPageFacade.ApplyRedactAnnotations |
All redaction annotations across the entire document | PdfDocumentFacade.ApplyRedactAnnotations |
You can also use PdfClearContentOptions to specify the type of content to remove (text, graphics, images, or annotations) when you apply the redaction.
PdfDocumentProcessor pdfProcessor = new PdfDocumentProcessor();
pdfProcessor.LoadDocument("Demo.pdf");
PdfDocumentFacade documentFacade = pdfProcessor.DocumentFacade;
// Review redaction annotations and apply
foreach (var page in documentFacade.Pages)
{
var redactionAnnotations =
page.Annotations.Where(annotation => annotation is PdfRedactAnnotationFacade).ToList();
foreach(PdfRedactAnnotationFacade annotation in redactionAnnotations)
{
if (annotation.Author == "Jane Doe")
annotation.Apply();
}
}
// Apply redaction annotations for a page
documentFacade.Pages[0].ApplyRedactAnnotations();
// Apply redaction annotations for the entire document
documentFacade.ApplyRedactAnnotations();
// Specify clear content settings and apply redaction annotations
documentFacade.ApplyRedactAnnotations(new PdfClearContentOptions()
{
ClearAnnotations = true,
ClearImages = true,
ClearText = true,
ClearGraphics = false
});
Spreadsheet Document API
OLE Objects
The DevExpress Spreadsheet Document API Library now supports OLE objects. OLE (Object Linking and Embedding) allows you to link external files and embed data (spreadsheets, PDFs, mail messages, presentations, etc.) into your Excel documents.
With OLE Object support, you can execute the following operations:
- Create OLE objects that store links to external files or embed data from these files in your document;
- Obtain OLE object properties;
- Extract OLE object data from a document;
- Remove OLE objects from a document;
- Print and export documents with OLE object icons to PDF and image formats;
- Load and save documents that contain OLE objects without content loss.
An OLE object is represented by the OleObject
interface. As a unique document shape, the OLE object inherits all settings from the base Shape
interface. Shape settings define OLE object icon appearance, location, and internal object properties (including name, icon size, icon position in a worksheet, alt text, etc).
To access OLE objects in a worksheet, use the Worksheet.OleObjects
collection. They are also available through the base Worksheet.Shapes
collection. The OleObjectCollection.AddLinkedOleObject
and OleObjectCollection.AddEmbeddedOleObject
methods allow you to create a new linked or embedded OLE objects in a worksheet.
Worksheet worksheet = workbook.Worksheets[0];
CellRange oleIconRange = worksheet.Range["B4:D6"];
SpreadsheetImageSource oleIcon = SpreadsheetImageSource.FromFile("oleIcon.png");
// Create linked OLE object
OleObject oleObjectLinked = worksheet.OleObjects.AddLinkedOleObject(
oleIconRange, "package.pdf", OleObjectType.Package, oleIcon);
// Create embedded OLE object from byte array
byte[] sourceData = File.ReadAllBytes("package.pdf");
OleObject oleObjectEmbedded1 = worksheet.OleObjects.AddEmbeddedOleObject(
oleIconRange, sourceData, OleObjectType.Package, oleIcon);
// Create embedded OLE object from file stream
using (var stream = File.OpenRead("package.pdf")) {
OleObject oleObjectEmbedded2 = worksheet.OleObjects.AddEmbeddedOleObject(
oleIconRange, stream, OleObjectType.Package, oleIcon);
}
You can also extract and analyze linked and embedded data. Use the OleObject.InsertType
property to determine OLE object type (linked or embedded). OleObject.AsLinkedContent
and OleObject.AsEmbeddedContent
methods allow you to extract additional information based on OLE object type. For linked objects, you can obtain the linked file name using the OleObjectLinkedContent.FileName
property. For embedded objects, you can get raw binary content using the OleObjectEmbeddedContent.GetRawData
method or save embedded content to a file using the OleObjectEmbeddedContent.SaveAs
method.
Worksheet worksheet = workbook.Worksheets[0];
OleObject oleObject = worksheet.OleObjects[0];
if(oleObject.InsertType == OleObjectInsertType.Linked)
{
OleObjectLinkedContent linkedContent = oleObject.AsLinkedContent();
string linkedFileName = linkedContent.FileName;
}
if(oleObject.InsertType == OleObjectInsertType.Embedded)
{
OleObjectEmbeddedContent embeddedContent = oleObject.AsEmbeddedContent();
byte[] oleRawData = embeddedContent.GetRawData();
if (oleObject.Type == OleObjectType.AdobeAcrobatDocument)
embeddedContent.SaveAs("embedded_document.pdf");
}
You can also remove OLE objects from your spreadsheets. Use the OleObject.Delete
, OleObjectCollection.Remove
or OleObjectCollection.RemoveAt
method to remove individual OLE objects, or use the OleObjectCollection.Clear
method to remove all OLE objects from a worksheet.
OleObject oleObject = worksheet.OleObjects[0];
// Remove the current OLE object
oleObject.Delete();
// or
worksheet.OleObjects.Remove(oleObject);
// Remove all OLE objects in the collection
worksheet.OleObjects.Clear();
Note
OLE object support is only available in OpenXML-based file formats (XLSX, XLSM, XLTX, and XLTM).
Word Processing Document API
Right-to-Left (RTL) Engine Enhancements - Word Processing Document API library
We enhanced our Right-to-Left text rendering engine (improved text order in paragraphs, headers and footers, numbering lists, tables and shapes with bi-directional text including mixed Right-to-Left and Left-to-Right text blocks).
Available when you generate a PDF file, image, or print output via the User Interface or code.
CJK (Chinese, Japanese and Korean) Text Wrapping - Word Processing library
v25.1 adds CJK text wrapping and line-break rule support. In previous versions, DevExpress Word-processing libraries applied Latin text wrapping rules (which rely on space characters and punctuation marks) to Chinese, Japanese, and Korean text.
New wrapping rules correctly apply line breaks between individual characters across document paragraphs, headers and footers, tables, and shapes. Documents that use CJK text wrapping rules can now be printed/exported to PDF and image formats with appropriate text layouts.
PDF Export - Convert Word Content Controls to PDF AcroForm Fields
We enhanced our Word Document to PDF export engine with built-in conversion of Word content controls to PDF AcroForm fields.
DevExpress Word-processing tools automatically convert the following content control types:
- Plain Text
- Rich Text
- Picture
- Check Box
- Combo Box
- Drop-Down List
- Date Picker
Note
Building Block and Repeating Section content controls are exported as regular text.
Due to PDF format limitations, PDF text fields generated from Rich Text content controls use formatting from the first word of the original Rich Text content control. Rich Text content controls that split pages or contain complex objects (like tables) are exported as regular text.
To enable this capability, you must:
- Load your Word file with content controls or generate content controls in your document using the DevExpress Word Processing Document API library.
- Create a
PdfExportOptions
instance. - Enable the
PdfExportOptions.ExportEditingFieldsToAcroForms
property. - Call the
ExportToPdf
method with thisPdfExportOptions
object as a parameter.
using DevExpress.XtraPrinting;
using DevExpress.XtraRichEdit;
using(RichEditDocumentServer wordProcessor = new RichEditDocumentServer())
{
wordProcessor.LoadDocument("input_content_controls.docx");
PdfExportOptions options = new PdfExportOptions();
options.ExportEditingFieldsToAcroForms = true;
wordProcessor.ExportToPdf("output_with_acroform.pdf", options);
}
Compare Word Documents
v25.1 includes new APIs to programmatically compare two Word documents. New CompareDocumentExtensions.Compare
extension methods allow you to execute the following operations:
- Identify changes between two documents and generate document output with revisions.
- Compare both document text and formatting (including case-sensitivity).
- Compare content in document headers, footers and textboxes.
- Setup author/date of output revisions.
- Compare content at the word or character level.
- Return the document with revisions as a new Document instance for further processing (or automatically replace an input document with revised content).
Note
Input documents must not include document revisions. Otherwise, our Compare
method will throw an exception.
RichEditDocumentServer wordProcessor1 = new RichEditDocumentServer();
wordProcessor1.LoadDocument("FirstLook_original.docx");
Document originalDocument = wordProcessor1.Document;
RichEditDocumentServer wordProcessor2 = new RichEditDocumentServer();
wordProcessor2.LoadDocument("FirstLook_revised.docx");
Document revisedDocument = wordProcessor2.Document;
// Setup compare options and generate output with revisions
Document docWithRevisions = originalDocument.Compare(revisedDocument, new CompareDocumentOptions() {
Author = "Jane Doe",
DateTime = DateTime.Now,
ComparisonLevel = ComparisonLevel.Word,
CompareCaseChanges = false,
CompareFormatting = true,
CompareHeadersAndFooters = true,
CompareTextBoxes = true
});
// Save output with revisions to a new file
docWithRevisions.SaveDocument("output_with_revisions.docx", DocumentFormat.Docx);
// Compare two documents and add revisions to the original document
originalDocument.Compare(revisedDocument, new CompareDocumentOptions(), ComparisonTargetType.Original);