Calling Office Conversion Services to generate PDFs from Docentric AX reports

The Docentric AX reporting engine generates reports by merging report data with Word templates and is able to generate multiple output file formats. One of the output formats is also PDF. Docentric reporting engine uses its own logic to generate the PDF file format, which covers most requirements. However, there are some edge scenarios that are not yet supported. In such scenarios, you can use the Office Conversion Services for PDF generation.

Consider using Office Conversion Services in these scenarios:

  • Reports must support RTL languages, such as Arabic or Hebrew, or languages that require compound characters (e.g. Thai)
  • Reports contain charts

If you have no problems with PDF generation with the Docentric AX reporting engine, we do not recommend using Office Conversion Services. The Docentric AX reporting engine is very fast as all processing is done on the AOS servers.

Explanation of Office Conversion Services (OCS)

OCS are used to generate a PDF output format from Word or Excel templates for Configurable Business Documents. This is a separate online service that can receive calls from D365FO, which sends the generated file in Word or Excel to this service and the service returns the generated PDF file. There are some restrictions on the use of this service, most importantly the input document should not exceed 300 pages.

Adding a template that requires OCS conversion

First you need a Docentric template registered in the Docentric AX reports form. It will be used to create reports that you convert to PDF with OCS.

The example in the screenshot below shows a new SalesInvoice.Report template that contains a chart. This template is set as the default template.

The Print management setup is using this template.

The code

Then you need to write a class that calls this service. Create your own model for this customization or use an existing model in which you make additional customizations.

This class contains two methods. The ConvertDocument method is used to send the generated Word document to OCS and get back the PDF format. The customDocumentConverterDelegate method is called via a delegate during report execution. You can set conditions if you want to use OCS for the conversion. The following example restricts the conversion to SalesInvoice.Report or reports in Arabic. Another condition is that the selected output format is PDF.

using System.IO;
using Docentric.AX.Reporting;
using Docentric.AX.Documents;
using Docentric.AX.Documents.Pdf.Properties;

// Sample document converter to generate PDF documents using custom Word->PDF conversion in certain business scenarios.
// This sample uses Office conversion service (BCS). Test it and study its limitations before using it in production.
class DocOfficeConversionServices implements IDocumentConverter
{
    /// <summary>
    /// Convert the Word document to the output format.
    /// </summary>
    /// <param name="document">Word document stream.</param>
    /// <param name="saveOptions">Save options (including output format).</param>
    /// <returns>Conversion result and errors.</returns>
    DocumentConversionResult ConvertDocument(Stream document, DocSaveOptions saveOptions)
    {
        try
        {
            using (MemoryStream convertedDocumentMs = DocDocumentHelper::convertWordExcel2PdfWithOfficeService(document, DocFileFormat::DOCX, true))
            {
                /* Set PDF properties */
                IDocPdfProperties  pdfProperties = DocPdfPropertyManager::GetProperties(convertedDocumentMs).Properties;
                pdfProperties.Title = saveOptions.Title;
                pdfProperties.Author = saveOptions.Author;
                pdfProperties.Subject = saveOptions.Subject;
                pdfProperties.Keywords = saveOptions.Keywords;
                // 'Created' property is set automatically

                MemoryStream resultMs = new MemoryStream();
                DocPdfPropertyManager::SetProperties(convertedDocumentMs, resultMs, pdfProperties);
            
                info("Used Office Conversion Service (BCS) to convert to PDF.");

                return new DocumentConversionResult(DocGlobalHelper::convertContainerToBytes(DocGlobalHelper::convertMemoryStreamToContainer(resultMs)));
            }
        }
        catch (Exception::CLRError)
        {
            DocGlobalHelper::handleClrException(funcName(), "Failed to convert document to PDF using Office Conversion Service (BCS)");
        }
        catch
        {
            DocGlobalHelper::handleException(funcName(), "Failed to convert document to PDF using Office Conversion Service (BCS)");
        }

        return null;
    }

    [SubscribesTo(classStr(DocRenderingSettings), delegateStr(DocRenderingSettings, customDocumentConverterDelegate))]
    static void customDocumentConverterDelegate(DocRenderingSettings _renderingSettings, DocPrintReportSettings _printReportSettings)
    {
        // In this example we want to call Office conversion service for SalesInvoice.Report or for any report in Arabic langurage
        if (_printReportSettings.parmReportId() == "SalesInvoice.Report" || _renderingSettings.parmRenderingLanguageId() == 'ar')
        {
            // use Office conversion service only for PDF output file format
            if (_renderingSettings.parmFileFormat() == DocFileFormat::PDF)
            {
                // Office PDF conversion option is only available for cloud deployments from version 10.0.10. The produced PDF is limited to a maximum number of 300 pages.
                // https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/analytics/electronic-reporting-destinations?toc=/dynamics365/finance/toc.json#OutputConversionToPDF
                // Works also on local onebox environments with the following fix.
                // https://ax.docentric.com/configure-sharepoint-online-integration-in-d365fo-onebox/
                // If you will have problems with fonts, embed them in the Word template.

                DocOfficeConversionServices officeServiceDocumentConverter = new DocOfficeConversionServices();

                _renderingSettings.parmCustomDocumentConverter(officeServiceDocumentConverter);
            }
        }
    }

}

Once you have this code, build the model that contains it. When the build is complete, you can run the report. The result will be a PDF file that is generated via OCS.