Creating a Printer Extension

This article describes how to use the OnAfterSetupPrinters and OnAfterDocumentPrintReady events to set up different printers for reports.

Overview

This article uses a simplified printer extension example for sending reports to an email printer. The example creates a printer extension that sets up a single email printer. Users can then assign the printer to reports from the Printer Selections page in the client. In this article, you will:

  • Create two codeunits, one that subscribes to the OnAfterSetupPrinters event and another that subscribes to the OnAfterDocumentPrintReady event.
  • Use the SMTP Mail and Mail Management codeunits that are part of the base application for sending the report via email.

Set up printers using the OnAfterSetupPrinters event

This section describes how to use the OnAfterSetupPrinters event to set up a printer. When completed, users can select the printer from on the Printer Selections page.

Getting started

  1. Create an AL project for the printer extension.

    See Get Started with AL.

  2. Create a codeunit to use for subscribing to the OnAfterSetupPrinters event.

  3. Create a method that subscribes to the OnAfterSetupPrinters event.

    At this point, your code would look something like this:

    codeunit 50100 SetupPrinter
    {
        [EventSubscriber(ObjectType::Codeunit, Codeunit::"ReportManagement", 'OnAfterSetupPrinters', '', true, true)]
        procedure OnSetupPrinters(var Printers: Dictionary of [Text[250], JsonObject])
        var
    
        begin
    
        end;
    }
    
    

    For more information about subscribing to events, see Subscribing to Events.

  4. Now you can start adding code to the event subscriber method to create the printer payloads.

    In this example, you set up a printer named 'My Printer' that has two paper trays: A4 and Custom. There are two ways of setting values for a payload JSON object. You can add properties (key-value pairs) by using the Add method, as shown in the example. Or, you can use the ReadFrom method to read the JSON data from the string into a JsonObject variable (see Using ReadFrom to create the payload).

    codeunit 50100 SetupPrinter
    {
        [EventSubscriber(ObjectType::Codeunit, Codeunit::"ReportManagement", 'OnAfterSetupPrinters', '', true, true)]
        procedure OnSetupPrinters(var Printers: Dictionary of [Text[250], JsonObject])
        var
            Payload: JsonObject;
            PaperTrays: JsonArray;
            PaperTrayA4: JsonObject;
            PaperTrayCustom: JsonObject;
        begin
            // Step 1: Create the paper trays
            PaperTrayA4.Add('papersourcekind', 'Upper');
            PaperTrayA4.Add('paperkind', 'A4');
    
            PaperTrayCustom.Add('papersourcekind', 'Custom');
            PaperTrayCustom.Add('paperkind', 'Custom');
            PaperTrayCustom.Add('width', 236);
            PaperTrayCustom.Add('height', 322);
            PaperTrayCustom.Add('units', 'mm');
    
            // Step 2: Add the paper trays to the list of paper trays
            PaperTrays.Add(PaperTrayA4);
            PaperTrays.Add(PaperTrayCustom);
    
            // Step 3: Add the required parameters for the payload
            Payload.Add('version', 1);
            Payload.Add('papertrays', PaperTrays);
    
            // Step 4: Add the printer to the dictionary
            Printers.Add('My Printer', Payload);
        end;
    }
    
    
  5. At this point, you can compile your project, and publish/install the extension on the tenant to test the payload.

    Open the Printer Selections page in the client. You should see My Printer as an option in the Printer Name field. If there are errors with payload, you'll get an error when you try to open the Printer Name field. For more information, see Troubleshooting Printer Payload Errors.

About the printer payload

The event subscriber method has one parameter, which is a dictionary of printers. The key is a name of the printer. The value is a JSON object that is referred to as the payload. The payload specifies information about a specific printer. The payload includes several attributes in the following structure:

{
    "version": 1,
    "description":[default=""],
    "duplex":[default=false],
    "color":[default=false],
    "defaultcopies":[default=1],
    "papertrays":  
    [
        {
            "papersourcekind":'Upper' | 1, 
            "paperkind":'A4' | 9,
            "units":[default='HI'],
            "height":[default=0],
            "width":[default=0],
            "landscape":[default=false]
        }
    ]
}

There are few required attributes, such as version and papertrays. The papertrays attribute must contain at least one paper tray setup, which in turn must include the attributes papersourcekind and paperkind. For more information about the attributes, see Printer Payload.

Using the ReadFrom method to create the payload

The following example illustrates how to create a payload by using the ReadFrom method.

codeunit 50101 SetupPrinter2
{
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"ReportManagement", 'OnAfterSetupPrinters', '', true, true)]
    procedure SetupPrinters(var Printers: Dictionary of [Text[250], JsonObject])
    var
        Payload: JsonObject;
    begin
        // Step 1: Read the payload from the text
        Payload.ReadFrom('{"version":1,"papertrays":[{"papersourcekind":"Upper","paperkind":"A4"},{"papersourcekind":"Custom","paperkind":"Custom","width":236,"height":322,"units":"mm"}]}');
 
        // Step 2: Add the printer to the dictionary
        Printers.Add('My Printer', Payload);
    end;
}

Select the paper tray

A printer can have several paper trays. If a report doesn't specify which paper tray to use or the specified paper tray isn't present in the printer's setup, a default paper tray is used. The default paper tray is the first one defined in the papertrays list.

You can change the paper tray for an existing report by subscribing to the OnAfterGetPaperTrayForReport event and setting a value for a DefaultPage parameter.

[EventSubscriber(ObjectType::Codeunit, Codeunit::"ReportManagement", 'OnAfterGetPaperTrayForReport', '', true, true)]
procedure GetPaperTrayForReport(ReportID: Integer; var FirstPage: Integer; var DefaultPage: Integer; var LastPage: Integer)

As an alternative, if you're creating a new report, you can set a paper tray by specifying PaperSourceDefaultPage property.

report 50103 MyReport
{
    PaperSourceDefaultPage = Upper;
            ...
}

Handle print using the OnAfterDocumentPrintReady event

This section describes how to subscribe to the OnAfterDocumentPrintReady event. You subscribe to the OnAfterDocumentPrintReady event to define what happens when a user chooses to print a report. The event subscriber specifies how and where to send the report.

Getting started

  1. Create a codeunit to use for subscribing to the OnAfterDocumentPrintReady event.

  2. Create a method that subscribes to OnAfterDocumentPrintReady event.

    At this point, your code would look something like this:

    codeunit 50102 HandlePrintAction
    {
        [EventSubscriber(ObjectType::Codeunit, Codeunit::ReportManagement, 'OnAfterDocumentPrintReady', '', true, true)]
        procedure OnDocumentPrintReady(ObjectType: Option "Report","Page"; ObjectId: Integer; ObjectPayload: JsonObject; DocumentStream: InStream; var Success: Boolean)
        var
    
        begin
    
        end;
    }
    

    For more information about subscribing to events, see Subscribing to Events.

  3. Now you can start adding code to the event subscriber method to handle the report payload and send the report.

    The following code sends a report by email to the printer that is named 'My Printer'. In this example, the printer's email address is 'myprinterb@businesscentral.onmicrosoft.com'.

    codeunit 50101 SendReportByEmail
    {
        [EventSubscriber(ObjectType::Codeunit, Codeunit::ReportManagement, 'OnAfterDocumentPrintReady', '', true, true)]
        procedure OnDocumentPrintReady(ObjectType: Option "Report","Page"; ObjectId: Integer; ObjectPayload: JsonObject; DocumentStream: InStream; var Success: Boolean)
        var
            SMTPMail: Codeunit "SMTP Mail";
            PrinterNameToken: JsonToken;
            PrinterName: Text;
            ObjectNameToken: JsonToken;
            ObjectName: Text;
            DocumentTypeToken: JsonToken;
            DocumentTypeParts: List of [Text];
            FileExtension: Text;
            MailManagement: Codeunit "Mail Management";
            SendFrom: Text;
            FileName: Text;
            Recipients: List of [Text];
        begin
            begin
                // Step 1: Before doing anything, it is important to check the current Success value
                if Success then
                    exit;
    
                // Step 2: Make sure code only runs for reports
                if ObjectType = ObjectType::Report then begin
    
                    // Step 3: Get report object payload information
                    ObjectPayload.Get('printername', PrinterNameToken);
                    PrinterName := PrinterNameToken.AsValue().AsText();
                    if PrinterName = 'My Printer' then begin
                        ObjectPayload.Get('objectname', ObjectNameToken);
                        ObjectName := ObjectNameToken.AsValue().AsText();
                        ObjectPayload.Get('documenttype', DocumentTypeToken);
    
                        // Step 4: Build the email message
                        DocumentTypeParts := DocumentTypeToken.AsValue().AsText().Split('/');
                        FileExtension := DocumentTypeParts.Get(DocumentTypeParts.Count);
                        Recipients.Add('myprinterb@businesscentral.onmicrosoft.com');
                        SendFrom := MailManagement.GetSenderEmailAddress();
                        SMTPMail.CreateMessage('Sender', SendFrom, Recipients, 'Hello this is your report', 'Please take a look');
                        SMTPMail.AddAttachmentStream(DocumentStream, ObjectName + '.' + FileExtension);
    
                        // Step 5: Send the email for print
                        SMTPMail.Send;
                        Success := true;
                        exit;
                    end;
                end;
            end;
        end;
    }
    
  4. At this point, you can compile and publish/install the extension on a tenant to test.

    First, make sure that SMTP email is set up on the tenant (see Set Up Email in the Application Help).

    Then, on the Printer Selections page, set a report to use 'My Printer', and then run and print the report.

About the report payload

The event subscriber receives the printer payload and combines it with the report metadata, like the report's ID. This combination is the report payload. The content of the report itself is received as a stream object. You add code to define how and where to send the report payload for printing. In this example, it's sent as an email.

The report object payload is a JSON object that includes several parameters and values arranged in a specific structure, as shown in the following example

{
    "filterviews":
    [
        {"name":"Header","tableid":112,"view":"VERSION(1) SORTING(Field3) WHERE(Field3=1(103027))"},
        {"name":"Line","tableid":113,"view":"VERSION(1) SORTING(Field3,Field4) WHERE(Field4=1(0..10000))"},
        {"name":"ShipmentLine","tableid":7190,"view":"VERSION(1) SORTING(Field1,Field2,Field3) WHERE(Field2=1(10000))"}
    ],
    "version":1,
    "objectname":"Standard Sales - Invoice",
    "objectid":1306,
    "documenttype":"application/pdf",
    "invokedby":"00000000-0000-0000-0000-000000000001",
    "invokeddatetime":"2020-01-17T15:33:52.48+01:00",
    "companyname":"CRONUS International Ltd.",
    "printername":"My Printer",
    "duplex":false,
    "color":false,
    "defaultcopies":1,
    "papertray":
    {
        "papersourcekind":257,
        "paperkind":0,
        "landscape":false,
        "units":0,
        "height":1268,
        "width":929
    }
}

The parameters can be read but not modified at runtime. For more information about the report payload, see Report Payload.

Working With and Troubleshooting Payloads
Developing Printer Extensions Overview
Events in AL
Publishing Events
Raising Events
Subscribing to Events