Creating QR codes and emailing them in a pdf as an attachment from the IBM i

QR codes are very much in the news at the moment. QR codes are basically 2D barcodes but with the ability to contain a greater amount of information that can be read much faster and more accurately than a barcode.

Barcodes were developed back in the 1960s in Japan to address the need for cashiers to be able to automatically scan products from the basket to the shopping bag without having to manually key in the details for each purchased item. Barcodes are great but with the need to be able to squeeze more and more data into the barcode a new format was required. At Denso Wave development engineer Masahiro Hara pioneered the new QR code format that the company launched into the world in 1994 in order to meet the needs of their customers who were craving greater amounts of information to be more easily captured.

Barcodes are typically read by an LED or laser scanner that bounces a red beam of light off the barcode and then back into a photoelectric cell. The rate and number of pulses of light that hit the photoelectric cell determine the characters that are encoded into the barcode. QR code scanners grab an image of the 2D barcode using a camera and then software analyses the image to find key reference points that tell the program where to start looking for the characters that have been encoded into the QR code. QR code optical scanners have the added advantage that they can read QR codes and barcodes that are displayed on a screen. This means they can read codes that have been sent to a mobile phone. LED and laser scanners fail in this regard as they cannot bounce light back from a mobile phone or LCD flat panel display.

Optical QR code scanner

Optical QR code scanner

The QR code scanner pictured above is an entry level scanner that can be purchased online for around forty GB pounds.

The following code snippet allows a barcode to be created on the IBM i and then emailed out as an attachment. First off we have an SQLRPGLE program that builds some of the parameters to be used in the process.


dcl-s addAttchMt char(100);
dcl-s body char(1000);
dcl-s subject char(80);
dcl-s qCmd char(2000);  

dcl-pr QCMDEXC extpgm ;                       
 *n char(250) options(*varsize) const;        
 *n packed(15:5) const;                       
end-pr;                                                                    

// ********************                                                                                          
begsr SendBarcodeEmail;                                                                                          
// ********************                                                                                          
                                                                                                                 
// Create temorary file name for PDF file in /tmp                                                                
exec sql SET :addAttchMt='/tmp/' CONCAT 'FILENAME' CONCAT                                         
 VARCHAR_FORMAT(CURRENT TIMESTAMP, 'YYYYMMDDHH24MISSNNNNNN') CONCAT TRIM(CHAR(INT(RAND()*10000))) CONCAT '.pdf'; 
                                                                                                                 
// Format subject text                                                                                           
subject='This is the subject of the QR code email.';                                             
                                                                                                                 
// Format body text                                                                                              
body='Hi,<br>'+                                                                                                  
'<br>'+                                                                                                          
'This is an example QR code email.<br>'+                                                                          
'<br>'+                                                                                                          
'best regards+'<br>';                                                                  
                                                                                                                 
// Call program to create exit barcode and send as an email attachment                                           
qCmd='SBMJOB CMD(SENDBARCD '+
 'SETFROM(''example_qr_code_email@example.com'') '+                                                 
 'ADDADDRESS(''receiver@example.com'') '+                                                                   
 'ADDATTCHMT('''+%trim(addAttchMt)+''') '+                                                                
 'SUBJECT('''+%trim(Subject)+''') '+                                                                      
 'BODY('''+%trim(body)+''') '+                                                                            
 'QRCODE(''This is some sample data to be encoded ion the QR code'')) '+                                                                     
 'JOBQ(QBATCH/QGPL) USER(QPGMR)';      
QCMDEXC(qCmd:%len(%trimr(qCmd)));                                                                         
                                                                                                          
// ***                                                                                                    
endsr;                                                                                                    
// ***                                                                                                    
                                                                                    

The following command source prompts the parameters for the program that creates the barcode pdf file.

CMD        PROMPT('Exit barcode PDF create & send')            
PARM       KWD(SETFROM) TYPE(*CHAR) LEN(80) RSTD(*NO) +        
             MIN(1) ALWUNPRT(*NO) FULL(*NO) +                  
             CASE(*MIXED) PROMPT('Email from address')         
PARM       KWD(ADDADDRESS) TYPE(*CHAR) LEN(80) RSTD(*NO) +     
             MIN(1) ALWUNPRT(*NO) FULL(*NO) +                  
             CASE(*MIXED) PROMPT('To address')                 
PARM       KWD(ADDATTCHMT) TYPE(*CHAR) LEN(100) RSTD(*NO) +    
             MIN(1) ALWUNPRT(*NO) FULL(*NO) +                  
             CASE(*MIXED) PROMPT('FQPN of PDF attachment')     
PARM       KWD(SUBJECT) TYPE(*CHAR) LEN(80) RSTD(*NO) +        
             MIN(1) ALWUNPRT(*NO) FULL(*NO) +                  
             CASE(*MIXED) PROMPT('Email subject')              
PARM       KWD(BODY) TYPE(*CHAR) LEN(1000) RSTD(*NO) +         
             MIN(1) ALWUNPRT(*NO) FULL(*NO) +                  
             CASE(*MIXED) PROMPT('Email body')                 
PARM       KWD(QRCODE) TYPE(*CHAR) LEN(26) RSTD(*NO) +        
             MIN(1) ALWUNPRT(*NO) FULL(*NO) +                  
             CASE(*MIXED) PROMPT('Number to be barcoded')      

Then use this CL program to run the barcode creation SQLRPGLE program. Note the workstation customization parameter (WSCST) that redirects the print file output to a PDF streamfile in the IFS.

/* Create exit barcode in embedded PDF file in /tmp and email to receiver */         
             PGM        PARM(&SETFROM &ADDADDRESS &ADDATTCHMT +                    
                          &SUBJECT &BODY &BARCODE)                       
                                                                                   
             DCL        VAR(&ADDADDRESS) TYPE(*CHAR) LEN(80)                       
             DCL        VAR(&ADDATTCHMT) TYPE(*CHAR) LEN(100)                      
             DCL        VAR(&BARCODE) TYPE(*CHAR) LEN(26)                          
             DCL        VAR(&BODY) TYPE(*CHAR) LEN(1000)                           
             DCL        VAR(&SETFROM) TYPE(*CHAR) LEN(80)                          
             DCL        VAR(&SUBJECT) TYPE(*CHAR) LEN(80)                          
                                                                                   
             /* Erase existing file. (File name unique so should never exist) */   
             ERASE      OBJLNK(&ADDATTCHMT)                                        
             MONMSG     MSGID(CPF0000)                                             
                                                                                   
             /* Create PDF with embedded barcode in /tmp */                        
             OVRPRTF    FILE(CRTBCDQR) DEVTYPE(*AFPDS) PAGESIZE(31 +               
                          60) LPI(4) CPI(12) OVRFLW(31) +                          
                          TOSTMF(&ADDATTCHMT) WSCST(*PDF) +                        
                          OVRSCOPE(*CALLLVL)                                       
             CALL       PGM(CRTBARCD) PARM(&BARCODE) 

             /* Erase temporary PDF document created in /tmp */                 
             ERASE      OBJLNK(&ADDATTCHMT)                                     
             MONMSG     MSGID(CPF0000)                                          
                                                                                
             ENDPGM                                                                                 

Compile the following print file (PRTF) CRTBCDQR using option 14 in PDM

     A          R CRTBCDQR1                                                       
     A                                      SPACEB(001)                           
     A            TEXTLINE      40A        3                                      
     A          R CRTBCDQR2                                                       
     A            QRCODE        26A       12BARCODE(QRCODE 1 *HRZ +               
     A                                        X'02' (*WIDTH .05) (*SWIDTH 2.5) +  
     A                                        (*QRCODE 4 1 *CONVERT(1) +          
     A                                        *TRIM *AIMSTD(99)))  

This is the ILERPG program to create the QR code

**free                                                                        
                                                                              
// Create QR code PDF file                                                    
// Novagem Ltd                                                                
                                                                              
ctl-opt dftactgrp(*NO);                                   
                                                                              
dcl-f CRTBCDQR printer usropn;                                                
                                                                             
dcl-s #t int(10);                                                             
                                                                              
dcl-ds msgData;                                                               
 *N char(40) inz('Hi,                                     ');                 
 *N char(40) inz('                                        ');                 
 *N char(40) inz('This is an example QR code.             ');                 
 *N char(40) inz('                                        ');                 
 *N char(40) inz('                                        ');                 
 *N char(40) inz('                                        ');                 
 *N char(40) inz('best regards.                           ');                 
 *N char(40) inz('                                        ');
 *N char(40) inz('Best wishes,                            ');                 
 *N char(40) inz('From all the team at                    ');                 
 *N char(40) inz('Novagem Ltd                             ');                 
 *N char(40) inz('                                        ');                 
 msgArray char(40) Dim(12) Pos(1);                                            
end-ds;                                                                       
                                                                              
dcl-pi *N;                                                                    
 barcode_ like(BARCODE);                                                      
end-pi;                                                                       
                                                                              
open CRTBCDQR;                                                                
                                                                              
for #t=1 to %elem(msgArray);                                                  
 msgArray(#t)=%scanrpl('USBIZNAM':USBIZNAM:msgArray(#t));                     
 textline=msgArray(#t);                                                       
 write CRTBCDQR1;                                                             
endfor;                                                                       
                                                                              
BARCODE=barcode_;                                                             
write CRTBCDQR2;                                                              
                                                                              
close CRTBCDQR;  
                                     
*INLR=*ON;                           
return;                                                                                                                         

Here is the CMD source to send the email with the QR code attachment.

CMD        PROMPT('Send Email')                                 
PARM       KWD(SETFROM) TYPE(*CHAR) LEN(80) RSTD(*NO) +         
             MIN(1) ALWUNPRT(*NO) FULL(*NO) +                   
             CASE(*MIXED) PROMPT('Email from address')          
PARM       KWD(ADDADDRESS) TYPE(*CHAR) LEN(80) RSTD(*NO) +      
             MIN(1) ALWUNPRT(*NO) FULL(*NO) +                   
             CASE(*MIXED) PROMPT('Email to address')            
PARM       KWD(ADDATTCHMT) TYPE(*CHAR) LEN(100) +               
             RSTD(*NO) MIN(0) ALWUNPRT(*NO) FULL(*NO) +         
             CASE(*MIXED) PROMPT('FQPN of PDF attachment')      
PARM       KWD(SUBJECT) TYPE(*CHAR) LEN(80) RSTD(*NO) +         
             MIN(1) ALWUNPRT(*NO) FULL(*NO) +                   
             CASE(*MIXED) PROMPT('Email subject')               
PARM       KWD(BODY) TYPE(*CHAR) LEN(1000) RSTD(*NO) +          
             MIN(1) ALWUNPRT(*NO) FULL(*NO) +                   
             CASE(*MIXED) PROMPT('Email body')  
                

ILERPG program to end any previous pase environment calls in this job ahead of a call to QP2SHELL.

**free                                                                  

// End PASE environment                                                 
// Novagem Ltd                                                          
                                                                        
ctl-opt dftactgrp(*NO) option(*NODEBUGIO);                              
                                                                        
dcl-s rc int(10);                                                       
                                                                        
dcl-pr Qp2EndPase int(10) extproc('Qp2EndPase') end-pr;                 
                                                                        
// End PASE                                                             
rc=Qp2EndPase();                                                        
                                                                        
// Close program                                                        
*INLR = *ON;                                                            
return;

Here is the CL program to submit the PHPMailer script using QP2SHELL that will send the email.

/* Send email using PHPMailer */                                                                                   
             PGM        PARM(&SETFROM &ADDADDRESS &ADDATTCHMT &SUBJECT &BODY)                                      
                                                                                                                   
             DCLPRCOPT  DFTACTGRP(*NO) ACTGRP(*NEW)                                               
                                                                                                                   
             DCL        VAR(&ADDADDRESS) TYPE(*CHAR) LEN(80)                                                       
             DCL        VAR(&ADDATTCHMT) TYPE(*CHAR) LEN(100)                                                      
             DCL        VAR(&BODY) TYPE(*CHAR) LEN(1000)                                                           
             DCL        VAR(&LEN1) TYPE(*DEC) LEN(5 0)                                                             
             DCL        VAR(&NULL) TYPE(*CHAR) LEN(1) VALUE(X'00')                                                 
             DCL        VAR(&PHPCMD) TYPE(*CHAR) LEN(100) VALUE('/QOpenSys/pkgs/bin/php')                          
             DCL        VAR(&SCRIPT) TYPE(*CHAR) LEN(100) VALUE('php/sendemail.php')                               
             DCL        VAR(&SETFROM) TYPE(*CHAR) LEN(80)                                                          
             DCL        VAR(&SUBJECT) TYPE(*CHAR) LEN(80)                                                          

             /* Null end variables for QP2SHELL */                                                               
             CHGVAR     VAR(&PHPCMD) VALUE(&PHPCMD *TCAT &NULL)                                                  
             CHGVAR     VAR(&SCRIPT) VALUE(&DOCSDIR *TCAT &SCRIPT *TCAT &NULL)                                   
             CHGVAR     VAR(&SETFROM) VALUE(&SETFROM *TCAT &NULL)                                                
             CHGVAR     VAR(&ADDADDRESS) VALUE(&ADDADDRESS *TCAT &NULL)                                          
             CHGVAR     VAR(&ADDATTCHMT) VALUE(&ADDATTCHMT *TCAT &NULL)                                          
             CHGVAR     VAR(&SUBJECT) VALUE(&SUBJECT *TCAT &NULL)                                                
             CHGVAR     VAR(&BODY) VALUE(&BODY *TCAT &NULL)                                                      
                                                                                                                 
             /* End previous PASE environment call in this activation group */                                   
             CALL       PGM(ENDPASE)                                                                             
                                                                                                                 
             /* Run PHP PHPMAiler script to email barcode */                                                     
             CALL       PGM(QP2SHELL) PARM(&PHPCMD &SCRIPT +                                                     
                                           &SETFROM +                                                            
                                           &ADDADDRESS +                                                         
                                           &ADDATTCHMT +                                                         
                                           &SUBJECT +
                                           &BODY)                                         
                                                                                          
             ENDPGM 

Here is the sendemail.php PHP script that will send the QR code email using PHPMailer. See the previous article on setting up PHPMailer on the IBM i https://novagem.co.uk/use-opensource-software-phpmailer-to-send-emails-with-attachments-using-sls-or-tls-on-ibm-i/

<?php

ini_set('display_startup_errors', 1);
ini_set('display_errors', 1);
error_reporting(-1);

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require '/QOpenSys/pkgs/lib/PHPMailer/src/Exception.php';
require '/QOpenSys/pkgs/lib/PHPMailer/src/PHPMailer.php';
require '/QOpenSys/pkgs/lib/PHPMailer/src/SMTP.php';


$mail = new PHPMailer(true);                                           // "true" as a parameter enables exceptions
try {
 $mail->SMTPDebug = 4;                                                 // 0=no debug output 4=maxiumum debug ouput;
 $mail->SMTPOptions = array(                                           // Ignore certificate errors, use with caution!
  'ssl' => array(
   'verify_peer' => false,
   'verify_peer_name' => false,
   'allow_self_signed' => true
  )
 );
 $mail->isSMTP();                                                      // Set mailer to use SMTP
 $mail->CharSet = 'UTF-8';                                             // Character set for HTML
 $mail->Host =  'mail.example.com';                                    // Specify SMTP server host or ip address
 $mail->Port = 465;                                                    // SMTP port
 $mail->SMTPAuth = true;                                               // Enable SMTP authentication
 $mail->Username =  'username';                                        // SMTP username
 $mail->Password =  'password';                                        // SMTP password
 $mail->SMTPSecure = 'ssl';                                            // Use SMTP encryption 
 $mail->SMTPAutoTLS = true;                                            // Use TLS if available on the server
 $mail->setFrom($argv[4]);                                             // Email from address
 $mail->addAddress($argv[5]);                                          // Send to email address
 if (!empty($argv[6])) $mail->addAttachment($argv[6]);                 // Add attachment
 $mail->isHTML(true);                                                  // Set email format to HTML
 $mail->Subject = $argv[7];                                            // Subject line
 $mail->Body = $argv[8];                                               // HTML formatted body text
 $mail->AltBody = strip_tags(str_replace('<br>', "r\n", $argv[8]),);   // Plain text body

 $mail->send();

} catch (Exception $e) {
 error_log($argv[0] . " PHPMailer failure: " . $mail->ErrorInfo, 0);
}

?>