parallax / jsPDF

Client-side JavaScript PDF generation for everyone.

Home Page:https://parall.ax/products/jspdf

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Custom fonts display correctly in Firefox but incorrectly in Edge and Adobe Acrobat Reader

mgirolet-gl opened this issue · comments

Preamble

I have read and understood the contribution guidelines.

I have read #2677 and am applying exactly what it recommends doing for custom font, therefore comments such as 'just see #2677' would be irrelevant.

jsPDF version

v2.5.1 (latest at the time of writing)

Situation

I am importing a font using #2677's/README's recommended method with the fontconverter:

    var font = '/*base64-encoded TTF file*/';
    var callAddFont = function () {
      this.addFileToVFS('OpenSans_SemiCondensed-Regular.ttf', font);
      this.addFont('OpenSans_SemiCondensed-Regular.ttf', 'OpenSansSemiCondensed', 'normal');
	};

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

I am then writing some text using it to a new document:

    let doc = new jsPDF('l', 'mm', 'a3');

    doc.setFont('OpenSansSemiCondensed');
    doc.setFontSize(30);
    doc.text("This is a test", doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' });

I then save it using FileSaver:

    saveAs(new Blob([doc.output()], { type: 'application/pdf' }), 'test.pdf');

The error

Once exported, the PDF looks as it should with the correct font when opened in Firefox:
Image of the PDF rendered correctly

However, when rendered in Edge, the PDF seems to use a default font instead of the embedded one:
Image of the PDF rendered with the wrong font

And even worse, when opened in Adobe Acrobat Reader (I use version 2024.001.20604 64 bits), a popup indicates 'Cannot extract the embedded font "OpenSansSemiCondensed". Some characters may not display or print correctly.' and the text is rendered using dots instead of actual glyphs:
Image of the PDF rendered with dots instead of text

Most of the forum posts I could find mentionning this issue said that the PDF required the font to be installed on the computer, which makes no sense since the entire point of PDFs is to be portable and therefore embed fonts. Plus, Firefox can display the text correctly so the font is definitely in the document.

Adobe Acrobat Reader does acknowledge the existence of the font in the file properties menu, precising that it's a TrueType (CID) with Identity-H encoding.

MCVE

See this JSFiddle: https://jsfiddle.net/cov42qfe/14/

If the link breaks for any reason, you can recreate it with the following code:

<script src="https://cdn.jsdelivr.net/npm/file-saver@2.0.5/dist/FileSaver.min.js"></script>
<script src=" https://cdn.jsdelivr.net/npm/jspdf@2.5.1/dist/jspdf.umd.min.js "></script>

<script>
  async function blobToBase64(blob) {
    return new Promise((resolve, _) => {
      const reader = new FileReader();
      reader.onloadend = () => resolve(reader.result);
      reader.readAsDataURL(blob);
    });
  }

  async function doStuff() {
    // Font: Open Sans (Semi-condensed, regular)
    // https://fonts.google.com/specimen/Open+Sans
    // https://github.com/googlefonts/opensans/raw/bd7e37632246368c60fdcbd374dbf9bad11969b6/fonts/ttf/OpenSans-CondensedRegular.ttf
    // Licensed under OPL

    const { jsPDF } = window.jspdf;

    var font = '/* Paste the base64-encoded string containing the TTF here. */';
    var callAddFont = function () {
      this.addFileToVFS('OpenSans_SemiCondensed-Regular.ttf', font);
      this.addFont('OpenSans_SemiCondensed-Regular.ttf', 'OpenSansSemiCondensed', 'normal');
    };

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

    let doc = new jsPDF('l', 'mm', 'a3');

    doc.setFont('OpenSansSemiCondensed');
    doc.setFontSize(30);
    doc.text("This is a test", doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' });

    saveAs(new Blob([doc.output()], { type: 'application/pdf' }), 'test.pdf');
  }
  
  doStuff();
</script>

Additional notes

I have tested this with Open Sans, Free Sans and Deja Vu Sans Condensed, the last of which shows random text junk and invalid characters instead of dots on Adobe Acrobat Reader.

I found where the problem was from.
It turns out that using output() and then building the Blob using new encodes the document in UTF-8, which Firefox accepts but not Edge nor Adobe Acrobat Reader.

The solution was to use output('blob') to get the ANSI-encoded bloc directly from jsPDF and save it:

saveAs(doc.output('blob'), 'test.pdf');