Creating a pdf file from Storyline running under an LMS

Feb 17, 2022

I know there are many excellent examples of how to create a pdf file in Storyline using javascript libraries such as jspdf and I have done this successfully in projects compiled for the web...

...but has anyone tackled this with an lms version? I know you could pass data to the lms using xAPI - but I really just want to click a button and "save" the pdf file created within the course using variables collected during the course.

Any ideas would be welcome.

37 Replies
John Cooper

Hi Ariff

Can I ask you what your "src =   " statement looks like in the index_lms.html file?

You have two option for loading the require jsPdf JavaScript library for your JavaScript code. You can load the latest source library at run-time from one of the on-line source code repositories - something like this:

<script src="https://unpkg.com/jspdf@latest/dist/jspdf.min.js"></script>

OR

if you have downloaded a version of the library containing the code you need then you need to add that file to the compiled storyline files and include:

 <script src="story_content/jspdf.debug.js"></script>
 
Where the file jspdf.debug.js has been copied to the "story_content" folder in the compiled version.
 
SO - check where your javascript library file is located and make sure the src = is pointing to the right folder.....
 
NOTE: the reason many of us are using the latter "jspdf.debug.js" file is that it is probably V1.5.2 of the library where inclusion of the javaScript modules was done using a "require" statement. After V1.5.2 the jsPdf library changed to a different (more modern) module format ES6. In this case the modules are added using an import statement. I haven't got round to testing my code with the ES6 module format - and I suspect others haven't either. 
Fiona Cross

Hi John, after 4 hours of trial and error, i've figured it out.

Apparently jsPDF versions after v1.5.3 simply do not work. I've tested each version working backwards from the most recent (as of this post v2.5.1) and found that v1.5.3 is the most recent one that works.

I was stumped and ended up in a google rabbit hole before stumbling across another thread on the web about trying older versions. 

From the changelog for v2.0.0: 

Modernized the output bundles: there are now bundles for ES modules, UMD and a special node version. We renamed the files in dist for consistency: jspdf.debug/min.js is now jspdf.umd(.min).js. We also changed the name of the global variable to jspdf (lower case) when using script tags to be consistent with the new es modules format and named imports/exports. For backwards compatibility add this line:
window.jsPDF = window.jspdf.jsPDF

Yet whenever i add the line "window.jsPDF = window.jspdf.jsPDF" it still doesn't work. So i've just decided to simply use v1.5.3 and baking it locally within the scorm file.

Have you had much success with any of the more recent versions of jsPDF?

Jürgen Schoenemeyer

>Yet whenever i add the line "window.jsPDF = window.jspdf.jsPDF" it still doesn't work

have you tested with the UMD (Universal Module Definition) Version?

the UMD Version can used in enviroments without modules ("Raw <script> loading") - like storyline javascript trigger

result in the browser console

jsPDF (v2.5.1) is initialized

John Cooper

Hi Ariff

No. As per my earlier post, I just hadn't got round to testing my code with the later versions of jsPdf. Like you, I used v1.5.2 and, as you say, 'baked' that into the SCORM code.

I may take a look at it and see if I can get the UMD version working. Hopefully, you have got your code working OK now? Albeit with V1.5.3.

I'm also interested in the JavaScript library pdf-lib as this allows you to fill in a pdf form which would be really useful.

If I get anywhere useful I will post here.

Regards, John

John Cooper

OK - So I finally got round to looking at the JavaScript library pdf-lib.

The reason I was interested in this library as opposed (or as well as) jsPdf is that it provides code that can modify an existing pdf or, importantly, fill in a pdf form.

The latter is what I was really after. I create a lot of Storyline courses that capture learner input throughout the course and then they provide a button to allow the learner to download the course notes (including their input) as a pdf at the end of the course. There is a demo on our website:

Downloadable Learner Notes (profilelearning.com)

This demo - and the courses we have done so far use the jsPdf library. The multipage pdf is created by adding each page as a png image and then using x and y coordinates (read from a table) to position the learner's input on each page. As you can see from the demo, this works just fine. BUT it is a bit of a chore working out and adjusting the position of each text block and truncating the text if it exceeds the character count.

Filling in a pdf form would make this MUCH easier.

As per the discussion above I also wanted to experiment incorporating 'modern' JavaScript modules into Storyline.

I have got the first test working - I have created a simple form with three text fields, created a single storyline screen to capture three input fields and then used pdf-lib routines to fill in the pdf form

Developing Others 1 (profilelearning.com)

So far so good - this works for web publishing - and it is using the latest pdf-lib ES6 modules. I will now test it as a SCORM package and make sure I can make that call to the ES6 modules work.

I will post my findings

 

John Cooper

So, as per above, I have managed to create a downloadable pdf incorporating user input by using the JavaScript pdf-lib library. The pdf is a blank pdf form copied to the root folder,  which is then filled out by a JavaScript routine using input from the learner. This could be used to fill out an action plan, or create a certificate, or complete some kind of survey.

The other thing is that this demo (see the second link above) uses ESM JavaScript module libraries only (so-called 'modern' JavaScript) - so it uses the pdf-lib and tiny-save-as libraries to create the filled out pdf form and download it.

The consequence of this is that I have been able to load the JavaScript libraries dynamically using the recently added Import () JavaScript construct. i.e. the JavaScript libraries are loaded when Storyline executes the JavaScript - meaning you do not have to edit the story.html or lms_index.html files after publishing the course.

Overall, I'm quite pleased with myself!

Allisa Cardella

Hello Matthew, 

That sounds awesome! It has given me an idea for a survey that I'm doing where I was using a traditional Likert scale resulting in the information being very crowded & not much room for anything else. Do you mind me asking how you were able to use the slider & have it transfer to the PDF?

John Cooper

Hi Mark

I haven't loaded jsPDF without modifying the story.html file - but that's because I don't use jsPDF anymore. I prefer to use the pdf-lib library which is more modern and more powerful (see discussion above). Because this library complies with the later ES6 module package format, you can actually load it dynamically FROM WITHIN THE JAVASCRIPT using an IMPORT statement:

async function loadMods() {
await import("https://unpkg.com/pdf-lib/dist/pdf-lib.js");
}

NOTE: This should really have some error handling in case it doesn't load,,, but that's straightforward.

Including this in the JavaScript code loads the library from (In this case) the unpkg.com repository at run time. I use an async function to call the 'import' statement so I can use  'await'  which will ensure the library is loaded before progressing.

There were a few heroes who criticised this method of loading libraries - but it works fine for me - no noticeable delays. AND, a big plus, it works when using RISE - no messing around with the published code.

With regard to Michael's comment about the size of javaScript libraries, using the Import statement means I can selectively load just the methods I need eg:

import { PDFDocument } from 'https://unpkg.com/pdf-lib';

which means it loads even faster.

Regards