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

renderToStream: NodeJS.ReadableStream will not complete, end event will not fire. #2706

Open
DaveFPath opened this issue Apr 6, 2024 · 4 comments

Comments

@DaveFPath
Copy link

Describe the bug
I am trying to create a pdf file on my backend and then serve it to my front end for download. Since renderToString has been deprecated, I am forced to use renderToStream. Then convert the stream to a buffer to send back in the response. I've tried sending the stream back directly in the response object but it throws an error.

export const renderPDF = async () => {

  // all the code that builds the pdf guts. "PDFComponent" is a react component that actually makes the pdf dom.
  const pdfGuts = { ... };

  const rootElemComponent: any = React.createElement(PDFComponent, pdfGuts);
  return ReactPDF.renderToStream(rootElemComponent);
};

export const renderToBuffer = (stream: NodeJS.ReadableStream) => {
  return new Promise<Buffer>(function (resolve, reject) {
    const chunks: any[] = [];
    stream.on("data", function (chunk: any) {
      return chunks.push(chunk);
    });
    stream.on("end", function () {
      return resolve(buffer.Buffer.concat(chunks));
    });
    stream.on("error", function (error: any) {
      return reject(error);
    });
  });
};


@Get(":id/billOfLading")
@HttpCode(HttpStatus.OK)
@Header("Content-Type", "application/pdf")
@Header("Response-Type", "arraybuffer")
@Header("Response-Encoding", "binary")
async getDocument(@CurrentUser() user, @Param("id") id, @Res() res) {
    
  const data: NodeJS.ReadableStream = await renderPDF();

  // convert the stream to an array bufferl
  const buffer = await renderToBuffer(data);  // <-- Boom! never returns anything... just freezes.

  // send the document to the client.
  res.send(buffer);
}

in my renderToBuffer function, the "data" event triggers just fine for each chunk, however, when it's finished, the "end" event never fires. Nor does the "finish" event.

I have tried several different ways of reading the stream, I have tried renderToFile as well and it too freezes up. (I'm assuming it's the same problem internally when writing the file, reading the stream never triggers and end event.)

Any thoughts?

To Reproduce
See code above.

Expected behavior
I expect that the readable stream will actually finish when all the data has been read.

Desktop (please complete the following information):

  • OS: Linux/PopOS
  • Browser: Chrome, but this is internal to a NodeJS backend server
  • React-pdf version: 3.4.2
  • Node Version: v18.20.1
@DaveFPath
Copy link
Author

Update:
I added a simple timer to the data event so that after 500 milliseconds if no data has come in, it manually emits the end event. This does trigger the event event and my code continues to run... however, the pdf that is output is unreadable as a pdf.

I also think that something under the hood is still operating and waiting because there is a memory leak when I try to execute the function multiple (a lot) times.

@DaveFPath
Copy link
Author

Found the exact source of the problem. It had to do with loading a font family from a CDN.
Here's a breakdown of what was going on:

In my document component, I would have the following

Font.register({
  family: "OpenSans",
  fonts: [
    {
      src: "https://cdn.jsdelivr.net/npm/@typopro/[email protected]/TypoPRO-OpenSans-Regular.ttf",
    },
    {
      src: "https://cdn.jsdelivr.net/npm/@typopro/[email protected]/TypoPRO-OpenSans-Bold.ttf",
      fontWeight: "bold",
    },
  ],
});

const styles = StyleSheet.create({
  page: {
    height: "100%",
    display: "none",
    backgroundColor: "white",
    paddingTop: 20,
    paddingBottom: 30,
    paddingHorizontal: 10,
    fontFamily: "OpenSans",
    fontSize: 8,
  },
});

export const BillOfLading: React.FC<BillOfLadingProps> = props => {
  
  ... 

  return (
    <Document>
        <Page size="A4" style={styles.page}>
          <Text>Test #14</Text>
        </Page>
    </Document>
}

Now, when I run the above, the document will not generate.
If I simply remove the style for the fontFamily, everything works just fine.

My solution was to remove the OpenSans font family all together and just use the default Helvetica

I hope this helps find the source of the problem.

@Katli95
Copy link

Katli95 commented Apr 11, 2024

I'm running into the same, loading fonts from local, but still, using non-standard fonts.

@Katli95
Copy link

Katli95 commented Apr 11, 2024

I can confirm in my case it's a duplicate of #2675 which is primarily worded as a front-end issue, but npm i [email protected] stopped me from running into the issue. I'd suggest to close this as a duplicate and add it to the description of the other issue.

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

No branches or pull requests

2 participants