Skip to content

Instantly share code, notes, and snippets.

@BirgittaHauser
Last active August 30, 2023 10:08
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save BirgittaHauser/041e1fb82b5f8bbeee26578b0a438f4e to your computer and use it in GitHub Desktop.
Save BirgittaHauser/041e1fb82b5f8bbeee26578b0a438f4e to your computer and use it in GitHub Desktop.
Access a PDF document (with RPG) and pass the PDF document as Parameter to another Program/Procedure
//*********************************************************************************************
// I recently get asked how to read a small PDF document into a BLOB and pass it as parameter
// to another function or procedure
// 1. When working with embedded SQL character host variables can only be used up to 32k
// Larger data (up to 16 MB - RPG restriction)
// can be read into a LOB-Variable defined with the SQLTYPE keyword
// 2. Unfortunately the SQLTYPE Keyword cannot be used in a Prototype or Procedure Interface
// 3. But the SQL precompiler generates for the LOB variable a data structure with an
// UNS(4) Subfield _LEN and
// CHAR(xxx) Subfield _DATA
// and this is exactly the structure of a Varying Length field with a 4 digit length prefix
// --> for solving the problem the CLOB data must be transferred into varying lenth field
// this varying length field then can be passed as Parameter to the next procedure/program
// and in the next program the data can be transferred back into a LOB variable (if needed)
//---------------------------------------------------------------------------------------------
// Note: I'd normally not pass the CLOB but only the path where the PDF document is located
// and then read it in the second procedure/Program
//*********************************************************************************************
// In this program I read a PDF-Document into a BLOB Variable and then
// pass it to an (internal) procedure.
// And then write the PDF document back into the IFS with a differen name.
//---------------------------------------------------------------------------------------------
// Note: will only work for small (up to 16 MB) IFS Files
// Should work for Release 7.3 or higher
//*********************************************************************************************
Ctl-Opt DatFmt(*ISO);
/If Defined (*CRTBNDRPG)
CTL-Opt DftActGrp(*No) ActGrp(*NEW);
/EndIf
//*********************************************************************************************
DCL-S GblBlobPDF SQLTYPE(BLOB: 10000000);
DCL-S GblBlobVar VarChar(10000000: 4);
Dcl-S GblErrText VarChar(50);
//*********************************************************************************************
Exec SQL Set Option Commit=*None, DatFmt=*ISO, TimFmt=*ISO,
Naming=*SYS, CloSQLCsr=*EndActGrp;
//*********************************************************************************************
*InLR = *On;
Clear GblBlobPDF;
Exec SQL
Select Line into :GblBlobPDF
from Table(Qsys2.Ifs_Read_Binary(
Path_Name => '/home/Hauser/PDF/PDFDocument_01.pdf'));
If SQLCODE < *Zeros;
Exec SQL Get Diagnostics Condition 1 :GblErrText = MESSAGE_TEXT;
dsply GblErrText;
EndIf;
GblBlobVar = GblBlobPDF;
Int_WriteBack(GblBlobVar);
Return;
//********************************************************************************************
// Write Back
//********************************************************************************************
DCL-Proc Int_WriteBack;
DCL-PI Int_WriteBack;
GblBlobVar VarChar(10000000: 4);
End-Pi;
DCL-S LocBLOBPDF SQLTYPE(BLOB: 10000000);
//---------------------------------------------------------------------------------------------
Clear LocBLOBPDF;
LocBlobPDF = GblBlobVar;
Exec SQL
CAll QSYS2.IFS_Write_Binary(
Path_Name => '/home/Hauser/PDF/PDFDocument_XXX.pdf',
Line => :LocBlobPDF,
File_CCSID => 850,
Overwrite => 'REPLACE');
If SQLCODE < *Zeros;
Exec SQL Get Diagnostics Condition 1 :GblErrText = MESSAGE_TEXT;
dsply GblErrText;
EndIf;
End-Proc;
@bobcozzi
Copy link

`
Ctl-Opt DatFmt(*ISO);
/If Defined (*CRTBNDRPG)
CTL-Opt DftActGrp(*No) ActGrp(*NEW);
/EndIf

   DCL-S  PDFData             SQLTYPE(BLOB: 10000000);
   Dcl-S  GblErrText           VarChar(50);
   DCL-S  pdf_CCSID  int(10);
   DCL-S  pdf_size   int(20);
   DCL-S  pdfFile varchar(256) Inz('/home/cozzi/sample.pdf');

     Exec SQL   Set Option  Commit=*CHG, NAMING=*SYS;

     *InLR       = *On;

     Clear PDFData;

       // Get PDF Size and CCSID
     Exec SQL
         SELECT ccsid, data_size
              INTO :pdf_ccsid, :pdf_size
           FROM TABLE(qsys2.IFS_OBJECT_STATISTICS( :pdfFile ))
           LIMIT 1;

     Exec SQL
          Select LINE  INTO :PDFData
             from Table(Qsys2.Ifs_Read_Binary(
                       Path_Name => :PDFFile ));
     If SQLCODE < *Zeros;
        Exec SQL Get Diagnostics Condition 1 :GblErrText = MESSAGE_TEXT;
        snd-msg gblErrText;
     EndIf;

     Int_WriteBack( PDFData );

     Return;

   DCL-Proc Int_WriteBack;

     DCL-PI Int_WriteBack;
        pdfInfo    LIKEDS(PDFDATA) options(*VARSIZE);
     End-Pi;

     DCL-S  l_sc       char(6);
     DCL-S  l_st       char(5);
     pdfFile = %ScanRPL('.pdf' : '_2.pdf' : pdfFile);
     // Note this assumes you want the target PDF file
     // to be the Windows CCSID regardless of what the
     // original PDF file CCSID is.
     Exec SQL
          CAll QSYS2.IFS_Write_Binary(
               Path_Name  => :PDFFILE,
               Line       => :pdfInfo,
               File_CCSID => 850,
               Overwrite  => 'REPLACE');

     If SQLCODE < *Zeros;
        l_sc = %char(sqlCode);
        l_st = sqlstate;
        Exec SQL Get Diagnostics Condition 1 :GblErrText = MESSAGE_TEXT;
        snd-msg gblErrText +
                  ' SQLState: ' + l_st +
                  ' SQLCODE: ' + l_sc;
     EndIf;

   End-Proc; `

@dancarlosgabriel
Copy link

cool stuff, thanks for sharing Birgitta, and Bob.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment