diegomura / react-pdf

📄 Create PDF files using React
https://react-pdf.org
MIT License
14.28k stars 1.12k forks source link

Unhandled Rejection (TypeError): Cannot read property 'hasGlyphForCodePoint' of null #777

Closed stevensacks closed 2 years ago

stevensacks commented 4 years ago

Describe the bug I'm using PDFDownloadLink and rendering a perfectly valid PDF document. It went from sometimes throwing this error to always throwing this error. It used to just appear in the console, but with the latest release of react-scripts (3.3.0), now when it occurs, my entire app crashes. I tried wrapping it in an ErrorBoundary but it won't catch it.

I render the document on its own prior to passing it to the PDFDownloadLink. The rendering of the document has no problems. But, if I render PDFDownloadLink with the document, my app crashes.

I don't know what it causing this, but you need to try catch this error internally so it won't just crash the entire app. Or throw it in a way that an ErrorBoundary will catch it.

To Reproduce I've figured out how to reproduce it. If you have two PDFDownloadLink's rendering the same document, or you quickly re-render the same PDFDownloadLink, it happens every time.

Expected behavior For this error not to happen anymore, or at least not crash the app and be catchable by ErrorBoundaries.

Screenshots

Part 1 Part 2

Desktop (please complete the following information):

SamJarmanPP commented 4 years ago
Lionprincipe commented 4 years ago

Hello,@diegomura
i had this issue yesterday when i was refactoring a previously working code. My issue was definitely like the one reported here https://github.com/diegomura/react-pdf/issues/636 . On chrome debugging tool the variable sent to the function buildSubsetForFont was null. I could not reproduce this issue on codesandbox. . While searching in the react-pdf code I have noticed that the Font class has a name issue on its register method. this.fontFamily should be "this.family". I am not sure if this is the reason for this error. I could not look deeper and now my code is working again and the Font is included on my PDF file. I will look again later on this.

class Font { static create(family) { return new Font(family); }

constructor(family) { this.family = family; this.sources = []; }

register({ src, fontWeight, fontStyle, ...options }) { this.sources.push( new FontSource(src, this.fontFamily, fontStyle, fontWeight, options), ); }

veyss commented 4 years ago

hi I'm getting the same problem, what should I do? I will appreciate any help thanks. styles.js import {Font, StyleSheet} from "@react-pdf/renderer"; import regular from "./Roboto-Regular.ttf"; import boldd from "./Roboto-Bold.ttf"; Font.register({ family: 'Roboto', fonts: [ { src: regular }, { src: boldd, fontWeight: 'bold' } ] })

amamenko commented 4 years ago

I was having the same issue, so I took the tip from @SamJarmanPP

  • 1, this is happening intermittently for me too. I register custom fonts as early as I can in the application lifecycle.

and basically what I did was create a function in my index.js file and call it in a useEffect hook only after the initial render as follows

`const registerFont = () => { Font.register({ family: "Montserrat", src: "http://fonts.gstatic.com/s/montserrat/v10/zhcz-_WihjSQC0oHJ9TCYC3USBnSvpkopQaUR-2r7iU.ttf", }); };

useEffect(() => { registerFont(); }, []);`

That seems to have worked and I'm not seeing the error anymore.

Xcellion commented 4 years ago

Anyone stumbling on this who is using PDFDownloadLink, I ended up using this solution to generate the PDFs on user click and it ended up working!

wangshijie0409 commented 3 years ago

For help How to solve this error? image

alampros commented 3 years ago

This happens using the v2 branch as well (@2.0.0-beta.14).

igorvc30 commented 3 years ago

I had this problem using NextJS. I solved this problem using require and installing next-fonts. So a downloaded the .tff and put it at the same folder. This was a really tricky problem, I tried using static folder and url. It was working at dev mode and local builds, but once I sent to production using AWS Amplify, my PDF Viewer was broken.

const font = require('./mplus.ttf')
Font.register({
  family: 'mplus-1',
  src: font
})
ampsteric commented 3 years ago

I have tried all the above things and still I am getting the same error. Please help.

ampsteric commented 3 years ago

I am getting this error:- Unhandled Rejection (TypeError): Cannot read property 'hasGlyphForCodePoint' of null

cloetensbrecht commented 3 years ago

Same issue.

The error occurs not by registering the fonts but by using the registered fonts inside the stylesheet as a fontFamily.

const PDFstyles = StyleSheet.create({
  text: {
    fontFamily: 'Open Sans'
  }
})
Font.register({
  family: 'Open Sans',
  src: 'http://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFVZ0ef8pkAg.ttf'
})

When the fontFamily is commented the error disappears.

const PDFstyles = StyleSheet.create({
  text: {
    // fontFamily: 'Open Sans'
  }
})
Font.register({
  family: 'Open Sans',
  src: 'http://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFVZ0ef8pkAg.ttf'
})

But of course in this case it's useless to Register the fonts because it's not possible to use them.

eldieco commented 3 years ago

Getting this same error with the following code on v1.6.11:

<Modal open={open} onClose={close}>
  <Modal.Content >  
    <StyledPDFViewer>
      <PDFViewer>
    <Invoice data={invoiceData} />
      </PDFViewer>
    </StyledPDFViewer>
    <Button>
      <PDFDownloadLink document={<Invoice data={invoiceData} />}>
        {({ blob, url, loading, error }) => {
      if (loading) return <Loader />;
        return 'Download Invoice';
    }}
      </PDFDownloadLink>
    </Button>
  </Modal.Content>
</Modal>

The error only occurs when I'm rendering the component in two different places, like you can see above.

In my example, <Invoice /> is a <Document /> component.

I'm using <PDFViewer> to display the generated PDF to the user. Then I'm using <PDFDownloadLink /> to give the user the option of downloading the PDF. When I remove the <PDFDownloadLink /> OR replace the document property with a different <Document /> component the error goes away.

Closing the modal (which I'm getting from 'semantic-ui-react'), and then opening it again seems to resolve the issue every time, replacing the loading state with the proper 'Download Invoice' text, at which point they can download the PDF successfully.

Any ideas how to fix this in the context of my example?

Thanks!

bugtype commented 3 years ago

Thank you for @eldieco

I'm using it as below.

function App() {

  const [isOnPdfView, setIsOnPdfView] = React.useState(false)

  React.useEffect(() => {

    setTimeout(() => {
      setIsOnPdfView(true)
    }, 1000);
  }, [])

  return (
    <div className="App">
    <PDFViewer style={{height: 1000, width: 500}}>
      <MyDocument />
    </PDFViewer>
    {isOnPdfView && <PDFDownloadLink document={<MyDocument />} fileName="invoice.pdf">
    {({ blob, url, loading, error }) => (loading ? 'Loading document...' : 'Download now!')}
    </PDFDownloadLink> }
    </div>
  );
}
  1. If there is an onLoaded event in the pdfViewer component. ex) react-google-maps
  2. When using PDF DownloadLink, how about having PDfViewer load internally?
gunjankothari commented 3 years ago

I was having the same issue, so I took the tip from @SamJarmanPP

  • 1, this is happening intermittently for me too. I register custom fonts as early as I can in the application lifecycle.

and basically what I did was create a function in my index.js file and call it in a useEffect hook only after the initial render as follows

`const registerFont = () => { Font.register({ family: "Montserrat", src: "http://fonts.gstatic.com/s/montserrat/v10/zhcz-_WihjSQC0oHJ9TCYC3USBnSvpkopQaUR-2r7iU.ttf", }); };

useEffect(() => { registerFont(); }, []);`

That seems to have worked and I'm not seeing the error anymore.

@amamenko It Worked for me too. Thanks.

kpawelczak commented 3 years ago

Hello, guys, I had similar issue. In my case, this type of code was causing the error:

<View render={() => { return <Text style={styleWithImportedFonts}></Text>/>

So the fix was simple:

<View style={styleWithImportedFonts} render={() => { return <Text style={styleWithImportedFonts}></Text>/>

hope it helps!

hamalsolutions commented 3 years ago

Hello guys, Having the same issue, I tried the workaround from @SamJarmanPP but no luck still getting

Cannot read property 'hasGlyphForCodePoint' of null

is there another way to overcome this issue?

maacaro commented 3 years ago

Hello folks, I am having the same issue working with<PDFDownloadLink/> and Font.register if I remove the registered font from the style it works quite similar to what @cloetensbrecht posted

amamenko commented 3 years ago

Changing the buildSubsetForFont function from

const buildSubsetForFont = font =>
  IGNORABLE_CODEPOINTS.reduce((acc, codePoint) => {
    if (font.hasGlyphForCodePoint && font.hasGlyphForCodePoint(codePoint)) {
      return acc;
    }
    return [...acc, String.fromCharCode(codePoint)];
  }, []);

to

const buildSubsetForFont = font =>
  IGNORABLE_CODEPOINTS.reduce((acc, codePoint) => {
    if (font) {
        if (font.hasGlyphForCodePoint && font.hasGlyphForCodePoint(codePoint)) {
            return acc;
        }
        return [...acc, String.fromCharCode(codePoint)];
    }
    return [...acc, String.fromCharCode(codePoint)];
}, []);

would, at the very least, prevent the entire app from crashing since you can't run the fontkit method hasGlyphForCodePoint on a null value.

maacaro commented 3 years ago

thanks, @amamenko for the answer, it didn't work for me, it breaks in other places now

zenininja commented 3 years ago

@amamenko, Thanks for the response. It worked for me..

And I'm using patch-package to add this patch..

zenininja commented 3 years ago

Looks like I was not registering fonts correctly and latest react-pdf 2.0 throws exception.. Once I'm registering fonts correctly this exception is not thrown...

diegomura commented 3 years ago

Can someone share a way I can replicate this? It's hard to see why based on the discussion above

tomaszzmudzinski commented 3 years ago

Can someone share a way I can replicate this? It's hard to see why based on the discussion above

Yes indeed, we found out how to simulate this on codesandbox.io:

https://codesandbox.io/s/dazzling-night-0io97?file=/src/App.js

You have to open the link via google chrome (https://0io97.csb.app/) and set network throttling (slow 3g) in your browser. Then click 'get multiple blob' 😄

This problem appears only when you need to display/open multiple pdfs and the first one is using other custom fonts than any of the later ones. It appears that it's some kind of promise which only registers fonts necessary to render the first pdf. It's get resolved when the first pdf is rendered and other pdfs are also rendered despite the fact that their fonts are not registered.

It is easier to simulate when you have network throttled or big pdfs, but from time to time it also happens on a fast connection.

bangarangler commented 3 years ago

Any updates on this? Having the same issues. No solutions above working. I like @amamenko but would be nice if it's implemented upstream. Thanks all ; )

OliverLeighC commented 3 years ago

Any word on this? Would it be possible to merge my PR so it doesn't crash the application and uses the fallback font if no font is found?

diegomura commented 2 years ago

Merged already. Closing this

madebydor commented 2 years ago

problem still happens. any solution? this only happens when trying to render PDFDownloadLink multiple times in one page.

kissartisan commented 2 years ago

Same issue.

The error occurs not by registering the fonts but by using the registered fonts inside the stylesheet as a fontFamily.

const PDFstyles = StyleSheet.create({
  text: {
    fontFamily: 'Open Sans'
  }
})
Font.register({
  family: 'Open Sans',
  src: 'http://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFVZ0ef8pkAg.ttf'
})

When the fontFamily is commented the error disappears.

const PDFstyles = StyleSheet.create({
  text: {
    // fontFamily: 'Open Sans'
  }
})
Font.register({
  family: 'Open Sans',
  src: 'http://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFVZ0ef8pkAg.ttf'
})

But of course in this case it's useless to Register the fonts because it's not possible to use them.

I can replicate this on my end intermittently and this was also my issue. fontFamily inside the StyleSheet.create() produced the discussed error.

Do someone knows what are other ways to modify the font family?

I'm using "@react-pdf/renderer": "1.6.13".

Thanks.

fedor-pavlov commented 2 years ago

I've got a task to generate dozens of PDF-links on a single page. After days and days of digging around this issue, I've found that:

1) The problem is caused by using fonts in PDF docs. 2) And it happens only if several PDF docs or links are being generated on a single page simultaneously (while using fonts)

So, the only workaround I've found is to line up all PDF docs (or PDF links) in a single queue and generate them one by one sequentially. It's easy to do by using a hook "useQueuedEffect" like so:

import { useState, useCallback } from 'react'; import { useQueuedEffect } from 'react-queued-effect'; import { pdf } from '@react-pdf/renderer';

function PDFLink({ filename, document }) {

const [ url, setUrl ] = useState();
const [ err, setErr ] = useState();

const renderPdf = useCallback(() =>

    pdf(document).toBlob()
        .then(blob => setUrl(URL.createObjectURL(blob)))
        .catch(err => setErr(err || 'Unknown error'))

, [document]);

useQueuedEffect(renderPdf);

if (url) {

    return <a download={ filename || 'download.pdf' } href={url}>Download PDF</a>
};

if (err) {

    console.error('PDFLink: failure to render a document', err)
    return <p>{ err }</p>
};

return <p>Loading...</p>;

}

fedor-pavlov commented 2 years ago

Hi Reymark,

I've just published a TypeScript-native version of the useQueuedEffect hook. Check version 2.0.0 at npmjs.org With the typescript-native version you shouldn't have any issues regarding TS types.

BR, Fedor | +7 905 794 8477 | @.***

On Fri, Oct 15, 2021 at 5:00 AM Reymark @.***> wrote:

Hi @fedor-pavlov https://github.com/fedor-pavlov ,

Thanks your reply and your library. Looks promising. But may I know how can I implement the useQueuedEffect on TypeScript?

I'm currently having an error after installing and importing it: Could not find a declaration file for module 'react-queued-effect'. 'react-queued-effect/index.js' implicitly has an 'any' type..

Thanks.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/diegomura/react-pdf/issues/777#issuecomment-943921649, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADDYEQJNRWBIRRJTA6OCRK3UG6DLZANCNFSM4JVVXTUQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

shaneiadt commented 2 years ago

If you're not using the PDFViewer this error doesn't seem to occur. Downloading the PDF with a registered & used font works otherwise.

burakakyuzobrosoft commented 1 year ago

use your local fontStyles. it will work!

import OpenSansBold from '@assets/fonts/feather/fonts/OpenSansBold.ttf'
import OpenSansExtraBold from '@assets/fonts/feather/fonts/OpenSansExtraBold.ttf'
import OpenSansRegular from '@assets/fonts/feather/fonts/OpenSansRegular.ttf'

const ResultPdf = () => {

  Font.register({
    family: 'MainFont',
    fonts: [
      {
        src: OpenSansRegular
      },
      {
        src: OpenSansBold,
        fontWeight: 'bold'
      },
      {
        src: OpenSansExtraBold,
        fontWeight: 'bold'
      }
    ]
  })

  const styles = StyleSheet.create({
    page: {
      fontFamily: 'MainFont'
    }
  })
 return (
    <Document>
      <Page
        style={styles.page}
      >
  //Components here
      </Page>
    </Document>
  )
}