jsPDF: Convert html to pdf: utf-8 works only with text, but not html (unicode/cyrillic not working)

I’m opening this issue again.

I need to convert html to pdf with jsPDF. It contains UTF-8 symbols (cyrillic). I used fontconverter to generate js-file for my custom font as written here: https://github.com/MrRio/jsPDF

So now example with text works like a charm (from https://github.com/MrRio/jsPDF/blob/master/examples/js/russian.js)

var pdf = new jsPDF('p', 'pt', 'letter');
doc.setFont('PTSans');
doc.setFontSize(10);
doc.text("А ну чики брики и в дамки!", 10, 10);

And example with html ignores my custom font and exports incorrect symbols.

var pdf = new jsPDF('p', 'pt', 'letter');
doc.setFont('PTSans');
doc.setFontSize(10);
pdf.html( "<html>А ну чики брики и в дамки!</html>", { callback: function (pdf) {
    var iframe = document.createElement('iframe');
    iframe.setAttribute('style', 'position:absolute;right:0; top:0; bottom:0; height:100%; width:500px');
    document.body.appendChild(iframe);
    iframe.src = pdf.output('datauristring');
    }
});

What I need to do to export html to pdf with unicode symbols?

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 18
  • Comments: 47 (2 by maintainers)

Most upvoted comments

Please try setting the font name also in the html markup. E.g.

<html style="font-family: PTSans;">А ну чики брики и в дамки!</html>

In my case, using the version 2.1, setting the font style on the html element like so: <html style="font-family: PTSans;">А ну чики брики и в дамки!</html> and importing the fonts according to https://www.devlinpeck.com/tutorials/jspdf-custom-font#:~:text=Adding Custom Fonts to jsPDF,of your desired font file.&text=Once you have your .,to this jsPDF Font Converter. worked for me when using an html element reference instead of using a raw html string.

After 4 hours of intense debugging this worked for me:

jsPDF version 2.5.1

tested for symbols: ľ š č ť ž ý á í é ú ä ň ô ď

  1. download Roboto-Regular.ttf
  2. add it do the root of your project
  3. add following lines to your script:
...
let doc = new jsPDF();

//add this:
doc.addFont('Roboto-Regular.ttf', 'Roboto-Regular', 'normal');
doc.setFont('Roboto-Regular');

  1. last step is to add the font-family to the HTML you are trying to convert:
wrapper.style.fontFamily = "Roboto-Regular";

Hope this helps some of yall 🙏

my solution to this problem:

  1. create custom font ( use this : https://www.devlinpeck.com/tutorials/jspdf-custom-font#:~:text=Adding Custom Fonts to jsPDF,of your desired font file.&text=Once you have your .,to this jsPDF Font Converter )
  2. add font to project :
    image
  3. import this font: image
  4. add font to pdf doc.: image

I managed to get multiple different fonts working for me. I’ll try to map out my steps (a lot of it is based on above advice).

Result image

  1. Get the Arial Unicode MS font. It can be downloaded here, but feel free to find your own source for the font. Why this font? Because it supports the most languages in one font, which is critical for a mixed language setup like I have. If you go with a Noto font from Google or similar, you will be limited in the languages you can show in the pdf ( more on this in a sec).

  2. Upload the Arial Unicode Font.ttf file to https://peckconsulting.s3.amazonaws.com/fontconverter/fontconverter.html and download the resulting js file.

  3. Modify the JS file so it simply exports the font string. It should just contain export const font = "AAEAAAAUAQAABABARFNJR21e.... part. image

  4. Add the file in your project (note I renamed mine to .ts for my setup in angular) image

  5. Add the font _when generating the pdf. I lazy load the font only when the user starts to generate the pdf, so not all users have to download the quite large Arial font. IF YOU USE THE .html() THE NEXT PART IS CRITICAL. So, if you use pdf.html() method, you have to create a css class that sets the font family you added, and place it on every single html element rendering your characters. If you don’t set the font, it won’t render correctly. See below screenshots of how it i can work/not work. Note that since we have to set one font family, we can’t cover multiple languages with multiple noto fonts, since the first font match is the only one that will be used. If you want to use multiple noto fonts, you will have to find a way to combine them first. This is why I opted for the Arial one. image

image

image

  1. ???

  2. Profit! Your pdf should now be able to print with a multitude of languages. I won’t say it’s elegant, but at least it’s working. Good luck!

my solution to this problem:

  1. create custom font ( use this : https://www.devlinpeck.com/tutorials/jspdf-custom-font#:~:text=Adding Custom Fonts to jsPDF,of your desired font file.&text=Once you have your .,to this jsPDF Font Converter )
  2. add font to project : image
  3. import this font: image
  4. add font to pdf doc.: image

thank you so much, i downloaded custom font, and I had to add style=“font-family: …” to wrapper div

None of the examples on this page worked for me, neither for Chinese nor for Cyrillic symbols.

I’ve finally got it working. Here is my piece of code:

` report.addFileToVFS(‘arialunicode.ttf’, arialunicode); report.addFont(‘arialunicode.ttf’, fontFamily, ‘normal’); report.setFont(fontFamily);

      pdfContent.querySelectorAll('pre').forEach((element) => {
        element.style.fontFamily = fontFamily;
      });

      report.html(pdfContent, {
        margin: 10,
        callback: (doc) => {
          doc.save('compare-code-report.pdf');
          pdfContent.remove();
        },
      });

`

A few important things are:

  • Load the font and add it to the PDF before using html method
  • In my case pdfContent contains a table, and the very last element in each cell that actually displaying the text is <pre>, I have to find all of them and adjust font-family style.

Hi guys. I figured out the reason. It’s about html2canvas. The fact is that if your styles explicitly specify the font, and it is not imported into the jspdf fonts, then you will not display correctly. When the html2canvas library reads the css properties from your html, it takes into account the css font-family as well.

Example of non-working code: <style> h1 { font-family: "Roboto", sans-serif; } </style>

`<div id="get">
	<h1 class="align-center">А ну чики брики и в дамки!</h1>
</div>

<script src="https://html2canvas.hertzen.com/dist/html2canvas.js"></script>
<script src='../../dist/jspdf.umd.js'></script>

<script>
	 
	const { jsPDF } = window.jspdf;

	const pdf = new jsPDF({
		orientation: "p",
		unit: "pt",
		format: "a4"
	});

	pdf.addFileToVFS('RobotoCondensedLight', RobotoCondensed.Light);
	pdf.addFileToVFS('RobotoCondensedBold', RobotoCondensed.Bold);
	pdf.addFileToVFS('RobotoCondensedLightItalic', RobotoCondensed.LightItalic);
	pdf.addFileToVFS('RobotoCondensedBoldItalic', RobotoCondensed.BoldItalic);
	pdf.addFont('RobotoCondensedLight', 'RobotoCondensed', 'normal');
	pdf.addFont('RobotoCondensedBold', 'RobotoCondensed', 'bold');
	pdf.addFont('RobotoCondensedLightItalic', 'RobotoCondensed', 'italic');
	pdf.addFont('RobotoCondensedBoldItalic', 'RobotoCondensed', 'bolditalic');
	pdf.setFont('RobotoCondensed');

	const div = document.createElement('div');
	div.style.fontFamily = "RobotoCondensed";

	const cop = document.getElementById('get');
	 
	div.innerHTML = cop.outerHTML;
	document.body.appendChild(div);

	pdf.html(div, {
		callback: function (isPdf) {
			isPdf.save('test.pdf');
		}
	});
</script>`

Now remove the css property: <style> h1 { } </style>

`<div id="get">
	<h1 class="align-center">А ну чики брики и в дамки!</h1>
</div>

<script src="https://html2canvas.hertzen.com/dist/html2canvas.js"></script>
<script src='../../dist/jspdf.umd.js'></script>

<script>
	 
	const { jsPDF } = window.jspdf;

	const pdf = new jsPDF({
		orientation: "p",
		unit: "pt",
		format: "a4"
	});

	pdf.addFileToVFS('RobotoCondensedLight', RobotoCondensed.Light);
	pdf.addFileToVFS('RobotoCondensedBold', RobotoCondensed.Bold);
	pdf.addFileToVFS('RobotoCondensedLightItalic', RobotoCondensed.LightItalic);
	pdf.addFileToVFS('RobotoCondensedBoldItalic', RobotoCondensed.BoldItalic);
	pdf.addFont('RobotoCondensedLight', 'RobotoCondensed', 'normal');
	pdf.addFont('RobotoCondensedBold', 'RobotoCondensed', 'bold');
	pdf.addFont('RobotoCondensedLightItalic', 'RobotoCondensed', 'italic');
	pdf.addFont('RobotoCondensedBoldItalic', 'RobotoCondensed', 'bolditalic');
	pdf.setFont('RobotoCondensed');

	const div = document.createElement('div');
	div.style.fontFamily = "RobotoCondensed";

	const cop = document.getElementById('get');
	 
	div.innerHTML = cop.outerHTML;
	document.body.appendChild(div);

	pdf.html(div, {
		callback: function (isPdf) {
			isPdf.save('test.pdf');
		}
	});
</script>`

And now everything works!!!

I am building a web app with three cultures "en", "tr", "ar". I followed the process as mentioned by @Rui-Jesus, but it didn’t work!

In my case, using the version 2.1, setting the font style on the html element like so: <html style="font-family: PTSans;">А ну чики брики и в дамки!</html> and importing the fonts according to https://www.devlinpeck.com/tutorials/jspdf-custom-font#:~:text=Adding Custom Fonts to jsPDF,of your desired font file.&text=Once you have your .,to this jsPDF Font Converter. worked for me when using an html element reference instead of using a raw html string.

Then I did few modifications to make it work with specific font (the same process didn’t work with all fonts)

  • Download font from google fonts, in my case I used Cairo (Regular 400) because it supports all my cultures.
  • Put the font link inside <header> :
<!-- GoogleFonts: Cairo -->
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Cairo&display=swap" rel="stylesheet">
  • Use CSS rules in CSS file:
body {
    font-family: 'Cairo', sans-serif !important;
}
  • Convert the font as mentioned in the tutorial. But I changed the module format to UMD.
  • The downloaded file uses the font name as Cairo-Regular, and that was the problem. I changed it to Cairo since the CSS font name is Cairo.
// this.addFont('Cairo-Regular-normal.ttf', 'Cairo-Regular', 'normal');
this.addFont('Cairo-Regular-normal.ttf', 'Cairo', 'normal');
  • Adding the style to html didn’t work, but it worked when added style to the body tag:
<body style="font-family: 'Cairo', sans-serif !important;">
  • Below is the rest of the code:
<!-- jsPDF -->
<script src="~/lib/jspdf/dist/jspdf.umd.min.js"></script>
<script src="~/js/html2canvas.min.js"></script>
<script src="~/lib/dompurify/dist/purify.min.js"></script>
<script src="~/js/Cairo-Regular-normal.js"></script>
<script>
    $(function () {
        setTimeout(function () {
            window.jsPDF = window.jspdf.jsPDF;
            // Default export is a4 paper, portrait, using millimeters for units
            const doc = new jsPDF('p', 'pt', 'a4');
            doc.html(document.body, {
                html2canvas: {
                    scale: 0.5,
                    scrollY:0
                },
                x: 0,
                y: 0,
                callback: function (doc) {
                    window.open(doc.output('bloburl'));
                }
            });
        }, 1000);
    });
</script>

Now I have a working solution with English and Turkish : image

But it is not fully supporting Arabic. Some letters are missing, and if I have multiple languages the latin letters are rendred over each other as seen in the below image: image

The correct html page looks like below: image

I also tried with adding doc.addLanguage("ar") but it didn’t help.

I managed to get multiple different fonts working for me. I’ll try to map out my steps (a lot of it is based on above advice).

Result image

  1. Get the Arial Unicode MS font. It can be downloaded here, but feel free to find your own source for the font. Why this font? Because it supports the most languages in one font, which is critical for a mixed language setup like I have. If you go with a Noto font from Google or similar, you will be limited in the languages you can show in the pdf ( more on this in a sec).
  2. Upload the Arial Unicode Font.ttf file to https://peckconsulting.s3.amazonaws.com/fontconverter/fontconverter.html and download the resulting js file.
  3. Modify the JS file so it simply exports the font string. It should just contain export const font = "AAEAAAAUAQAABABARFNJR21e.... part. image
  4. Add the file in your project (note I renamed mine to .ts for my setup in angular) image
  5. Add the font _when generating the pdf. I lazy load the font only when the user starts to generate the pdf, so not all users have to download the quite large Arial font. IF YOU USE THE .html() THE NEXT PART IS CRITICAL. So, if you use pdf.html() method, you have to create a css class that sets the font family you added, and place it on every single html element rendering your characters. If you don’t set the font, it won’t render correctly. See below screenshots of how it i can work/not work. Note that since we have to set one font family, we can’t cover multiple languages with multiple noto fonts, since the first font match is the only one that will be used. If you want to use multiple noto fonts, you will have to find a way to combine them first. This is why I opted for the Arial one. image

image

image

  1. ???
  2. Profit! Your pdf should now be able to print with a multitude of languages. I won’t say it’s elegant, but at least it’s working. Good luck!

Works like a charm! THANKS. I was so close but was really messing up the matching names and classes in my less files. Kept getting me frustrated but going step by step as you said worked perfectly.

Setting PTSans-Bold on body worked for me

import { jsPDF } from 'jspdf'
import '@/assets/font/PTSans-Bold-normal'
export const useDownloadCompPdf = () => {
  const savePDF = (htmlElement: HTMLElement) => {
    const body = document.querySelector('body')
    body.style = 'font-family: PTSans-Bold'
    const doc = new jsPDF({
      orientation: 'l',
      unit: 'mm',
      format: 'b0',
      putOnlyUsedFonts: true,
      floatPrecision: 'smart',
    })
    doc.addFont('PTSans-Bold-normal.ttf', 'PTSans-Bold', 'normal')
    doc.setFont('PTSans-Bold')

    doc.html(htmlElement, {
      callback(pdf) {
        pdf.addFont('PTSans-Bold-normal.ttf', 'PTSans-Bold', 'normal')
        pdf.setFont('PTSans-Bold')
        pdf.save('invoice.pdf')
      },
    })
    setTimeout(() => {
      body.style = 'font-family: Inter, sans-serif'
    }, 1000)
  }
  return { savePDF }
}

I have the same problem. I’m surprised there is no good solution. How this library got so many likes? Everything is so hard to solve in there.

so it is my code but not supporting fornAwesome and cyrylic.

import './RobotoBlackNormal';
...
var doc = new jsPDF('p', 'pt', 'a4');
        // doc.addFont("test/reference/PTSans.ttf", "PTSans", "normal");
        doc.addFont('https://use.fontawesome.com/releases/v5.3.1/webfonts/fa-regular-400.ttf', 'FontAwesome', 'normal');
        doc.addFont('Roboto-Regular.ttf', 'Roboto-Regular', 'normal');
        doc.addFont('Roboto-Black.ttf', 'Roboto-Black', 'normal');
        doc.addFont('Roboto-Bold.ttf', 'Roboto-Bold', 'normal');
        doc.addFont('Roboto-Medium.ttf', 'Roboto-Medium', 'normal');
        doc.setFont('Font Awesome 5 Free');
        doc.setFont('Roboto-Regular');
        doc.setFont('Roboto-Black');
        doc.setFont('Roboto-Bold');
        doc.setFont('Roboto-Medium');

        doc.html(ref.current.querySelector('#response-list'), {
            callback: function (doc) {
                doc.save();
            }});

image

use it like this

var callAddFont = function () {
    /* add all types or that font to jspdf fontlist below all is ttf file just in  alias */
    this.addFont(interfontNormal, "inter", "normal");
    this.addFont(interfontBold, "inter", "bold");
    this.addFont(interfontLight, "inter", "lighter");
    this.addFont(interfontThin, "inter", "thin");
  };

  jsPDF.API.events.push(["addFonts", callAddFont]);
}

Steps to Follow for Exporting HTML Content with Cyrillic Fonts:

1.Download Roboto Font: Start by downloading the Roboto font family from Google Fonts. Afterward, use a font converter, like https://peckconsulting.s3.amazonaws.com/fontconverter/fontconverter.html, to convert the font for integration into your project.

2.Add Roboto-Regular Font: Once you have the converted Roboto-Regular font files, incorporate them into your project. The roboto regular font converted by this converter should look like this : Screenshot 2023-12-05 113009

3.Import Font in Code: In the component or file where you plan to export content as a PDF, import the Roboto-Regular font. For instance: import ‘…/…/assets/fonts/Roboto-Regular-normal.css’;

  1. Generate Report Method: Implement a method for generating the PDF report. Ensure to set the font family to ‘Roboto-Regular’ for content that includes Cyrillic characters. A sample method might look like this: image
  const generatePDF = () => {
    let doc = new jsPDF();
    let elementHTML = document.getElementById('print-content-report');
    if (elementHTML) {
      elementHTML.style.fontFamily = 'Roboto-Regular';

      doc.html(elementHTML, {
        callback: function (doc) {
          doc.save('sample-document.pdf', { returnPromise: true }).then(() => {
            console.log('content is exported successfully');
          });
        },
        x: 15,
        y: 15,
        width: 170,
        windowWidth: 650
      });
    } else {
      console.error('Element with id "print-content-report" not found');
    }
  };

image this is how i solve this problem.

POLISH LETTERS

Here is my working js fiddle - I left it here for future readers

It works with some fonts, but not with all - that is the first problem one will have if trying to work with this (for example, it doesn’t work with Roboto). Next, I was able to create a regular font (like you did) but when I start adding italic, bold, and bold italic everything was messed up again. Simply said it can’t give you the full thing anyway. So what I did I dismiss everything and just replace ČĆĐ (čćđ) to c i dj (what in Serbian Latin script we can read without any problems). Of course, that may not work for more complex scripts, but my client is happy!

https://github.com/parallax/jsPDF/issues/2968#issuecomment-796507870

my solution to this problem:

  1. create custom font ( use this : https://www.devlinpeck.com/tutorials/jspdf-custom-font#:~:text=Adding Custom Fonts to jsPDF,of your desired font file.&text=Once you have your .,to this jsPDF Font Converter )
  2. add font to project : image
  3. import this font: image
  4. add font to pdf doc.: image

thank you so much this solution works wry well!

Well, dear friends, perhaps I have found the Main Evil. To display .html with a specific font as of October 05, 2021, it is enough to specify the font as a style for the html element, and it will be applied to all the text inside it. It is important to use the correct fonts, which have the layout of your alphabet, Cyrillic, Polish, etc. This is my experience of 2 days searching for an answer to my problem. Hope this helps someone not to waste time. Alternatively, write the same set of fonts that is used on your site using the .addFont & .setFont method. That is, the text in your html has exactly the same font set as you specify with the .addFont & .setFont methods. Otherwise, the font will be displayed incorrectly. All the best, be attentive and quick

In my case, using the version 2.1, setting the font style on the html element like so: <html style="font-family: PTSans;">А ну чики брики и в дамки!</html> and importing the fonts according to https://www.devlinpeck.com/tutorials/jspdf-custom-font#:~:text=Adding Custom Fonts to jsPDF,of your desired font file.&text=Once you have your .,to this jsPDF Font Converter. worked for me when using an html element reference instead of using a raw html string.

Thank you. He inspired me. I use html method, but the tag on the page does not show the Chinese I want, but after reading your reply, I added font family to the tag on the page, and it can be used.

This is the end result: QQ截图20210120095315 This is code:

const htmlStr: HTMLElement = document.querySelector('.main') || document.body;
doc.html(htmlStr, {
        callback(isPdf) {
          // add the font to jsPDF
          doc.addFileToVFS('fzjt.ttf', MyFont); // addfont
          doc.addFont('fzjt.ttf', 'MyFont', 'normal');
          doc.setFont('MyFont');
          isPdf.text('又是中文', 10, 10);
          isPdf.save();
        },
      });
.main
  background #ffffff
  width 90%
  font-family 'MyFont'

It’s amazing 🎉

@potapovnikita

I’m opening this issue again.

I need to convert html to pdf with jsPDF. It contains UTF-8 symbols (cyrillic). I used fontconverter to generate js-file for my custom font as written here: https://github.com/MrRio/jsPDF

So now example with text works like a charm (from https://github.com/MrRio/jsPDF/blob/master/examples/js/russian.js)

var pdf = new jsPDF('p', 'pt', 'letter');
doc.setFont('PTSans');
doc.setFontSize(10);
doc.text("А ну чики брики и в дамки!", 10, 10);

And example with html ignores my custom font and exports incorrect symbols.

var pdf = new jsPDF('p', 'pt', 'letter');
doc.setFont('PTSans');
doc.setFontSize(10);
pdf.html( "<html>А ну чики брики и в дамки!</html>", { callback: function (pdf) {
    var iframe = document.createElement('iframe');
    iframe.setAttribute('style', 'position:absolute;right:0; top:0; bottom:0; height:100%; width:500px');
    document.body.appendChild(iframe);
    iframe.src = pdf.output('datauristring');
    }
});

What I need to do to export html to pdf with unicode symbols?

I would like to mention two things here.

  1. setting font as doc.setFont(‘PTSans’) won’t work for the HTML. for accessing fonts in the HTML you have to pass it as an inline style with font-family.

  2. I have observed that the .html API of jsPdf does not support <html> directly, takes the main element of the body, and renders it. ( Correct me if I’m wrong but I have observed it many times.)

so, after spending almost my entire day on it, I have figure out the below solution which helps me to get those bullets on my pdf.

after adding custom fonts just like explained below article you have to import your font files into your component and just style your HTML like this. https://www.devlinpeck.com/tutorials/jspdf-custom-font#:~:text=Adding Custom Fonts to jsPDF,of your desired font file.&text=Once you have your .,to this jsPDF Font Converter.

`import ‘…/…/…/Lato-Regular-normal’;

const htmlToConvert = <div style="font-size:16px;font-family:Lato ">

doc.html(htmlToConvert,{ callback:function{ doc.save() } })`

@potapovnikita as I mentioned - there is a problem. I just showed part of my code in case I made a mistake somewhere 😃

@HackbrettXXX the same with Polish signs 😦 Is there any chance for a quick fix of this problem?

I’m tring to create pdf in this way:

window.jsPDF = window.jspdf.jsPDF
var doc = new jsPDF('p', 'px', 'a4');

var elementToPrint = document.getElementById('printPDFtest');
doc.html(elementToPrint, {
        html2canvas: {
            scale: 0.45
        },
        callback: function (doc) {
            doc.save();
        }
    });