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.

47 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

Sarah Scott

Hey John! Did you ever get around to posting the article about a multi-page PDF? I successfully have a single page PDF download functionality with learner input and a background image, but I want to have a second page with a different background image and I haven't quite got the code right. Hoping to see yours as a model! Thanks!

John Cooper

Hi Sarah,

I did get the multi-page pdf creation working BUT, as I explained above, I don't use the jsPDF library anymore. I use pdf-lib because:

  1. This reads a pdf form and fills the form in (i.e. no background image and then working out where everything goes) the pdf form can be as many pages as you like.
  2. You can change the pdf form without having to modify the JavaScript (as long as the form field names remain the same)
  3. It's way simpler to code and quicker to implement
  4. You can load the pdf-lib dynamically (i.e. you don't have to change the story.html file after publishing

There is a detailed example and the code is explained in this article:

Creating downloadable pdf files in Storyline - an update on earlier methods - Articulate Storyline Discussions - E-Learning Heroes

I know it sounds like a backward step to change your code if you have it partially working but, believe me, you won't regret changing to pdf-lib it is much more powerful and so much quicker to code and use.

Best regards, John

Sara Bean

I have been able to get this to work with outstanding results. The only things I am still struggling with, and it seems like a small detail but will impact the UX in the end, is text wrapping. 

I know how to set  text wrapping in Storyline and I know how to set text wrapping in Adobe. Somehow, the two aren't working together. When the text comes over in the PDF, if I set the Adobe form as "Auto" size and "Multi line", text that would normally overflow and, therefore, wrap, does not. It just disappears. 

I've tried looking at some forums to paste some additional script into storyline to help prior to it coming over but then the form doesn't execute at all.

Anyone have anything like this happen and, if so, what did you do?

John Cooper

Hi Sara

When I started this thread I was using the jsPDF library to create pdf's - for reasons explained in this thread (and others I have posted) I now use pdf-lib.

Can I just check? You mention the 'Adobe Form' so I'm guessing this question relates to using pdf-lib?

I don't know the answer to your question, but I'm also hoping someone else might be ableto suggest a solution.

AS I understand it, the problem is that, when setting the field properties in the receiving pdf form, these would appear to be the options:

I know there are problems if we try and set the "Allow Rich Text Formatting" flag - and my understanding (but I may be wrong) is that the pdf-lib JavaScript library just doesn't support RTF. In which case this might be related to your issue.

Basically, you lose all formatting when you capture the text entry in Storyline and then write it to the pdf form field via pdf-lib. Meaning this is not an approach that is really suited to long-form answers. THIS IS NOT TRUE - SEE POST BELOW

In our design we just stick to short (one paragraph) answer type questions in Storyline.

BUT - this does raise an interesting challenge of how to overcome this restriction if anyone is up for it??

Sara Bean

Hi John,

Thanks for the speedy response. 

I have read the other thread in detail - SO MUCH  great stuff there. And I am using pdf-lib for all the reasons you mentioned here and on that thread.

The boxes I have checked in the dialog above are "Check spelling" (not that it applies to this issue) and "multi-line". "Scrolling" is not checked, nor is "Rich text", as I understand those cause issues.  

My submissions are a mix between short form and long form. 

I too put the challenge out to the group!

John Cooper

In the post I removed I was looking at the output text in a demonstration I was working on and, in my case, line breaks were not being transferred to the output pdf...

...and I was trying to explain this - BUT then I realised, the demo I was testing was a RISE course where the text input was being stored in SCORM 2004 memory before being retrieved and then written to the pdf - and, in this case, I was losing the line breaks.

When I tested on a straightforward demo just using Storyline, the line breaks appear fine. I obviously have more work to do on my RISE demo. :)

It would appear that plain text formatting (NOT rich text formatting) gets transferred to the output pdf. So my earler comments about losing ALL formatting are WRONG - you just can't use Rich Text formatting.

Sorry

 

Sara Bean

John! It worked! Thank you!!!! Every other forum (regarding Adobe) suggested NOT checking "scrolling" so I didn't, thank you for including this. 

This wraps up a long, interesting learning experience that once again showed me the value of this forum. 

Now that it is "fixed", I'm including a link to a resource that I made for my team showing how to do this. I'm a senior instructional designer at New York Life and often train my team on different bits and bobs of the technical aspects of the job so I put this little thing together for them and held a short training session. 

I'll put a link to download the .story and the PDF file in the comments on Review. 

https://360.articulate.com/review/content/3b3e97ba-fa9a-4dcd-a378-aba59cde4898/review