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.
Click on Static Application Files.
Click the Create button and upload the image file.
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.
Right-click on the P2_NAME item and select Create Dynamic Action. That should look as follows
Name: Print Membership CardEvent: Change
Selection Type: Item
Item(s): P2_NAME
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();