Storyline 360 - Generating a PDF Certificate using JavaScript

Jan 31, 2018

This example demonstrates how to use the pdfmake JavaScript library to generate PDF certificates in the browser using Storyline 360.

This is a followup to my original post found here. The only changes from the original are that this is now a Storyline 360 project and pdfmake has been updated to the latest version (v0.2.4 at time of posting).

The linked example provides a brief overview of how this works. I've tested this successfully in Internet Explorer, Chrome, Firefox, Chrome on Android and Safari on iOS.

The attached source files contains a basic .story file as well as a folder (certificate) that includes the required HTML and JavaScript that needs to be included within a web object within the project.

The generated PDF certificate is very basic at the moment, you'll need to refer to the pdfmake documentation to update the design.

Feel free to have a look and re-use if it's useful.

(21/03/2022 - pdfmake updated to v0.2.4 and example link replaced)

90 Replies
Kate Mackenzie

function createPDF() {
var D1 = '';
var D2 = '';

var player = parent.GetPlayer();

D1 = player.GetVar("firstname");
D2 = player.GetVar("lastname");
//var docDefinition = { content: 'This is an sample PDF printed with pdfMake for '+name+"!" };
var docDefinition = {

content: [

{
table: {
heights: [100,100,100,100],
widths: [500],
headerRows: 3,
body: [
['Certificate of completion'],
[HOW THE HELL DO I GET THE VARIABLES HERE?],
['completed the eSafety Early Years module'],
['We SAY and SHARE with technology'],
]
},
style: 'header',
alignment: 'center',
layout: 'noBorders'
},

Phil Mayor

I would build outside of a table:

{
text: "Certificate of completion",
bold: true,
color: '#3e313e',
fontSize: 14,
bold: true,
alignment: 'left',
// margin: [left, top, right, bottom]
margin: [0, 25, 0, 0]
},

{
text: D1 + " " D2,
bold: true,
color: '#3e313e',
fontSize: 11,
alignment: 'left',
// margin: [left, top, right, bottom]
margin: [0, 5, 0, 0]
},
{
text: "completed the eSafety Early Years module",
//bold: true,
color: '#8B8B8B',
fontSize: 11,
alignment: 'left',
// margin: [left, top, right, bottom]
margin: [0, 0, 0, 0]
},

{
text: "We SAY and SHARE with technology",
bold: true,
color: '#3e313e',
fontSize: 11,
alignment: 'left',
// margin: [left, top, right, bottom]
margin: [0, 10, 0, 0]
},

You way this should work 

{
table: {
heights: [100,100,100,100],
widths: [500],
headerRows: 3,
body: [
['Certificate of completion'],
[D1 + " " D2],
['completed the eSafety Early Years module'],
['We SAY and SHARE with technology'],
]
},
style: 'header',
alignment: 'center',
layout: 'noBorders'
},

 

 

 

Doug Dewan

Hi Ryan, not sure what we are doing wrong. We followed all the steps but still can't seem to make the course work to produce the certificate when published to Articulate Review, or the LMS sandbox we use. The only difference I can see from your example Story file and ours is on the "Success" layer of your last slide there is a "web object" but I'm not seeing in the steps how this Web Object is established in the Articulate Story file. Would you be able to provide clarity on how we can establish this?

Thank you in advance!

Doug

Ryan Lowry

Hi Doug, the Source.zip file attached to the original post contains a folder certificate with the required files for the web object that you'll need to insert. In the example it is inserted on the success layer of the results slide but you can insert it anywhere you see fit.

Insert > Web Object > Select "certificate" folder

Doug Dewan

Hi Ryan, sorry to bother you again, I'm still having trouble getting this to work on our end. If I publish your course example to 360 Review or our Internal LMS it works fine, but the course I built doesn't. Of course my web address is different from yours but I'm also not clear on how your example is pulling the certificate from that address and incorporating it into your .Story file. Would the problem be with my Java script or the location of my file or something else. I'm at a loss. 

Below is the JS we wrote:

var player = GetPlayer();
 
var date = new Date();
var day = String(date.getDate()).padStart(2, '0');
var month = String(date.getMonth() + 1).padStart(2, '0');
var year = date.getFullYear();
 
var issuedOn = month + '/' + day + '/' + year;
var expiresOn = month + '/' + day + '/' + (year + 3);
var uName = player.GetVar("uName"); 
 
var doc = new jspdf.jsPDF({
    orientation: 'portrait'
});
 
var img = new Image;
img.onload = function () {
    doc.addImage(this, 'png', 0, 0, 230, 305);
    doc.setFontSize(10);
 
    doc.setTextColor(77, 77, 79);
    doc.setFont('Helvetica', 'normal');
 
    doc.text(uName.trim(), 19, 31, null, null, 'left');
    doc.text(issuedOn, 32, 43, null, null, 'left');
    doc.text(expiresOn, 32, 50, null, null, 'left');
 
    doc.save("Certificate.pdf");
};
 
img.crossOrigin = "";
img.src = "certificate.png";

Ryan Lowry

Hi Doug, there are 2 parts to this, a JavaScript trigger in Storyline and then an embedded web object. The trigger in storyline calls a JS function within the embedded web object which generates the PDF.

In my example you'll see that there is an embedded web object on the Success layer of the Results slide. This embedded web object isn't pointed at a URL but at a local folder on my computer called certificate, a copy of which you can find in the Source.zip file attached to the original post. When you publish a project, Storyline embeds all of the files within the local folder in the published output. Full details on adding web objects can be found here. You'll see there is also a JavaScript Trigger on the Success layer, this calls the function generatePDF() found in certificate.js in the embedded web object.

You need to do something similar in your own project. I'd suggest copying my setup exactly before modifying it as you see fit. I note you are using jsPDF rather than PDFMake to generate the PDF file, either should work, I honestly can't remember why I chose PDFMake over jsPDF.

Where have you placed the code you shared?

Ryan Lowry

Doug, looking at the .story file you've provided I think you are mixing two different methods of generating a PDF in Storyline. Were you following this tutorial by any chance? Rather than embedding a web object which contains the files required to generate a PDF you need to edit the published output and modify it to include the files required to generate the PDF. This is described towards the end of the tutorial under the heading "Publish and Modify the eLearning Output".

If you wish to follow the method outlined in this post you'd need to copy the JavaScript trigger as found in the .story file in Source.zip e.g.

// Name of the certificate html file

var certFilename = 'certificate.html';

// HTMLCollection of elements of type iFrame

var iframeElements = document.getElementsByTagName("iframe");

// Iterate over the iFrameElements HTMLCollection

for (var i = 0; i < iframeElements.length; i++) {

  /* If src of current iFrame element equals the filename set in variable

     ** certFilename call the generatePDF() function.

     */

    var src = iframeElements[i].getAttribute('src');

    if (src.indexOf(certFilename) != -1) {

        iframeElements[i].contentWindow.generatePDF();

    }

}
Doug Dewan

Hi Ryan, sorry I think you are correct. We are working with IT and believe I have misdirected them showing two methods.
Ideally we would like a solution that does not require modifying files each time so believe your solution is the best for that as it addressed it for each publishing with out having to add files after, is this correct?