Renduva

Renduva documentation

PL/SQL package

The renduva package calls the server from the database and returns the document as a BLOB. It works from APEX pages, plain PL/SQL and scheduler jobs. It uses APEX_WEB_SERVICE, so it needs APEX installed in the database (any supported release).

Install

1. Let the database reach the server. As a DBA, allow both the schema that will own the package and the APEX schema (APEX_ plus the release, e.g. APEX_240200) to connect to the Renduva host and port. The APEX schema needs it because it makes the HTTP call:

begin
  for p in (select column_value u from table(sys.odcivarchar2list('APP_SCHEMA', 'APEX_240200'))) loop
    dbms_network_acl_admin.append_host_ace(
      host => 'renduva.internal', lower_port => 8080, upper_port => 8080,
      ace  => xs$ace_type(privilege_list => xs$name_list('http'),
                          principal_name => p.u, principal_type => xs_acl.ptype_db));
  end loop;
end;
/

2. Install the package as APP_SCHEMA, which needs CREATE PROCEDURE:

SQL> @renduva_install.sql

For an https:// server, the certificate must be trusted by the wallet APEX uses (Instance Settings → Wallet in APEX administration).

Use

declare
  l_pdf blob;
begin
  renduva.set_server('http://renduva.internal:8080', 'rdv_...');

  -- a template stored on the server
  l_pdf := renduva.generate(
             p_template => 'invoice',
             p_data     => '{"customer": "City of Example", "items": []}');

  -- or a template you keep in the database (e.g. an APEX file upload)
  l_pdf := renduva.generate(
             p_template    => l_docx_blob,
             p_data        => l_json,
             p_output_type => 'docx',
             p_filename    => 'contract.docx');
end;
/
Function / procedure Parameters
set_server p_url, p_api_key: the server and key for this database session
generate (stored template) p_template (name), p_data (JSON, CLOB), p_output_type (default pdf), p_filename
generate (template BLOB) p_template (BLOB), p_data, p_template_type (default docx), p_output_type, p_filename
image p_image (BLOB): returns the image as base64 (CLOB) for the JSON data

p_data is one JSON object for one document, or a JSON array of objects for several records merged into one PDF.

set_server lasts for the database session. APEX does not keep a session between page requests, so call it in an application process (Before Header), or at the start of a job.

Errors

Every failure raises ORA-20100 with the HTTP status and the server's message:

ORA-20100: renduva: HTTP 401: missing or invalid API key
ORA-20100: renduva: HTTP 404: no stored template named "nope"

Building the JSON in SQL

Oracle's JSON functions build the data straight from your tables:

select json_object(
         'customer' value c.name,
         'invoice'  value i.invoice_no,
         'date'     value to_char(i.invoice_date, 'DD Mon YYYY'),
         'items'    value (select json_arrayagg(
                                    json_object('description' value l.description,
                                                'quantity'    value l.quantity,
                                                'price'       value to_char(l.price, 'FM999G990D00'))
                                    order by l.line_no returning clob)
                             from invoice_lines l
                            where l.invoice_id = i.id)
         returning clob)
  into l_json
  from invoices i join customers c on c.id = i.customer_id
 where i.id = :P1_INVOICE_ID;

Format numbers and dates in SQL (TO_CHAR), as above: Renduva prints values exactly as it receives them. For several invoices in one PDF, wrap the query in json_arrayagg.

Images stored as BLOBs go in with renduva.image, which returns the base64 the template's image tags expect (and null for a null BLOB):

json_object('customer' value c.name,
            'logo'     value renduva.image(c.logo)
            returning clob)

Download the PDF from an APEX page

In a page process (or a button's Execute Server-side Code):

declare
  l_pdf blob;
begin
  l_pdf := renduva.generate(p_template => 'invoice', p_data => l_json);
  sys.htp.init;
  sys.owa_util.mime_header('application/pdf', false);
  sys.htp.p('Content-Disposition: attachment; filename="invoice.pdf"');
  sys.owa_util.http_header_close;
  sys.wpg_docload.download_file(l_pdf);
  apex_application.stop_apex_engine;
end;