I am developing an elearning course with the possibility of downloading certificate after successful completion of the course. I am using jsPDF and everything works OK, I am able to copy the variable from the course to pdf, I am able to save the pdf, but I ran into problem when I try to add a background image.
I am not an expert in JavaScript, so I am following the documentation properly. I am able to load the scripts into html, the javascript code works perfectly except the addimage function, it always gives the "Cannot read property 'addImage' of undefined" and crashes the script.
This is the javascript I am using:
var player = parent.GetPlayer();
var doc = new jsPDF('p', 'mm', 'a4');
var certif = player.GetVar("cert");
if (certif == 1) {
parent.backgroundimage ();
}
var namename = player.GetVar("input1");
doc.setFontSize(18);
doc.setTextColor(0,0,0);
doc.setFontStyle("bold");
doc.text(17, 55, namename);
doc.save('Certificate.pdf');
Here is separate javascript (image.js) for the backgroundimage function which I saved separately because Articulate Storyline does not directly support Base64.
function backgroundimage() {
var imgData = 'data:image/jpeg;base64,... string with over 120.000 characters'
doc.addImage(imgData, 'JPEG', 0, 0, 210, 223)
}
And here are the scripts I am including in the head of the html:
<script src="https://unpkg.com/jspdf#latest/dist/jspdf.min.js" type="text/javascript"></script>
<script src="https://unpkg.com/jspdf#1.5.3/dist/jspdf.debug.js" type="text/javascript"></script>
<script src="image.js" type="text/javascript"></script>
Everything seems to work OK, I can create, save the pdf, position the text, I can call the function "backgroundimage" (tested it with console.log) I only get an error when I include the doc.addimage. I have tested with the addimage.js included in jspdf but without result. I used downloaded javascripts, and I have tried with jspdf.js only, probably all the possibilities I could think of, but no result.
Related
I've added the jsPDF library to my Titanium project to generate PDFs client side, which has been working great. But now I want to localize the app for Arabic countries, which means that I have the add a custom font. This works perfectly if you use doc.save('file.pdf'), but it doesn't seem to work correctly for doc.output(). I have to use output because I'm using jsPDF outside of a browser.
To make the library work in Titanium I've had to strip all of the references to window, because it's not running in a browser or webview.
I've tried writing the file from different sources, but nothing seems to yield any results.
My current implementation:
doc = new jsPDF();
var f = Ti.Filesystem.getFile(Ti.Filesystem.resourcesDirectory, 'fonts/markazi-text.regular.ttf');
var contents = f.read();
var base64font = Ti.Utils.base64encode(contents).toString();
doc.addFileToVFS("MarkaziText-Regular", base64font);
doc.addFont('MarkaziText-Regular', 'markazi-text', 'normal');
doc.setFontSize(20);
doc.setFont('markazi-text', 'normal');
doc.text('The quick brown fox jumps over the lazy dog', 20, 20);
var tempFile = Ti.Filesystem.getFile(Ti.Filesystem.getTempDirectory(), 'report.pdf');
if (tempFile.exists()) {
tempFile.deleteFile();
}
tempFile.write(doc.output());
I've also tried to write the file from a blob:
var reader = new FileReader();
reader.onloadend = function () {
tempFile.write(reader.result);
};
reader.readAsText(getBlob(buildDocument()));
But the pdf is empty if I use this. I've also tried the library in a webview within a titanium application, which does work but I don't really want to go that road. It would require too many changes to the code.
Expected:
Actual:
I've finally resolved it by creating a local HTML file. In this HTML file I've loaded jsPDF and my own JavaScript to generate a PDF file. I've loaded this HTML file in a WebView.
I'm generating all the data needed for the PDF in an Alloy controller. I'm sending this data to my WebView JavaScript by firing an app event and catching it in the WebView.
After the PDF is created I trigger an app event in the WebView that contains the base64 data of the jsPDF doc:
Ti.App.fireEvent('app:pdfdone', {
output: doc.output('dataurlstring').replace("data:application/pdf;filename=generated.pdf;base64,", "")
});
I finally save this as a file in the Alloy controller:
var f = Ti.Filesystem.getFile(Ti.Filesystem.getTempDirectory(), 'doc.pdf');
f.write(Ti.Utils.base64decode(e.output));
I'm using jsPDF (https://parall.ax/products/jspdf, https://github.com/MrRio/jsPDF) to produce dynamic PDFs in a web application.
It works well, but I'd like to figure out whether it's possible to use Google web fonts in the resulting PDF.
I've found a variety of links that are related to this question (including other questions on SO), but most are out of date, and nothing looks definitive, so I'm hoping someone clarify whether/how this would work.
Here's what I've tried so far, with no success:
First, load the font, and cache it as a base64-encoded string:
var arimoBase64;
var request = new XMLHttpRequest()
request.open('GET', './fonts/Arimo-Regular.ttf');
request.responseType = 'blob';
request.onload = function() {
var reader = new FileReader();
reader.onloadend = function() {
arimoBase64 = this.result.split(',')[1];
}
reader.readAsDataURL(this.response);
};
request.send()
Next, create the pdf doc:
doc = new jsPDF({
orientation: "landscape",
unit: "pt",
format: "letter"
});
doc.addFileToVFS("Arimo-Regular.ttf", arimoBase64);
doc.addFont("Arimo-Regular.ttf", "Arimo Regular", "normal");
doc.setFont("Arimo Regular", "normal");
doc.text("Hello, World!", 100, 100);
doc.save("customFontTest");
When the PDF is saved - if I view it in my browser - I can see the custom font. However - if I view it using Adobe Reader or the Mac Preview app - the fonts are not visible.
I assume that's because the font is rendered in the browser using the browser's font cache, but the font is not actually embedded in the PDF, which is why it's not visible using Adobe Reader.
So - is there a way to accomplish what I'm trying to do?
OK - I finally figured it out, and have gotten it to work. In case this is useful for anyone else - here is the solution I'm using...
First - you need two libraries:
jsPDF: https://github.com/MrRio/jsPDF
jsPDF-CustomFonts-support: https://github.com/sphilee/jsPDF-CustomFonts-support
Next - the second library requires that you provide it with at least one custom font in a file named default_vfs.js.
That file should look like this:
(function (jsPDFAPI) {
"use strict";
jsPDFAPI.addFileToVFS("[Your font's name]","[Base64-encoded string of your font]");
})(jsPDF.API);
I'm using two custom fonts - Arimo-Regular.ttf and Arimo-Bold.ttf - both from Google Fonts. So, my default_vfs.js file looks like this:
(function (jsPDFAPI) {
"use strict";
jsPDFAPI.addFileToVFS("Arimo-Regular.ttf","[Base64-encoded string of your font]");
jsPDFAPI.addFileToVFS("Arimo-Bold.ttf","[Base64-encoded string of your font]");
})(jsPDF.API);
There's a bunch of ways to get the Base64-encoded string for your font, but I used this: https://www.giftofspeed.com/base64-encoder/.
It lets you upload a font .ttf file, and it'll give you the Base64 string that you can paste into default_vfs.js.
You can see what the actual file looks like, with my fonts, here: https://cdn.rawgit.com/stuehler/jsPDF-CustomFonts-support/master/dist/default_vfs.js
So, once your fonts are stored in that file, your HTML should look like this:
<script src="js/jspdf.min.js"></script>
<script src="js/jspdf.customfonts.min.js"></script>
<script src="js/default_vfs.js"></script>
Finally, your JavaScript code looks something like this:
const doc = new jsPDF({
unit: 'pt'
});
doc.addFont("Arimo-Regular.ttf", "Arimo", "normal");
doc.addFont("Arimo-Bold.ttf", "Arimo", "bold");
doc.setFont("Arimo");
doc.setFontType("normal");
doc.setFontSize(28);
doc.text("Hello, World!", 100, 100);
doc.setFontType("bold");
doc.text("Hello, BOLD World!", 100, 150);
doc.save("customFonts.pdf");
This is probably obvious to most, but in that addFont() method, the three parameters are:
The font's name you used in the addFileToVFS() function in the default_vfs.js file
The font's name you use in the setFont() function in your JavaScript
The font's style you use in the setFontType() function in your JavaScript
You can see this working here: https://codepen.io/stuehler/pen/pZMdKo
Hope this works as well for you as it did for me.
I recently ran into this same issue, but it looks like the jsPDF-CustomFonts-support repo was rolled into MrRio's jsPDF repository, so you no longer need it to get this working.
I happen to be using it in a React App and did the following:
npm install jspdf
Create a new file fonts/index.js (Note: You can download the Google Font as a .ttf and turn it into the Base64 encoded string using the tool in mattstuehler's answer)
export const PlexFont = "[BASE64 Encoded String here]";
Import that file where you need it:
import jsPDF from 'jspdf';
import { PlexFont } from '../fonts';
// Other Reacty things...
exportPDF = () => {
const doc = new jsPDF();
doc.addFileToVFS('IBMPlexSans-Bold.ttf', PlexBold);
doc.addFont('IBMPlexSans-Bold.ttf', 'PlexBold', 'normal')
doc.setFont('PlexBold');
doc.text("Some Text with Google Fonts", 0, 0);
// Save PDF...
}
// ...
Just wanted to add an updated answer - for version 1.5.3:
Convert the font file to base64 = https://www.giftofspeed.com/base64-encoder/
const yanone = "AAWW...DSES"; // base64 string
doc.addFileToVFS('YanoneKaffeesatz-Medium.ttf', yanone);
doc.addFont('YanoneKaffeesatz-Medium.ttf', 'YanoneKaffeesatz', 'normal');
doc.setFont('YanoneKaffeesatz');
So, I have dynamically created a pdf, and now i want to print it:
var doc = new jsPDF();
var name = "Doe, John"
doc.setFontType("normal");
doc.setFontSize(12);
doc.text(20,20,'Name: '+ name);
//do something that prints the pdf...
So, how do I take this doc variable and print it. Everywhere else I found uses the url of the pdf. Do I need to create a url for it first?
So, the solution I am currently going with is to show the pdf in a new tab/window, from which the pdf can be printed.
window.open(doc.output('datauristring'));
Unfortunately, this only works in Chrome. Anyone know how to get it working in IE, Firefox, Safari, etc.?
I still wonder if there is a way to skip this step (of opening the pdf and then requiring another button to be pushed).
There is a method autoPrint() provided by jsPDF library.
You can use that as shown below
var doc = new jsPDF();
var name = "Doe, John"
doc.setFontType("normal");
doc.setFontSize(12);
doc.text(20,20,'Name: '+ name);
doc.autoPrint();
//This is a key for printing
doc.output('dataurlnewwindow');
Try this:
window.open(doc.output('bloburl'), '_blank');
This can have problems with adblock sometimes.
So, in conclusion, for Chrome and Safari, use
window.open(doc.output('datauristring'));
but for IE and Firefox, use
doc.save();
These will both allow you to open the pdf in a new window, from which it can be printed. For those who have taken the time to figure out what is necessary on other browsers, feel free to add your research here...
All you need is to add this
doc.save('Test.pdf');
(I suppose you already have all the above written code inside the button trigger which user clicks to get pdf)
You better implement "printjs" from npm, convert jspdf document to blob and print it like this
const data = PDF.output('blob')
const blobUrl = URL.createObjectURL(data);
printJS(blobUrl);
After requested a Web-service, i get the following answer (PDF file Streamed)
%PDF-1.5
%ยตยตยตยต
1 0 obj
<</Type/Catalog/Pages 2 0 R/Lang(en-GB) /StructTreeRoot 10 0 R/MarkInfo<</Marked true>>>>
endobj
2 0 obj
<</Type/Pages/Count 1/Kids[ 3 0 R] >>
endobj
3 0 obj
................. continue .......................
So far, i can generate the pdf file using windows.open(application/pdf;base64, url).
What i really need is to save the pdf document in the storage!
How can i generate the PDF file with the response above?
Tks
I do something similiar with my site at http://dogfeatherdesign.com/ttn_js, except I'm going from Canvas to pdf files, when the user hits a print button. Depending on which OS and browser, the download happens automatically. (Note: Safari is NOT good for this.) I'm using the libraries html2canvas and jsPDF
Here's my code:
$('#dte_onPrintClick').on('click', function(e) {
e.preventDefault();
html2canvas($("#canvas_dte"), {
onrendered: function(canvas) {
var imgData = canvas.toDataURL(
'image/png');
var doc = new jsPDF('p', 'mm', paperInfo.paperSize);
doc.addImage(imgData, 'PNG', 10, 10);
doc.save('output-file.pdf');
}
});
});
now obviously you are going from pdf to pdf, but I'm guessing the doc = new jsPDF(), doc.addXXX, doc.save(filename.pdf) might have some merit for you.
Update:
Without specifics on the pdf feed, I'm not quite sure how to be more helpful, except to say that what you want to do should be achievable on most modern browsers. You CAN save the pdf content as a file withOUT user input, via the .save() call listed above (in many, but not all browsers).
If its a hack you are looking for, you could catch the pdf stream, save it as an image in a hidden div, then convert it back to pdf and do the doc.save() function. You'd have to add lengthy time delays to ensure the feed finishes, and the image populates, etc, but it should be do-able.
I do see a putstream() function, getBlob() function, and getArrayBuffer() function to jsPDF library, so its quite possible one of those would work directly to help catch the pdf and generate a stored document, all on the fly via JavaScript.
Edit #2: Heres the immediate download, no user interaction jsfiddle page. I'm actually surprised this works that way, but so be it. I agree, normally a file save requires user intervention, but this download thing just works. Frankly I'm surprised at this too.
I got a a script wherein I want to convert the extension to another one then open it using a specific application.
For instance, I got .mht file located in my Desktop. An html file with internal javascript on it.
What I want to happen is when I open the HTML file on internet explorer and click on the hyperlink, it should convert .mht file into .docx and open it using Microsoft Word. Unfortunately, my below code does not work, if i click the hyperlink, it does opens up Microsoft Word but giving me an error message saying that the file cannot be found. Can someone assist me with this please? Thanks in advance, will much appreciated.
<HTML>
<HEAD>
<script type="text/javascript">
<!--
function openDocument(file)
{
try
{
var Word = new ActiveXObject("Word.Application")
var file;
file = file.split(".");
file = file[0]+".docx";
Word.Visible = true
Word.Documents.Open(file)
}
catch(exception)
{
if(Word)
{
alert(exception.message)
Word.Quit()
}
window.location.href = file
}
}
// -->
</script>
<TITLE>Launch Word - Local</Title>
</HEAD>
<BODY>
Summary
</BODY>
</HTML>
For security purpose, Javascript can't write on the filesystem, so it is impossible to change the extension of a file.
You should be able to do something with the new FileSystem API (check that tutorial : http://www.html5rocks.com/en/tutorials/file/filesystem/), but it is available only on Chrome (http://caniuse.com/#feat=filesystem)
EDIT:
With an ActiveX, using GetFile may work:
var fso = new ActiveXObject("Scripting.FileSystemObject");
var file = fso.GetFile("c:\\myfile.mht");
file.name = "newName.newExt";