A complete step-by-step guide to building a client-side PDF generator with profile image embedding and PL/SQL database persistence.
Welcome to the world of jsPDF inside Oracle APEX. In this tutorial, we wire up a PDF generator that captures a user's name, email, phone number, and profile picture — renders them into a beautiful PDF layout — and saves everything to Oracle, all in one button click.
What you'll build: A dynamic PDF generator with user profile details and an embedded image, powered by jsPDF in the browser with PL/SQL database persistence on submit.
1Create Page Items
Navigate to Page 4 in your APEX application and create the following four page items. These will collect all the user information needed for the PDF.
The P4_PROFILE_PIC item with type Image Upload is the key ingredient — it exposes a native file input that jsPDF uses to embed the image via the FileReader API.
2Load jsPDF via JavaScript File URL
In Page Designer, go to Page 4 → JavaScript → File URLs and add the following CDN URL. This loads jsPDF as a UMD global available as window.jspdf — no npm, no bundler required.
3Create the Generate PDF Button
Add a button to the page with the following settings, then attach two True Actions via a Dynamic Action on button click.
-- Button Configuration
Name : Generate_PDF
Action : Defined by Dynamic Action
// Import jsPDF from the global window object
var { jsPDF } = window.jspdf;
// Gather form values from page items
var ename = $v('P4_NAME');
var email = $v('P4_EMAIL');
var phone = $v('P4_PHONE_NUMBER');
// Function to generate and download the PDF
function generatePDFWithImage(imgDataURL) {
var doc = new jsPDF();
// PAGE DIMENSIONS
var pageWidth = doc.internal.pageSize.getWidth();
// MAIN HEADING
doc.setFont("helvetica", "bold");
doc.setFontSize(24);
doc.setTextColor(10,10,80);
var mainTitle = "My Custom PDF Report";
var textWidth = doc.getTextWidth(mainTitle);
var xPos = (pageWidth - textWidth) / 2;
var yPos = 18;
doc.text(mainTitle, xPos, yPos);
// SECTION BORDER
var sectionX = 15,sectionY = 30,sectionW = 180,sectionH = 80;
doc.setDrawColor(0,150,200);
doc.setLineWidth(1);
doc.rect(sectionX, sectionY, sectionW, sectionH);
// SECTION TITLE
doc.setFontSize(24);
doc.setTextColor(40,40,120);
doc.text("User Profile", sectionX + 5, sectionY + 12);
// USER DETAILS
var leftX = sectionX + 8;
var valX = leftX + 35;
var startY = sectionY + 25;
var lineHeight = 14;
doc.setFontSize(12);
doc.setTextColor(0,102,204);
doc.text("Name:", leftX, startY);
doc.setTextColor(60,60,60);
doc.text(ename, valX, startY);
doc.setTextColor(0,102,204);
doc.text("Email:", leftX, startY + lineHeight);
doc.setTextColor(60,60,60);
doc.text(email, valX, startY + lineHeight);
doc.setTextColor(0,102,204);
doc.text("Phone:", leftX, startY + 2 * lineHeight);
doc.setTextColor(60,60,60);
doc.text(phone, valX, startY + 2 * lineHeight);
// PROFILE IMAGE
var imgW = 50;
var imgH = 50;
var imgX = sectionX + sectionW - imgW - 10;
var imgY = sectionY + 15;
if(imgDataURL) {
doc.setDrawColor(160,160,160);
doc.rect(imgX - 2, imgY - 2, imgW + 4, imgH + 4);
doc.addImage(imgDataURL, 'JPEG', imgX, imgY, imgW, imgH);
} else {
doc.setFontSize(11);
doc.setTextColor(140,140,140);
doc.text("No Profile Picture", imgX, imgY + imgH / 2);
}
// VERTICAL SEPARATOR
var sepX = imgX - 13;
doc.setDrawColor(200,220,255);
doc.line(sepX, sectionY + 8, sepX, sectionY + sectionH - 8);
// DOWNLOAD PDF
doc.save("UserDetails.pdf");
}
// READ IMAGE FIRST (THIS PART MOVED OUTSIDE FUNCTION)
var fileInput = document.getElementById('P4_PROFILE_PIC');
if (fileInput && fileInput.files && fileInput.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
generatePDFWithImage(e.target.result);
};
reader.readAsDataURL(fileInput.files[0]);
} else {
generatePDFWithImage(null);
}
apex.page.submit({
request: 'SUBMIT',
validate: true
});
4Create the PL/SQL Process
In Page Designer, go to Processing → Processes → Create. Set the type to PL/SQL Code, point to After Submit, and paste the following:
DECLARE
l_blob BLOB;
l_filename VARCHAR2(255);
l_mime VARCHAR2(128);
BEGIN
SELECT blob_content, filename, mime_type
INTO l_blob, l_filename, l_mime
FROM apex_application_temp_files
WHERE name = :P4_PROFILE_PIC; -- Replace with your item name
INSERT INTO USER_PROFILE_DETAILS (
USER_NAME,
USER_EMAIL,
USER_PHONE_NUMBER,
USER_PROFILE,
PROFILE_MIME,
PROFILE_NAME
)
VALUES (:P4_NAME,:P4_EMAIL,:P4_PHONE_NUMBER,l_blob, l_mime, l_filename);
END;
apex_application_temp_files is a built-in APEX view that temporarily stores uploaded files during a session. We query it using the page item name as the file identifier key.
End-to-end flow
Here's what happens the moment the user clicks Generate PDF:
Tips & customization ideas
- Change RGB values in
setTextColor()andsetDrawColor()to match your brand palette. - Use
doc.addPage()to add additional pages and continue rendering beyond the first page. - Load TTF fonts into jsPDF with
doc.addFont()for non-Latin characters and custom brand typography. - Add the jsPDF-AutoTable plugin (also available on CDN) for dynamic, styled data tables inside the PDF.
- Add APEX page validations so Name, Email, and Phone are required before the button fires the Dynamic Action.
You're all set.
No server rendering, no paid plugins, no complex configuration. Clean JavaScript, a few page items, and the power of the browser's native APIs — delivering a premium download experience right inside Oracle APEX.
Comments
Post a Comment