v24.2 Office File API Release Notes
- 17 minutes to read
Important Changes
GDI+ (Libgdiplus) - End of Support for Non Windows-based systems
Due to our commitment to .NET 8 and following the release of our cross-platform SkiaSharp-based engine last year, we refactored our source code and removed all GDI+ related method calls for non-Windows environments (libgdiplus).
Support Skia for Windows Environment and Deprecation of the AzureCompatibility Property
Multiple DevExpress products (Reporting, BI Dashboard, and Office File API) ship with printing and data export functionality for restricted environments such as Microsoft Azure (where certain GDI/GDI+ calls related to metafiles and font objects are unavailable). The AzureCompatibility.Enable property manages this functionality.
In our v23.2 release cycle, we ended support for capabilities offered by this property toggle for non-Windows environments. With v24.2, we phased out the AzureCompatibility.Enabled
toggle for Office File API libraries in Windows-based environments as well.
We recommend use of our Skia-based Cross-Platform Graphics Engine when targeting cloud/restricted environments.
Office File API libraries automatically determine the applicable drawing engine. You can switch to a different drawing engine manually by using the static DevExpress.Drawing.Settings.DrawingEngine option.
Resolve Missing Fonts
We added a new DXFontRepository.QueryNotFoundFont event for fonts used within a document but missing in an application’s hosting environment. This feature allows you to identify and resolve missing fonts by adding them to DevExpress.Drawing.DXFontRepository before document generation.
In the following code snippet, we use Google Fonts to locate/add fonts to DXFontRepository
:
DXFontRepository.QueryNotFoundFont+= QueryNotFoundFont;
void QueryNotFoundFont(object sender, NotFoundFontEventArgs e) {
var service = new FontCollectorService();
var fontData = service.ProcessFont(e.RequestedFont).Result;
e.FontFileData = fontData;
}
With this enhancement, you are now able to identify and address document appearance issues, ensuring that documents appear as designed, regardless of the hosting platform.
Our components will also log font names in the application output for debugging purposes.
Spreadsheet Document API
Dynamic Array Formulas
We’ve extended our formula calculation engine and integrated dynamic arrays into our Spreadsheet Document API library. Unlike standard array formulas, which return a single value for each cell with a formula, dynamic array functions return a dynamic array of values (this array of values automatically spills into neighboring cells). New functionality includes:
- Dynamic array calculations and Spill Range support.
- New formula error type (#SPILL!).
- Implicit intersection operator (@ symbol) to return a single formula value instead of an array.
You can now import Excel documents containing dynamic array formulas in the Spreadsheet Document API library, call the Workbook.Calculate method to recalculate these formulas, and save the document calculated with values to the Excel formats. Printing and export to PDF are also available.
Additionally, you can manage dynamic array formulas in code. v24.2 ships the following new APIs:
- CellRange.DynamicArrayFormula - Gets or sets a dynamic array formula for a cell range based on the current workbook culture.
- CellRange.DynamicArrayFormulaInvariant - Gets or sets a dynamic array formula for a cell range based on the Invariant culture.
- CellRange.HasDynamicArrayFormula - Indicates if a cell range contains a dynamic array formula.
- Cell.IsTopLeftCellInDynamicArrayFormulaRange - Indicates whether the current cell is the top left cell in the dynamic array formula range.
- Cell.GetDynamicArrayFormulaRange() - Returns a cell range where a dynamic array formula spills.
- Worksheet.DynamicArrayFormulas - Returns a collection of dynamic array formulas for the current worksheet.
Refer to the following code snippet if you’d like to obtain, insert, or clear dynamic array formulas using the new APIs:
Worksheet worksheet = workbook.Worksheets.ActiveWorksheet;
// Insert dynamic array formulas
worksheet["A1"].DynamicArrayFormulaInvariant = "={\"Red\",\"Green\",\"Orange\",\"Blue\"}";
worksheet.DynamicArrayFormulas.Add(worksheet["A2"], "=LEN(A1:D1)");
// Clear dynamic array formulas
Cell cell = worksheet.Cells["B2"];
if (cell.HasDynamicArrayFormula)
{
CellRange dymanicArrayRange = cell.GetDynamicArrayFormulaRange();
dymanicArrayRange.Clear();
}
worksheet.DynamicArrayFormulas.Remove(worksheet.Cells["A1"].GetDynamicArrayFormulaRange());
To suppress dynamic array formula calculations and revert to the previous behavior, set the WorkbookCapabilitiesOptions.DynamicArrayFormulas property to DocumentCapability.Disabled
. In this instance, dynamic array formulas will be calculated as standard array formulas.
Refer to the following article for more information: Array Formulas in Spreadsheet Documents
Embed Images in Cells
The DevExpress Spreadsheet Document API library (v24.2) allows you to embed images directly into worksheet cells (using Microsoft Excel’s Place in Cell option). You can import documents with images embedded in worksheet cells, print these documents, export them to the PDF and save them to a XLSX file (without the content loss).
v24.2 includes new APIs designed to manage cell embedded images in code. Embedded images are stored as cell values. To determine if a cell has an embedded image, use the CellValue.IsCellImage property. Use the CellValue.ImageValue property to obtain the cell image value as an OfficeImage object (allows you to check the image format or obtain image bytes). To insert an image in a cell, assign it to the CellRange.Value property.
The Spreadsheet Document API library supports the following object types as an image source:
Byte[]
System.IO.Stream
System.Drawing.Image
- DevExpress.Drawing.DXImage
- OfficeImage
Additionally, we implemented options to specify image Alt Text (a meaningful description) values and mark the cell image as decorative. These settings are available via the Cell.ImageInfo property.
The following code snippet demonstrates this new API in action:
byte[] imageBytes = File.ReadAllBytes("image.png");
MemoryStream imageStream = new MemoryStream(imageBytes);
DXImage dxImage = DXImage.FromStream(imageStream);
// Insert cell images from a byte array, stream and DXImage object
worksheet.Cells["A1"].Value = imageBytes;
worksheet.Cells["A2"].Value = imageStream;
worksheet.Cells["A3"].Value = dxImage;
// Set image Alt Text values
worksheet.Cells["A1"].ImageInfo.AlternativeText = "Image AltText";
// Mark the cell image as decorative
if (worksheet.Cells["A2"].Value.IsCellImage)
worksheet.Cells["A2"].ImageInfo.Decorative = true;
// Save the cell image to a new file
OfficeImage cellImage = worksheet.Cells["A1"].Value.ImageValue;
if (cellImage.RawFormat == OfficeImageFormat.Png)
{
byte[] cellImageBytes = cellImage.GetImageBytes(cellImage.RawFormat);
File.WriteAllBytes("saved_image.png", cellImageBytes);
}
// Remove cell image
worksheet.Cells["A3"].ClearContents();
Justify & Distributed Vertical Text Alignment
v24.2 adds support for Justify and Distributed vertical alignment types within Spreadsheet cells. Excel files with these alignment options can be printed and exported to PDF.
You can use the Cell.Alignment.Vertical
property to specify vertical alignment in code.
The following code snippet specifies alignment in code:
var workbook = new Workbook();
var worksheet = workbook.Worksheets[0];
Cell cellA1 = worksheet.Cells["A1"];
cellA1.Value = "Centered and Justified";
cellA1.Alignment.Horizontal = SpreadsheetHorizontalAlignment.Center;
cellA1.Alignment.Vertical = SpreadsheetVerticalAlignment.Justify;
workbook.ExportToPdf("Result.pdf");
PDF Document API
ZuGFeRD v2.3.2 Support
The DevExpress PDF File API (part of the Office File API Suite) fully supports Germany’s ZuGFeRD e-invoicing standard (ZuGFeRD v2.3.2).
You can attach ZuGFeRD v2.3.2 XML files to your PDF documents by calling the PdfDocument.AttachZugferdInvoice method (using the new PdfZugferdVersion.Version2_3_2
enumeration value as an argument):
using (var pdfDocumentProcessor = new PdfDocumentProcessor())
{
pdfDocumentProcessor.LoadDocument("Invoice.pdf");
pdfDocumentProcessor.Document.AttachZugferdInvoice
(File.ReadAllBytes("ZUGFeRD-invoice.xml"), PdfZugferdVersion.Version2_3_2, PdfZugferdConformanceLevel.Extended);
pdfDocumentProcessor.SaveDocument("Invoice_ZuGFeRD.pdf");
}
Signature Validation Enhancements
v24.2 ships with enhanced PDF Signature Validation APIs. New capabilities include:
- API to verify certificate revocation based on Online Certificate Status Protocol (OCSP) responses.
- APIs to verify certificate revocation based on Certificate Revocation List (CRL).
- APIs to validate Long Term Validation (LTV) signatures.
- APIs to validate Document Level Timestamps.
Call the PdfPkcs7Signature.CheckCertificateRevocation method to obtain information about signature certificate revocation.
using DevExpress.Pdf;
// ...
CheckCertificateRevocationInfo();
// ...
public static void CheckCertificateRevocationInfo() {
using (PdfDocumentSigner documentSigner = new PdfDocumentSigner("Signed_file.pdf"))
// Retrieve the list of `PdfSignatureInfo` objects in the document.
foreach (var signature in documentSigner.GetSignatureInfo()){
Console.WriteLine("Signature field name : {0}", signature.FieldName);
Console.WriteLine("Signer name : {0}", signature.SignerName);
var pkcs7 = documentSigner.GetPdfPkcs7Signature(signature.FieldName);
Console.WriteLine("Is signature valid? : {0}", pkcs7.VerifySignature());
// Check whether the CRL/OCSP responses are embedded in the signature; otherwise, request them online.
Console.WriteLine("Is CRL embedded?: {0}", pkcs7.CrlList != null);
Console.WriteLine("Is OCSP embedded?: {0}", pkcs7.OcspList != null);
PdfRevocationVerificationOptions options = new PdfRevocationVerificationOptions(){
TryFetchCrlOnline = pkcs7.CrlList == null,
TryFetchOcspOnline = pkcs7.OcspList == null,
};
// Obtain information about the signature certificate revocation.
var result = pkcs7.CheckCertificateRevocation(DateTime.UtcNow, options);
Console.WriteLine("Is certificate revoked?: {0}", result.IsCrlRevoked);
if (result.IsCrlRevoked)
Console.WriteLine("Is CRL found online?: {0}", result.IsCrlFoundOnline);
Console.WriteLine("OCSP Response Status: {0}", result.OcspRevocationStatus);
if (result.OcspRevocationStatus != PdfOcspRevocationStatus.None)
Console.WriteLine("Is OCSP found online?: {0}", result.IsOcspFoundOnline);
// Check whether the signature is a TimeStamp.
Console.WriteLine("Is Document Timestamp?: {0}", pkcs7.IsDocumentTimeStamp);
Console.WriteLine();
}
}
Call the PdfDocumentSigner.VerifyLtv method to obtain Long Term Validation (LTV) records about the state of the signature certificate.
int largestEdgeLength = 1000;
using (PdfDocumentProcessor processor = new PdfDocumentProcessor())
{
// Load a document
processor.LoadDocument("..\\..\\Document.pdf");
for (int i = 1; i <= processor.Document.Pages.Count; i++)
{
// Export pages to SVGs
DXSvgImage image = processor.CreateSvgImage(i, largestEdgeLength);
// Save the images
image.Save("..\\..\\MySvg" + i + ".svg", DXImageFormat.Svg);
}
}
Export PDF Documents to SVG
We implemented the PdfDocumentProcessor.CreateSvgImage method designed to convert individual PDF pages to SVG images.
int largestEdgeLength = 1000;
using (PdfDocumentProcessor processor = new PdfDocumentProcessor())
{
// Load a document
processor.LoadDocument("..\\..\\Document.pdf");
for (int i = 1; i <= processor.Document.Pages.Count; i++)
{
// Export pages to SVGs
DXSvgImage image = processor.CreateSvgImage(i, largestEdgeLength);
// Save the images
image.Save("..\\..\\MySvg" + i + ".svg", DXImageFormat.Svg);
}
}
Refer to the following article for more information: How to: Export a PDF Page to an SVG Image
Note
SVG export ships with limitations. We do not embed fonts in target SVG files. PDF patterns and shadings are not supported. Limited transparency support.
Image Extraction API
To improve user experiences when extracting PDF content and analyzing document structure, we introduced PdfDocumentProcessor.GetImagesInfo method overloads for the PDF Document API library. With our new APIs, you can obtain additional information about PDF page images and determine image size and location on a page.
Refer to the following article for more information: How to: Extract Images from a Document.
Word Processing Document API
SmallCaps Formatting
Our Word Processing Document API library now supports Small Caps character formatting. Word documents with Small Caps formatting can be printed and exported to PDF.
To enable formatting in code, use the CharacterProperties.SmallCaps property:
Document document = wordDocumentProcessor.Document;
Paragraph paragraph = document.Paragraphs[0];
CharacterProperties characterProperties = document.BeginUpdateCharacters(paragraph.Range);
characterProperties.SmallCaps = true;
document.EndUpdateCharacters(characterProperties);
Page Borders
The DevExpress Word Processing Document API library now supports page borders. Word documents with page borders can be saved without content loss, printed, and exported to PDF.
New APIs allow you to manage page borders in code. Borders can be set for each document section individually. Use the Section.PageBorders property to access and modify borders for a specific section.
New members help you adjust the following border settings:
- Line style
- Color
- Width
- Distance from text or page edge
You can also apply borders to specific pages within a section: to all pages, only the first page, or all pages except the first. And you can change border z-order to show them in front or behind main text. The following code snippet applies different borders in a document with two sections:
Document document = wordDocumentProcessor.Document;
string pageBreaks = "\f\f\f";
// Generate a document with two sections and multiple pages in each section
document.AppendText(pageBreaks);
document.Paragraphs.Append();
document.AppendSection();
document.AppendText(pageBreaks);
// Set borders for the first page of the first section
SetPageBorders(pageBorders1.LeftBorder, BorderLineStyle.Single, 1f, Color.Red);
SetPageBorders(pageBorders1.TopBorder, BorderLineStyle.Single, 1f, Color.Red);
SetPageBorders(pageBorders1.RightBorder, BorderLineStyle.Single, 1f, Color.Red);
SetPageBorders(pageBorders1.BottomBorder, BorderLineStyle.Single, 1f, Color.Red);
pageBorders1.AppliesTo = PageBorderAppliesTo.FirstPage;
Section secondSection = document.Sections[1];
SectionPageBorders pageBorders2 = secondSection.PageBorders;
// Set borders for all pages of the second section
SetPageBorders(pageBorders2.LeftBorder, BorderLineStyle.Double, 1.5f, Color.Green);
SetPageBorders(pageBorders2.TopBorder, BorderLineStyle.Double, 1.5f, Color.Green);
SetPageBorders(pageBorders2.RightBorder, BorderLineStyle.Double, 1.5f, Color.Green);
SetPageBorders(pageBorders2.BottomBorder, BorderLineStyle.Double, 1.5f, Color.Green);
pageBorders2.AppliesTo = PageBorderAppliesTo.AllPages;
pageBorders2.ZOrder = PageBorderZOrder.Back;
//.....
void SetPageBorders(PageBorder border, BorderLineStyle lineStyle,
float borderWidth, Color color)
{
border.LineStyle = lineStyle;
border.LineWidth = borderWidth;
border.LineColor = color;
}
Refer to the following article for more information: Specify Page Borders
Paragraph Borders API
v24.2 includes new APIs to manage Word document paragraph borders in code. You can now add borders to document paragraphs, change border style, color, and thickness for each border type (Top, Left, Right, Bottom or Horizontal) individually, or remove border formatting as needed. Additionally, you can manage paragraph border settings for paragraph styles and list levels in numbered lists.
Paragraph border settings are available via the ParagraphBorders class. Use the Paragraph.Borders, ParagraphProperties.Borders, or ParagraphStyle.Borders
property to obtain the ParagraphBorders
object and modify border settings based on your usage scenario.
RichEditDocumentServer documentProcessor = new RichEditDocumentServer();
documentProcessor.Text = "paragraph 1\r\nparagraph 2\r\nparagraph 3\r\nparagraph 4\r\n";
Document document = documentProcessor.Document;
// Setup borders for one paragraph
Paragraph firstParagraph = document.Paragraphs[0];
ParagraphBorders borders1 = firstParagraph.Borders;
SetBorders(borders1.LeftBorder, BorderLineStyle.Single, 1f, Color.Red);
SetBorders(borders1.TopBorder, BorderLineStyle.Single, 1f, Color.Red);
SetBorders(borders1.RightBorder, BorderLineStyle.Single, 1f, Color.Red);
SetBorders(borders1.BottomBorder, BorderLineStyle.Single, 1f, Color.Red);
// Setup borders for multiple paragraphs
Paragraph secondParagraph = document.Paragraphs[1];
Paragraph forthParagraph = document.Paragraphs[3];
DocumentRange paragraphRange = document.CreateRange(secondParagraph.Range.Start,
forthParagraph.Range.End.ToInt() - secondParagraph.Range.Start.ToInt());
ParagraphProperties paragraphProperties = document.BeginUpdateParagraphs(paragraphRange);
ParagraphBorders borders2 = paragraphProperties.Borders;
SetBorders(borders2.LeftBorder, BorderLineStyle.Double, 1.5f, Color.Green);
SetBorders(borders2.TopBorder, BorderLineStyle.Double, 1.5f, Color.Green);
SetBorders(borders2.RightBorder, BorderLineStyle.Double, 1.5f, Color.Green);
SetBorders(borders2.BottomBorder, BorderLineStyle.Double, 1.5f, Color.Green);
SetBorders(borders2.HorizontalBorder, BorderLineStyle.Double, 1.5f, Color.Green);
document.EndUpdateParagraphs(paragraphProperties);
// Reset paragraph borders
secondParagraph.Borders.Reset();
//...
void SetBorders(ParagraphBorder border, BorderLineStyle lineStyle,
float borderWidth, Color color)
{
border.LineStyle = lineStyle;
border.LineWidth = borderWidth;
border.LineColor = color;
}
Alt Text for Tables
Table.Title and Table.Description properties allow you to specify alternative, text-based representations of the information contained in a Word document table.
Table title and description are also in use when you export a Word document to an accessible PDF format.
RichEditDocumentServer documentProcessor = new RichEditDocumentServer();
documentProcessor.LoadDocument("tables.docx");
Document document = documentProcessor.Document;
if(document.Tables.Count > 0)
{
Table table = document.Tables[0];
table.Title = "Table title..";
table.Description = "Table description..";
}
Note
The table title and description are only supported in OpenXml document formats (.docx, .docm, .dotx, .dotm) and HTML.
Macros API
We implemented a Document.VbaProject property to programmatically retrieve a VBA project stored in a macro-enabled Word file. Use the VbaProject.Modules collection to obtain information about VBA project modules (the number of modules, their names and binary data) or remove a specific module from the project. If the current document format does not support macros or a macro-enabled document does not contain macros, the VbaProject.Modules collection will be empty.
// Check if the current document has macros and remove them
Document document = wordProcessor.Document;
if(document.VbaProject.Modules.Count > 0)
document.VbaProject.Modules.Clear();
Barcode Generation API
New Aztec Barcode
You can now generate an Aztec code when using our Barcode Generation library. Aztec barcodes offer a compact/efficient way to encode large amounts of data without requiring a quiet zone, which makes them ideal for space-constrained documents.
To create an Aztec Barcode, set the BarCode.Symbology property value to AztecCode
and specify appropriate barcode properties.
You can also specify barcode data using BarCode.CodeText or BarCode.CodeBinaryData properties as follows:
using DevExpress.BarCodes;
using DevExpress.XtraPrinting.BarCode;
using DevExpress.Drawing;
// ...
BarCode barCode = new BarCode();
barCode.Symbology = Symbology.AztecCode;
barCode.Options.AztecCode.CompactionMode = AztecCodeCompactionMode.Text;
barCode.CodeText = "https://www.devexpress.com/";
// OR
// barCode.Options.AztecCode.CompactionMode = AztecCodeCompactionMode.Binary;
// barCode.CodeBinaryData = Encoding.Default.GetBytes("https://www.devexpress.com/");
barCode.Options.AztecCode.ErrorLevel = AztecCodeErrorCorrectionLevel.Level1;
barCode.Options.AztecCode.ShowCodeText = false;
barCode.DpiX = 72;
barCode.DpiY = 72;
barCode.Module = 2f;
barCode.Save("aztec_code.png", DXImageFormat.Png);
New Micro QR Code
You can now create Micro QR Codes (a smaller alternative to traditional QR Codes). Micro QR Codes are suitable for space-limited documents, where only small amounts of data (35 characters or 128 bits) require encoding.
To create a Micro QR Code, set the BarCode.Symbology property value to MicroQRCode
and specify appropriate barcode properties.
You can also specify barcode data using BarCode.CodeText or BarCode.CodeBinaryData properties as follows:
using DevExpress.BarCodes;
using DevExpress.XtraPrinting.BarCode;
using DevExpress.Drawing;
// ...
BarCode barCode = new BarCode();
barCode.Symbology = Symbology.MicroQRCode;
barCode.Options.MicroQRCode.CompactionMode = MicroQRCodeCompactionMode.AlphaNumeric;
barCode.CodeText = "123ABC";
//OR
// barCode.Options.MicroQRCode.CompactionMode = MicroQRCodeCompactionMode.Byte;
// barCode.CodeBinaryData = Encoding.Default.GetBytes("123ABC");
barCode.Options.MicroQRCode.ErrorLevel = MicroQRCodeErrorCorrectionLevel.L;
barCode.Options.MicroQRCode.ShowCodeText = false;
barCode.Save("microqr_code.png", DXImageFormat.Png);