Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jsPDF doesn't escape font names correctly #2593

Closed
aniederer-chatham opened this issue Oct 8, 2019 · 3 comments · Fixed by #2816
Closed

jsPDF doesn't escape font names correctly #2593

aniederer-chatham opened this issue Oct 8, 2019 · 3 comments · Fixed by #2816
Labels

Comments

@aniederer-chatham
Copy link

Hey, thanks for such a cool library! I believe I've run into an issue naming custom fonts in PDFs with whitespace in the name.

To Reproduce

First, generate a PDF using this code:

import * as jsPDF from 'jspdf';

jsPDF.API.events.push(['addFonts', function() {
    this.addFileToVFS(
      'Open Sans.ttf',
      /* Insert your favorite base64-encoded TTF here - I used Open Sans to repro */ ''
    );
    this.addFont('Open Sans.ttf', 'Open Sans', '');
}])

const pdf = new jsPDF();
pdf.setFont('Open Sans', '');
pdf.setTextColor(0, 0, 0);
pdf.text("Hello!", 0, 0);
pdf.save();

Second, open the generated PDF with your text editor of choice and find the font descriptor for your inserted font:

21 0 obj
<<
/Type /FontDescriptor
/FontName /Open Sans
/FontFile2 19 0 R
/FontBBox [-550 -271 1204 1048]
/Flags 32
/StemV 0
/ItalicAngle 0
/Ascent 1069
/Descent -293
/CapHeight 1462
>>
endobj

You will notice that the value of /FontName is an incorrect name - per the PDF specification version 1.4, section 3.2.4, "The name may include any regular characters, but not delimiter or white-space characters". It appears that "Open Sans" from addFont is copied nearly verbatim into the generated document.

This also occurs with other names referencing "Open Sans", such as the /BaseFont attribute in an object of type /Font

23 0 obj
<<
/Type /Font
/Subtype /Type0
/ToUnicode 20 0 R
/BaseFont /Open Sans
/Encoding /Identity-H
/DescendantFonts [22 0 R]
>>
endobj

As of PDF 1.2, one could use the escape sequence #20 to create a valid name literal with a space in it, e.g. /Open#20Sans

While I did not manually reproduce this, it appears that this issue will occur for any delimiter characters, as well. This may allow one to inject arbitrary attributes into /Font and /FontDescriptor objects, as well.

Possible Fixes

Escaping spaces and delimiters in all names using PDF 1.2's pound-sign syntax would probably be the most bulletproof solution, if you don't care about supporting anything below PDF 1.2. For versions before PDF 1.2, or as a half-measure, just throwing an error when a name contains an illegal character would also be a huge help.

Workarounds

To work around this issue, one should only include non-delimiter and non-whitespace characters in font names for now. Using this.addFont('Open Sans.ttf', 'Open#20Sans', ''); did not work for me, throwing a warning about a missing font reference, but I did not test this workaround extensively after finding the root cause of the issue.

Impact

Adobe Reader will refuse to read the generated PDF, and Microsoft Edge's inbuilt PDF reader will often produce incorrect results. Chrome and Firefox don't exhibit issues.

image

Affected Versions

I can confirm that the issue exists in jsPDF 1.5.3 in the browser, but from a cursory look at the code, it appears that this is an issue on master as well.

Please let me know if you'd like any additional information!

@github-actions
Copy link

github-actions bot commented Jul 7, 2020

This issue is stale because it has been open 90 days with no activity. It will be closed soon. Please comment/reopen if this issue is still relevant.

@HackbrettXXX
Copy link
Collaborator

I thought this was fixed already, but it seems like one occurrence was forgotten.

@Nervniyak
Copy link

Nervniyak commented Jul 21, 2020

Thank you @aniederer-chatham for pointing out the root of the issue.
Encountered this issue in jsPDF-AutoTable, workaround for me is to use everywhere "Opensans" instead of "Open Sans" I guess it doesn't matter if font name in JsPDF is the real name of the font.

For example:

    const fontName= `Opensans`;
    const fontStyle= `normal`;
    const fontIdentifier = `${fontName}-${fontStyle}.ttf`;
    doc.addFileToVFS(fontIdentifier, fontBase64);
    doc.addFont(fontIdentifier, fontName, fontStyle);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants