Preview a Dynamic Content region as a PDF in your Oracle APEX application

Photo by CURVD® on Unsplash

Preview a Dynamic Content region as a PDF in your Oracle APEX application

Introduction

Including Dynamic content regions in an Oracle APEX application has sort of become a common practice by most developers due to its ability to render any HTML or text using the PL/SQL Web Toolkit. There are several use cases such as displaying a summary of responses, purchase orders, invoices and lots more. However, there could be an additional user requirement to have them previewed as PDFs. This post shows you how I achieved this using jsPDF - a javascript library for generating PDFs.

demo: Dynamic content to PDF

My Approach

I have achieved this using the steps below:

Add the following URLs to the page property

Javascript (File URLs):

https://html2canvas.hertzen.com/dist/html2canvas.min.js https://unpkg.com/jspdf@latest/dist/jspdf.umd.min.js https://unpkg.com/dompurify@3.0.5/dist/purify.js

Function and Global Variable Declaration:

async function printPDF(pElement) {
   var pdf = new jspdf.jsPDF({format: "a4", unit: "mm"});

   pdf.html(pElement, {
     callback: function (pdf) {
       const fileInput = document.getElementById('PXX_BLOB_HOLDER');
       var file = new File([pdf.output('blob')],  'pdfpreview.pdf');
       let container = new DataTransfer();
       container.items.add(file);
       fileInput.files = container.files
      apex.page.submit( {
          request: "SUBMITREQ",
          showWait: true,
       } );
     },
     margin: 10,
     width: 190,
     windowWidth: 950,
     autoPaging: 'text',
   });
}

Create a region 'File Browse region' and set Appearance - Template: Inline Dialog

Add a file browse item type such as PXX_BLOB_HOLDER (where XX is the page number) and set below

Create a Dynamic content region 'Dynamic content' and include the below code in the PL/SQL Function Body returning a CLOB section.

PL/SQL:

declare
l_clob clob;
 cursor c_random is
   select employee_id, first_name,last_name,phone_number
     from employees
     where rownum <= 40;

begin
  l_clob :='<div id="genpdf">';
  for a in c_random
  loop
    l_clob:=l_clob||apex_escape.html(a.employee_id) || '       '||apex_escape.html(a.first_name)||'       '||apex_escape.html(a.last_name) || '  ('||apex_escape.html(a.phone_number)||')<br/>' ;
  end loop;
  l_clob:=l_clob||'</div>';
  return l_clob;
end;

Create a button 'PDF_Preview' in the Dynamic content region. This button will generate the PDF on the client side.

Add a when button click Dynamic action to the button 'genpdf' as shown below:

Execute Server-side Code

PL/SQL:

delete from apex_application_temp_files;

Execute JavaScript Code

*Note: Please ensure the id names in the dynamic content region and the javascript code below match.

JavaScript:

var element = document.getElementById('genpdf');
printPDF(element)

Create an AJAX callback: Run this application process when requested by a page process and call it 'pdf_preview'.

PL/SQL:

declare
 l_blob_content   blob;
  l_mime_type     VARCHAR2(200);
  l_file_name     VARCHAR2(200):='pdfpreview.pdf';
  l_sla_id        NUMBER;
  l_err_code      VARCHAR2(1000);
  l_err_msg       VARCHAR2(1000);
  v_sqlerrm       VARCHAR2(1000);

BEGIN

 SELECT blob_content,
        mime_type
  INTO   l_blob_content,
         l_mime_type
  FROM   apex_application_temp_files;

  sys.HTP.init;
  sys.OWA_UTIL.mime_header(l_mime_type, FALSE);
  sys.HTP.p('Content-Length: ' || DBMS_LOB.getlength(l_blob_content));
  sys.HTP.p('Content-Disposition: filename="' || l_file_name || '"');
  sys.OWA_UTIL.http_header_close;

  sys.WPG_DOCLOAD.download_file(l_blob_content);
  apex_application.stop_apex_engine;
EXCEPTION
  WHEN apex_application.e_stop_apex_engine
    THEN RAISE;
  WHEN OTHERS THEN
    HTP.p('Kindly generate the document');
END;

Create a static region to display the PDF in an iframe

JavaScript:

<p align="center">
<iframe src="f?p=&APP_ID.:0:&SESSION.:APPLICATION_PROCESS=pdf_preview:NO::" width="99%" height="700">
</iframe>
</p>

Conclusion

This approach is entirely on the client side and you may choose to download and/or print the document. I hope you find it useful.