JavaScript pdf generation library with Unicode support - javascript

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:

Related

Draft.js conversion without browser

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();

Korean language not rendering on PDF while using JSPDF

I am using JSPDF library to generate pdf while working on korean language the letters are getting change in symbols and sometime empty. Could anyone please help me?
const doc = new jsPDF();
const myFont = ... // overhere i am inserting a string which is being generated using following link https://rawgit.com/MrRio/jsPDF/master/fontconverter/fontconverter.html(it's too long)
// add the font to jsPDF
doc.addFileToVFS("MyFont.ttf", myFont);
doc.addFont("MyFont.ttf", "MyFont", "normal");
doc.setFont("MyFont");
You can try directly print the HTML text element.
Shameless plug 😃:
generate-pdf-from-react-html

jsPDF PubSub Error – "No unicode cmap for font"

I'm trying to add a custom font to jsPDF. I converted my file to base64 and did the following:
doc.addFileToVFS("font/rajdhani-regular-webfont.woff", base64enc);
where base64enc is the base 64 encoded string
then I add the font as follows:
doc.addFont('font/rajdhani-regular-webfont.woff', 'rajdhani', 'normal');
doc.setFont('rajdhani');
however, I keep getting the following error
[Error] jsPDF PubSub Error – "No unicode cmap for font" – Error: No unicode cmap for font — jspdf.min.js:9068
Error: No unicode cmap for font — jspdf.min.js:9068registerTTF — jspdf.min.js:9068i — jspdf.min.js:9027:86open — jspdf.min.js:9032(anonymous function) — jspdf.min.js:6031publish — jspdf.min.js:308yt — jspdf.min.js:729:166addFont — jspdf.min.js:1286callme — freport.php:500onclick — freport.php:100
publish (jspdf.min.js:29:5989)
yt (jspdf.min.js:29:18435)
addFont (jspdf.min.js:29:33701)
callme (freport.php:500)
onclick (freport.php:100)
I don't know why this is happening.
I've been struggling too but I finally found how to do it!
As mentioned here there is a font converter made by jsPDF Contributers which you can find on here.
First upload your font here and then type the name of your font. It will generate JS file which then you need to includ in your project. This can be your js folder if you wish.
And once its in your folder add this script tag to your index.html (or whichever html file you need the jsPDF for).
<script src="./fontName.js"></script>
Now that you have the font automatically added to the jsPDF Api for you with that file, you can go ahead and delete below two lines from your code.
doc.addFileToVFS("font/rajdhani-regular-webfont.woff", base64enc);
doc.addFont('font/rajdhani-regular-webfont.woff', 'rajdhani', 'normal');
Once you do that just check the file you downloaded from the converter and you should see at the very bottom, your font name on the second parameter of below code.
this.addFont('fontFile.woff', 'THIS IS FONT NAME' , '.......');
Copy that name. Lets say its "rajdhani" for this example. So you simply just put this before any text you create on the instance of jsPDF (doc).
doc.setFont('rajdhani');
You can simply test it with below
/*
"p" for portrait page orientation
"pt" for points. default is mm
"a4" is page size
*/
var doc = new jsPDF("p","pt","a4");
doc.setFont('rajdhani');
doc.text('This is a test', 20,20);
doc.save('filename.pdf');
Now you should see a pdf with 20px left 20px top margin ssaying "This is a test" with your font style.
Hope this helps :)
Also for ES6 module users the steps are very similar.
if you are here looking for a solution on Ionic 4 angular typescript like I was, below is the solution that worked for me!
First you need to install the package via npm as below.
npm install jspdf#latest --save
After that as mentioned in my previous reply there is a font converter made by jsPDF Contributers which you can find on here. Use this to upload your font file and include it in your project somewhere easy to reference inside your TS file so it could be in the same folder as the ts file for example.
-fontname-style.js // <<--- Here (This is the file you downloaded from converter)
-custom.page.module.ts
-custom.page.html
-custom.page.scss
-custom.page.spec.ts
-custom.page.ts
Now in your custom.page.ts import the following;
import * as jsPDF from 'jspdf';
import './fontname-style.js';
//OPTIONAL (Only import if you want to use autotable)
import 'jspdf-autotable';
import { autoTable as AutoTable } from 'jspdf-autotable';
Once you do that just check the file you downloaded from the converter and you should see at the very bottom, your font name on the second parameter of below code.
this.addFont('fontFile.ttf', 'fontName' , '.......');
Now you can create a function like this inside the custom.page.ts;
//custom.component.ts or custom.page.ts etc.
exportPDF(){
var doc = new jsPDF("p","pt","a4"); // You can set your own paramaters
doc.setFont('fontName');
doc.text('This is a test', 20,20); // You can set your own margins
return doc.save('filename.pdf');
}
Now you should see a pdf with 20px left 20px top margin ssaying "This is a test" with your font style :)
OPTIONAL PLUGIN - AutoTable for jsPDF
If you want to use autotable plugin for jsPDF, install the following package from npm.
npm install jspdf-autotable#latest --save
Once installed add this two imports to your custom.page.ts.
import 'jspdf-autotable';
import { autoTable as AutoTable } from 'jspdf-autotable';
You can create a data of columns and headers if you like
and inside the exportPDF() function we created before add those lines;
//custom.component.ts or custom.page.ts etc.
exportPDF(){
var doc = new jsPDF("p","pt","a4");
doc.setFont('fontName');
doc.text('This is a test', 20,20); // <-- Remove this line
((doc as any).autoTable as AutoTable)({
head: [['Name', 'Email', 'Country']], // <-- Table headers
body: [ // <-- Table body
['David', 'david#example.com', 'Sweden'],
['Castille', 'castille#example.com', 'Norway'],
// ...
],
styles: { font: "fontName" } /* <-- This is for the font to work in your
table */
});
return doc.save('filename.pdf');
}
Now you should get a pdf with the table inside it using your font :)
I hope this helps to someone.
i faced the same issue and followed the steps #TuncGerdan explained. My errors got cleared but i am getting the following warning in console.
jspdf.min.js:29 Unable to look up font label for font 'font name', 'normal'. Refer to getFontList() for available fonts.
When i get the fonts list using doc.getFontList(), i can see the custom font i included in the list.
Could somebody help with this.
I also got Error: No unicode cmap for font when trying to add a new font in binary form without their converter like mentioned in the docs. I think the crucial line in their example is this:
const myFont = ... // load the *.ttf font file as binary string
where for me it was unclear what they mean with binary string. However looking at their converter makes it clear that binary string should be a base64 encoded string.
So for me fetching a font from remote and adding it works with this:
/**
* load a font from remote and add it to jsPDF doc
* #param path the path of the font, e.g. '/app/fonts/My-Font.ttf'
* #param doc the jsPDF doc instance to install the font to
* #return {Promise<void>}
*/
async function loadFont(path, doc) {
let response = await fetch(path).catch((e) => console.error(e));
if (!response) {
return;
}
let fontName = path.substring(path.lastIndexOf("/") + 1);
console.log("using font", fontName);
let contentBuffer = await response.arrayBuffer();
let contentString = util.arrayBufferToBase64(contentBuffer)
if (contentString) {
doc.addFileToVFS(fontName, contentString);
doc.addFont(fontName, fontName, "normal");
doc.setFont(fontName);
}
}
For util.arrayBufferToBase64 I'm using the function from this Stackoverflow answer.

Is it possible to use custom Google web fonts with jsPDF

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');

Emoji to PNG or JPG in Node.js - how to?

For the project I'm working on I need to generate an image file from emoji (ideally Apple emoji). I thought it should be a fairly simple thing, but with each tool I use, I eventually run into a wall.
I've also considered working with an emoji set, like this one that I could query when needed. Unfortunately, the one I've linked to doesn't have Unicode 9.0 emoji such as avocado (🥑) shrimp (🦐) or harambe (🦍). Do you know of such an up-to-date set?
Code-wise, I've tried opentype.js, but it doesn't support .ttc fonts, which is the extension of the emoji font on my mac (Apple Color Emoji.ttc). I've converted the font to .ttf but that didn't work either:
var opentype = require('opentype.js');
opentype.load('./build_scripts/apple_color_emoji.ttf', function (err, font) {
if (err) {
alert('Could not load font: ' + err);
} else {
console.log("loaded the font",font);
var Canvas = require('canvas')
, Image = Canvas.Image
, canvas = new Canvas(200, 200)
, ctx = canvas.getContext('2d');
var path = font.getPath('🐐🦃', 0, 150, 72);
path.draw(ctx);
console.log('<img src="' + canvas.toDataURL() + '" />');
}
});
The result looks like this:
I've tried fontkit, which is supposed to be able to read .ttc files, but it throws an error if I try to use the Apple Color Emoji font.
var fontkit = require('fontkit');
var font = fontkit.openSync('./build_scripts/Apple Color Emoji.ttc');
// TypeError: font.layout is not a function
If I try the same with my converted .ttf file I end up with some incorrect svg:
var fontkit = require('fontkit');
var font = fontkit.openSync('./build_scripts/apple_color_emoji.ttf');
var run = font.layout('🐐🦃');
var svg = run.glyphs[0].path.toSVG();
console.log(svg);
// M-1 0ZM799 800Z
My question is, am I even approaching this the right way? Converting emoji that I already have on my machine to a .png or another format seems like something that should be fairly straightforward but I just can't get it to work.
EDIT:
I've found a list of emoji names by their hex codes in this repo (big shoutouts to rodrigopolo). Now I can simply use this:
var emoji = "😊".codePointAt(0).toString(16); //1f60a
var emojiFile = fs.readFileSync('./my-emoji-folder/' + emoji + '.png');
Still, would be great to know if somebody has a coding solution to this problem!
FURTHER EDIT:
Turns out the first solution I've found only included emoji up to Unicode 8.0, not Unicode 9.0. I've found a ruby gem gemoji that does emoji extraction. If you're not a ruby developer (I'm not), you can simply use the following commands in your shell:
git clone https://github.com/github/gemoji.git
cd gemoji
bundle install
bundle exec gemoji extract some-directory/emoji
You now have Unicode 9.0 emoji in your some-directory/emoji folder!
I was able to get this to work with fontkit by selecting a font from the font collection. I haven't found a case yet where using either of the TTFs included in the "Apple Color Emoji.ttc" gives different results.
const fs = require('fs');
const fontkit = require('fontkit');
const emoji = require('node-emoji');
const font = fontkit.openSync('./Apple Color Emoji.ttc').fonts[0];
let emo = emoji.get('100');
let run = font.layout(emo);
let glyph = run.glyphs[0].getImageForSize(128)
fs.writeFileSync('100.png', glyph.data);

Categories