Iām trying to convert XML data into PDF files from a web page and I was hoping I could do this entirely within JavaScript. I need to be able to draw text, images and simple shapes. I would love to be able to do this entirely in the browser.
I've just written a library called jsPDF which generates PDFs using Javascript alone. It's still very young, and I'll be adding features and bug fixes soon. Also got a few ideas for workarounds in browsers that do not support Data URIs. It's licensed under a liberal MIT license.
I came across this question before I started writing it and thought I'd come back and let you know :)
Generate PDFs in Javascript
Example create a "Hello World" PDF file.
// Default export is a4 paper, portrait, using milimeters for units
var doc = new jsPDF()
doc.text('Hello world!', 10, 10)
doc.save('a4.pdf')
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.5/jspdf.debug.js"></script>
Another javascript library worth mentioning is pdfmake.
pdfmake playground
pdfmake on github
The browser support does not appear to be as strong as jsPDF, nor does there seem to be an option for shapes, but the options for formatting text are more advanced then the options currently available in jsPDF.
I maintain PDFKit, which also powers pdfmake (already mentioned here). It works in both Node and the browser, and supports a bunch of stuff that other libraries do not:
Embedding subsetted fonts, with support for unicode.
Lots of advanced text layout stuff (columns, page breaking, full unicode line breaking, basic rich text, etc.).
Working on even more font stuff for advanced typography (OpenType/AAT ligatures, contextual substitution, etc.). Coming soon: see the fontkit branch if you're interested.
More graphics stuff: gradients, etc.
Built with modern tools like browserify and streams. Usable both in the browser and node.
Check out http://pdfkit.org/ for a full tutorial to see for yourself what PDFKit can do. And for an example of what kinds of documents can be produced, check out the docs as a PDF generated from some Markdown files using PDFKit itself: http://pdfkit.org/docs/guide.pdf.
You can also try it out interactively in the browser here: http://pdfkit.org/demo/browser.html.
Another interesting project is texlive.js.
It allows you to compile (La)TeX to PDF in the browser.
For react fans there is another great resource for PDF generation: React-PDF
It is great for creating PDF files in React and even let the user download them from the client side itself with no server required!
this is a small example snippet of React-PDF to create a 2 section PDF file
import React from 'react';
import { Page, Text, View, Document, StyleSheet } from '#react-pdf/renderer';
// Create styles
const styles = StyleSheet.create({
page: {
flexDirection: 'row',
backgroundColor: '#E4E4E4'
},
section: {
margin: 10,
padding: 10,
flexGrow: 1
}
});
// Create Document Component
const MyDocument = () => (
<Document>
<Page size="A4" style={styles.page}>
<View style={styles.section}>
<Text>Section #1</Text>
</View>
<View style={styles.section}>
<Text>Section #2</Text>
</View>
</Page>
</Document>
);
This will produce a PDF document with a single page. Inside, two different blocks, each of them rendering a different text. These are not the only valid primitives you can use. you can refer to the Components or Examples sections for more information.
It is worth mentioning PDF-LIB which is an awesome library:
Supports pure JavaScript.
Can edit existing PDF templates even with pure JavaScript. (Most impotently. Many JavaScript libraries can't do it)
It is generating a PDF with select-able/copy-able/highlight-able text not an image file inside an PDF like many other libraries generate.
More easy to use. (I love it)
If you are interested in using it with pure JavaScript this may
help.
If you are interested to do the same with the most popular JavaScript
library as of now JSPDF this may help. (Simply JSPdf can't do most time saving thing we want, editing an existing template.)
See how pretty the code is
<script type="text/javascript">
async function downloadPdf() {
const url = './print-templates/pquot-template.pdf';
const existingPdfBytes = await fetch(url).then(res => res.arrayBuffer());
// Getting the document
const pdfDoc = await PDFLib.PDFDocument.load(existingPdfBytes);
// Getting the first page
const pages = pdfDoc.getPages();
const firstPage = pages[0];
// Customer name
firstPage.drawText('Customer name is here with more text (GAR004) quick brown customerm jumps over lazy dog.', {
x: 10.5*9,
y: 76.6*9,
size: 10,
maxWidth: 28*9, // Wrap text with one line. WOW :O
lineHeight: 1.5*9
});
// Currency short code
firstPage.drawText('LKR', {
x: 10.5*9,
y: 73.5*9,
size: 10
});
var itemName = 'Here is the item name with some really really long text and quick brown fox jumps over lazy dog. long text and quick brown fox jumps over lazy dog:)';
// Item name
firstPage.drawText(itemName, {
x: 5*9,
y: 67*9,
size: 10,
maxWidth: 31*9,
lineHeight: 2*9
});
const pdfDataUri = await pdfDoc.saveAsBase64({ dataUri: true });
document.getElementById('pdf').src = pdfDataUri;
}
</script>
UPDATE: Free service no longer available. But there is a reasonably priced service you can use if you need something in a crunch and it's should be reliable.
https://pdfmyurl.com/plans
You can use this free service by adding a link which creates pdf from any url (e.g. http://www.phys.org):
http://freehtmltopdf.com/?convert=http%3A%2F%2Fwww.phys.org&size=US_Letter&orientation=portrait&framesize=800&language=en
Even if you could generate the PDF in-memory in JavaScript, you would still have the issue of how to transfer that data to the user. It's hard for JavaScript to just push a file at the user.
To get the file to the user, you would want to do a server submit in order to get the browser to bring up the save dialog.
With that said, it really isn't too hard to generate PDFs. Just read the spec.
Related
I'm in the middle of migrating some saved editor text/data from another rich text editor (Quill) to using Draft.js. The data exist in files in a format digestible by the current text editor, and I would like to transform these files to Draft.js format.
All of the rich text editors have some sort of data-to-html and html-to-data conversion functions, which come in handy here.
My plan was to convert the current editor text/data to html using a converter of the current editor, and then convert the resulting html to the format supported by Draft.js using some converter from the Draft.js package.
However, it seems (I could be wrong) that all of the Draft.js converters require the browser in some form or another, so I'm unable to this as a backend/server-side application only. Is there a tool that I'm missing and/or is there another type of solution to this?
TL;DR:
editor_data --(phase 1)--> html --(phase 2)--> Draft.js_data
phase 2 seems to require browser, I would like to not involve browser
convert data from quill to markdown and then convert markdown back to draftjs accepted format, below code is an example of export and can be executed using node, follow the same logic for import
quill --> markdown --> draftjs
https://github.com/sstur/draft-js-utils
const {
convertFromRaw
} = require('draft-js');
const {stateToMarkdown} = require('draft-js-export-markdown');
const {draftToMarkdown} = require('markdown-draft-js');
const converter = () => {
const sampleMarkup =
{"blocks":[{"key":"dsf1t","text":"ššš¾ this isn't trueeeeeeeeeeeeeeeee","type":"unstyled","depth":0,"inlineStyleRanges":[],"entityRanges":[{"offset":0,"length":1,"key":0},{"offset":1,"length":2,"key":1}],"data":{}}],"entityMap":{"0":{"type":"emoji","mutability":"IMMUTABLE","data":{"emojiUnicode":"š"}},"1":{"type":"emoji","mutability":"IMMUTABLE","data":{"emojiUnicode":"šš¾"}}}} ;
const blocksFromHTML = convertFromRaw(sampleMarkup);
// const state = ContentState.createFromBlockArray(blocksFromHTML);
console.log('state: ', stateToMarkdown(blocksFromHTML));
console.log('state: ', draftToMarkdown(sampleMarkup));
}
converter();
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');
I have a image folder say answers where I am storing answer for my question which are images
There are 200 images for now in that folder.
Now I want to show the image of the answer according to the user's answer.
I am generating the answers randomly
`var answer = getAnswers(data)` // some calculation for file name for answer
<Image source={require('../path/to/my/answer/' + answer + '.jpg')}
Doing this I am getting error ``requireexpect exactly 1 string literal argument
I came to know that builder needs to know the location before building.
But how to solve this type of issue.
I can not map 200 files initailly for require
var one = require('../path/one.jpg')
var two = require('../path/two.jpg')
How can I solve this type of issue. Is there any way to do this ??
Help needed.
A dynamic search path in require is simply not possible.
From what I understand you have 2 options here:
1. Generate an Image file
Instead of writing 200 lines by hand you can make a script that scans through your image folder and generates a list of image urls. I would suggest a format similar to:
// Images.js
export const Answers = {
ANSWER_ONE : require('../path/one.jpg'),
ANSWER_TWO : require('../path/two.jpg'),
...
}
Then use it:
import {Answers} from './Images'
var answer = getAnswers(data)
<Image source={Answers[answer]} />
2. Copy image files to asset folders
If choosing this option you have to copy all image files to both your android and ios projects assets folder. You are then able to use them like this:
var answer = getAnswers(data)
<Image source={{uri: '../path/to/my/answer/' + answer + '}} />
Read more about using image assets.
...
Even though the second option seems tempting I would prefer the first in most cases. Mostly because the second requires you to make two copies every time you add an image (assuming you supporting both android and ios). Also, option 1 gives your better typing suggestions where you won't misspell the image name.
Hope this helps!
I want to generate a pdf file using JavaScript at client side . Firstly I tried using jsPdf API . But it does not work with Unicode character like Chinese.
Is there any option to enhance jspdf to support Unicode or any other library which supports Unicode .
Pdfmake API says it supports Unicode but when I tried it also does not work out, I checked in there demo example placing Unicode character .
I tried using pdfkit in Node.js but pdf is not getting created properly
var PDFDocument = require("pdfkit");
var fs = require('fs');
var doc = new PDFDocument;
doc.pipe(fs.createWriteStream('output.pdf'));
doc.fontSize(15);
doc.text('Generate PDF! ę¼¢åę¼¢åę¼¢åę¼¢å');
doc.end();
In pdf it created as Generate PDF! o"[Wo"[Wo"[Wo"[W
Also , Can I add multiple font in pdfMake
var fontDescriptors = {
Roboto: {
normal: 'examples/fonts/Roboto-Regular.ttf',
bold: 'examples/fonts/Roboto-Medium.ttf',
italics: 'examples/fonts/Roboto-Italic.ttf',
bolditalics: 'examples/fonts/Roboto-Italic.ttf'
}
};
var printer = new pdfMakePrinter(fontDescriptors);
I'll describe a solution using Node.js and PDFKit since you mentioned it but it also applies to pdfmake which internally uses PDFKit.
Most of the time, the default font used in these libraries do not accept Chinese characters. You have to add and use fonts that are compatible for the languages you need to support. If we take pdfmake for example, they use Roboto as their default font and it indeed does not accept Chinese characters.
Using your code example, we can add support for Chinese using the Microsoft YaHei font (msyh.ttf) for instance with only 1 additional line of code:
var PDFDocument = require("pdfkit");
var fs = require('fs');
var doc = new PDFDocument;
doc.pipe(fs.createWriteStream('output.pdf'));
doc.font('fonts/msyh.ttf');
doc.fontSize(15);
doc.text('Generate PDF! ę¼¢åę¼¢åę¼¢åę¼¢å');
doc.end();
The output would look like this:
I've followed the letter of the law (Javscript SDK) along with numerous variations but thus far I have not been able to save an image to a Parse.File. I'm starting think this is code they never finished before they abandoned the platform?
This is my error:
Failed to construct 'File': 2 arguments required, but only 0 present.
This is my code:
var base64 = $base64.encode(unescape(encodeURIComponent('a string')));
var file = new Parse.File("logo.png", { base64: base64});
file.save().then(function(){
var newLogo = new Parse.File();
newLogo.set('step2.png', file);
newLogo.save().then(function(){
offer.set("Alogo.png", newLogo);
offer.save();
}).then(function(){}, function(error){console.log(error);});
});
It saves no image or note of 'Alogo.png' or anything dealing with a logo in my ParseDB. Any help you could offer would be much appreciated!
For once, Parse was right on point. The issue was not with my code but with Angular and it's lack of recognition of 'file' tags in html. This post/tutorial proved very useful as it has a directive that enables angular to recognize post tags so that angular can add files to the model.
http://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs