Create a dynamic membership card on APEX

Create a dynamic membership card on APEX

In this post, I will demonstrate how to dynamically embed data into images and download them. For this demo, I will be creating a membership card by adding the membership details to a pre-defined template.

First, let's add the membership template image to the static application files. I will use a file I created using canva.com

Open your APEX application and go to Shared Components.

  1. Click on Static Application Files.

  2. Click the Create button and upload the image file.

  3. We are going to need later the file reference. (e.g. #APP_FILES#membership_canvas.png)

To store the data, I created a table named "membership_card", which includes the following details:

create sequence membership_card_seq;

create table membership_card (
    id                             number default on null membership_card_seq.NEXTVAL 
                                   constraint membership_card_id_pk primary key,
    name                           varchar2(255),
    membership_number              number,
    image                          blob
)
;

We are now able to create an APEX page that generates the card. The page will include a select list item (P2_NAME) that allows us to choose the data from the membership by using the following query as the source.

select name,
       id
from membership_card
order by 1

To display the image, we require a standard region where we can add the following code to load the image that was previously added to the shared components. But we don't need to show it, so we will add style="display:none".

<img id="img_membership_card" style="display:none" src="#APP_FILES#membership_canvas.png" crossorigin="anonymous"/>
<canvas id="canvas" />

The next step is to create an AJAX callback process for retrieving the membership data.

DECLARE
  ln_error_code NUMBER;
  lv_error_msg  VARCHAR2(4000);
BEGIN
  FOR i IN 1..apex_application.g_f01.count
  LOOP
    FOR x IN
    (
           SELECT mb.name,
                  to_char(mb.expiration_date,'fmDay, fmDD fmMonth, YYYY') as expiration_date ,
                  substr(membership_number,1,4)||' '||substr(membership_number,5,4)||' '||substr(membership_number,9,4)||' '||substr(membership_number,13,4)  as membership_number, 
                  apex_web_service.blob2clobbase64(p_blob => mb.image_file) as img
           FROM   membership_card mb
           WHERE  mb.id = apex_application.g_f01(i))
    LOOP
      apex_json.open_object;
      apex_json.Open_array('output');
      apex_json.open_object;
      apex_json.WRITE('membershipNumber',x.membership_number);
      apex_json.WRITE('expirationDate',x.expiration_date);
      apex_json.WRITE('name',x.name);
      apex_json.WRITE('image',x.img);
      apex_json.close_object;
      apex_json.close_array;
      apex_json.close_object;
    END LOOP;
  END LOOP;
EXCEPTION
WHEN OTHERS THEN
  apex_json.open_object;
  apex_json.Open_array('output');
  apex_json.open_object;
  apex_json.WRITE('lv_error_code', 0);
  apex_json.WRITE('lv_error_msg', SQLERRM);
  apex_json.close_object;
  apex_json.close_array;
  apex_json.close_object;
END;

Note that we are utilizing the apex_web_service.blob2clobbase64 function to convert the member picture blob into clob base64 format, which enables us to easily use it within the JavaScript code.

Now let's create the JavaScript code that will retrieve the data, embed it into the template image, and generate a new image.

function generateCard(id) {
    /* get the template image size and create a canvas */
    var canvas = document.getElementById("canvas"),
        ctx = canvas.getContext("2d");
    canvas.width = $("#img_membership_card").width();
    canvas.crossOrigin = "Anonymous";
    canvas.height = $("#img_membership_card").height();

    /* load the membership data calling the AJAX process */

    apex.server.process(
        "LOAD_DATA",
        {
            f01: id,
            pageItems: "#P2_NAME"
        },
        {
            success: function (pData) {
                let membershipNumber,
                    name,
                    expirationDate,
                    image,
                    img = new Image();
                img.onload = function () {
                    ctx.drawImage(this, 450, 20, 102, 100);
                };

                /* get the membership data returned by the AJAX   callback process */
                image = pData.output[0].image;
                membershipNumber = pData.output[0].membershipNumber;
                name = pData.output[0].name;
                expirationDate = pData.output[0].expirationDate;

                /* print the data onto the canvas */

                img.src = "data:image/png;base64," + image;

                ctx.clearRect(0, 0, canvas.width, canvas.height);
                ctx.drawImage($("#img_membership_card").get(0), 0, 0);

                ctx.font = "18pt monospace";
                ctx.fillStyle = "#ffffff";
                ctx.fillText(membershipNumber, 40, 200);

                ctx.font = "12pt Arial";
                ctx.fillStyle = "#000000";
                ctx.fillText(name, 120, 245);
                ctx.fillText(expirationDate, 120, 280);
            },
        }
    );
}
/* function to download the image */

function downloadCard() {
    var link = document.createElement("a");
    link.download = "membership_card.jpg";
    link.href = document.getElementById("canvas").toDataURL();
    link.click();
}

Now, we simply need to create a dynamic action fired by the select list to invoke the JavaScript mentioned above.

  1. Right-click on the P2_NAME item and select Create Dynamic Action. That should look as follows
    Name: Print Membership Card

    Event: Change

    Selection Type: Item

    Item(s): P2_NAME

  2. Create an Execute Javascript Code action and add the code below

generateCard(apex.item("P2_NAME").getValue());

We can now run the page. When we select a value from the select list, the template image is populated with the membership data generating a new image.

To download the membership card, we can create a button with the action "Redirect to URL" and add the following code as the target.

javascript:downloadCard();

Did you find this article valuable?

Support Rodrigo Mesquita by becoming a sponsor. Any amount is appreciated!