Printing Invoices to PDF from Dynamics NAV

This is an example of how to use the PDF Printer from Microsoft Dynamics NAV (Navision). I will use the C/AL code to show you a couple of thing that you can do from within NAV. These subjects are covered by the example.

Click here to see a sample output file

PDF Printer version 10.2 or later is required to follow this example.

Printing Multiple Documents

One of the tricky aspects of printing multiple documents in a row is to control the settings for each print job. The tricky part is that the RUNREPORT function returns before the PDF is created. It returns control to your C/AL code as soon as the print job is created and sent to the print spooler queue. The Windows spooler decides when the job is sent to the printer where the actual PDF creation takes place.

Before you run a report in your code, you must set the parameters for the next print job. This controls things like output path, watermarks, and all the other settings for the printer. When this file is saved using the call to the WriteSettings method, you can run your report.

At this point, you have put a print job on the spooler queue. Now you should wait in your code for the PDF creation to finish. There are different strategies that can be used here. The obvious is to wait for the output PDF to appear in the location where we expect it to be created. However, this relies on the fact that every goes as expected. In case of an error the output file may never appear in this location.

It is considered a better strategy to have the PDF Printer create a status file and wait for that instead. The status file is also created in the event of an error. Once the status file is created, you will know that you either have your output as expected or an error to report. It is shown in the example how you set the file name of the status file and how you check it for errors.

Watermarks

A PDF watermark is a simple text string that you can create in your C/AL code. The content is dynamic, which means that you can change the text from one PDF creation to the next. You can change the font, color, size, rotation, and location of the watermark text. A watermark can be placed under or on top of the printed content from the report that you run.

Use Another PDF as Background or Letterhead

One of the strong features of the PDF Printer is the ability to print on top of another PDF document. This is often used when you have a PDF containing only your company letterhead. Normally, the background is below the content of the printed report. However, it is possible to use the other PDF as a stamp on top of the printed content instead.

Signing the PDF

More and more companies require that the documents they receive are signed using a digital certificate by the sender. This is used to validate the origin and content of the document. A digital signature can tell if someone tampered with the content of the document after it was signed.

Merging with other PDF Documents

Another nice feature is that you can merge your report output with other PDF files. Sometime you have information in external PDF documents that you want to merge with the output of your report. Typical use of this feature in ERP systems is merging an invoice with technical information concerning the products on the invoice. Another typical use is adding a PDF with terms and conditions.

Hiding Dialogs

When printing from Navision you often want to hide the normal user interface of the PDF Printer. You make all the selections from your C/AL code and therefore you want to bypass the printing dialogs. The example shows you how to do that.

Error Handling

Handling errors are important to make sure your jobs runs smoothly. Here you will see how errors are detected and error messages are retrieved from the status file.

The Code

// Export Invoices example
// This example is based on PDF Printer 10.2 or later.
// Please go to www.bullzip.com to download the printer.
// There is a counter that limits the number of invoices printed to PDF.

// NOTE: You should change the paths to match the location of the files on your system.

CREATE(pdfSettings);
CREATE(pdfUtil);

IF header.FINDFIRST THEN BEGIN
  baseFolder := 'C:\ss2\Projects\PDFPrt\Examples\Microsoft Dynamics NAV';
  // The status file is used to check for errors and determine when the PDF is ready.
  statusFileName := baseFolder + '\temp\status.ini';
  // You can get a free test certificate at http://www.pdfpowertool.com
  certificateFileName := baseFolder + '\ressources\certificate.pfx';
  certificatePassword := 'password';
  // Set file name of background PDF.
  // Performance can be increased if you use an EPS file for background instead of a PDF.
  // Letterhead-A4.eps is a sample EPS background.
  // Note that using EPS files as background requires the Expert edition of the PDF Printer.
  backgroundFileName := baseFolder + '\ressources\letterhead-a4.pdf';

  mergeBeforeFileName := baseFolder + '\ressources\Before.pdf';
  mergeAfterFileName := baseFolder + '\ressources\After.pdf';

  counter := 0;
  REPEAT
    // Set file name for output file.
    pdfFileName := baseFolder + '\Output\Invoice ' + header."No." + '.pdf';
    
    // Delete old output file if it already exist.
    IF EXISTS(pdfFileName) THEN ERASE(pdfFileName);
    
    // Multiple PDF printers could be installed.
    // Let the automation know which one to control.
    pdfSettings.printerName := pdfUtil.DefaultPrinterName;

    // Set output file name
    pdfSettings.SetValue('Output', pdfFileName);
    
    // Make sure no dialogs are shown during conversion.
    pdfSettings.SetValue('ShowSaveAs', 'never');
    pdfSettings.SetValue('ShowSettings', 'never');
    pdfSettings.SetValue('ShowPDF', 'no');
    pdfSettings.SetValue('ShowProgress', 'no');
    pdfSettings.SetValue('ShowProgressFinished', 'no');
    pdfSettings.SetValue('ConfirmOverwrite', 'no');

    // Set file name of status file to wait for.
    pdfSettings.SetValue('StatusFile', statusFileName);

    // Add a text watermark.
    pdfSettings.SetValue('WatermarkText', 'PDF EXAMPLE - invoice ' + header."no.");
    pdfSettings.SetValue('WatermarkColor', '#FF0000');
    pdfSettings.SetValue('WatermarkVerticalPosition', 'top');
    pdfSettings.SetValue('WatermarkHorizontalPosition', 'right');
    pdfSettings.SetValue('WatermarkRotation', '90');
    pdfSettings.SetValue('WatermarkOutlineWidth', '0.5');
    pdfSettings.SetValue('WatermarkFontSize', '20');
    pdfSettings.SetValue('WatermarkVerticalAdjustment', '5');
    pdfSettings.SetValue('WatermarkHorizontalAdjustment', '1');

    // Add a background.
    // If a professional or expert license is installed the quality of the
    // background will improve.
    pdfSettings.SetValue('Superimpose', backgroundFileName);
    pdfSettings.SetValue('SuperimposeLayer', 'bottom');

    // Merge with other PDF files.
    pdfSettings.SetValue('MergeFile', mergeBeforeFileName + '|.|' + mergeAfterFileName);

    // Sign with a digital certificate.
    pdfSettings.SetValue('SignCertificate', certificateFileName);
    pdfSettings.SetValue('SignPassword', certificatePassword);
    pdfSettings.SetValue('ShowSignature', 'yes');

    // Do not show errors in PDF user interface.
    pdfSettings.SetValue('SuppressErrors', 'yes');

    // You can see more features and settings at
    // http://www.biopdf.com/guide/settings.php

    // Write settings to printer.
    // This writes a file name runonce.ini. It is a configuration that is used
    // for the next print job. The printer will delete the runonce.ini after it
    // is read.
    pdfSettings.WriteSettings(TRUE);

    IF EXISTS(statusFileName) THEN ERASE(statusFileName);


    header2.COPY(header);
    header2.SETRECFILTER;
    REPORT.RUNMODAL(206, FALSE, FALSE, header2);

    IF pdfUtil.WaitForFile(statusFileName, 20000)  THEN BEGIN
      // Check status file for errors.
      IF pdfUtil.ReadIniString(statusFileName, 'Status', 'Errors', '') <> '0' THEN BEGIN
        ERROR('Error creating PDF. ' + pdfUtil.ReadIniString(statusFileName, 'Status', 'MessageText', ''));
      END;
    END ELSE BEGIN
      // The timeout elapsed. Something is wrong.
      ERROR('Error creating ' + pdfFileName)
    END;

    counter := counter + 1;
  UNTIL (header.NEXT=0) OR (counter >= 5);
END;

Objects and C/AL Code

You can download a ZIP file with the code used in this example. Please change the object number in the text file before you import it in your NAV database. Otherwise, you may end up overwriting your own code.

Download C/AL Code for printing multiple invoices in Microsoft Dynamics NAV