How to implement react-pdf with print function

leonlai picture leonlai · Dec 30, 2018 · Viewed 12.5k times · Source

I would like to use react-pdf to display the PDF and develop a printing function for direct printing (like using window.print());

The REST server is developed using Jersey.

The PDF will generate from server and return to React client using Jersey with return type is application/pdf. React client will display the PDF using react-pdf.

I don't want to declare the URL path in "file" because this will retrieve the PDF from server again if the React state changed and triggered re-render. Also, I need to develop a print function to print the PDF displayed (Because the PDF content may change if retrieve the PDF again from server)

Below show my code:

Server:

@Override
@GET
@Path("/pdf")
@Produces(MediaType.APPLICATION_PDF_VALUE)
public Response testPdf() throws Exception {

    File file = new File("C:\\Desktop\\test.pdf");
    FileInputStream fileInputStream = new FileInputStream(file);
    
    ResponseBuilder response = Response.ok((Object) fileInputStream);
    response.type("application/pdf");
    response.header("Content-Disposition", "filename=test.pdf");
    
    return response.build();
}

Client

import React, { Component } from 'react';
import { Document, Page } from 'react-pdf';
import axios from 'axios';

class MyApp extends Component {
    state = {
        numPages: null,
        pageNumber: 1,
        pdfContent: null
    }

    componentDidMount(){
        var that = this;

        axios.get("url\Pdf").then((response) => {
             that.setState({pdfContent:response.data});
        }).catch((error) => {
             console.warn(error);
        });
    }

    onDocumentLoadSuccess = ({ numPages }) => {
        this.setState({ numPages });
    }

    printHandler(){
        window.print();
    }

    render() {
        const { pageNumber, numPages } = this.state;

        return (
            <div>
                <Document
                    file={this.state.pdfContent}
                    onLoadSuccess={this.onDocumentLoadSuccess}
                >
                    <Page pageNumber={pageNumber} />
                </Document>
                <p>Page {pageNumber} of {numPages}</p>

                <button onClick={() => this.setState(prevState => ({ 
                        pageNumber: prevState.pageNumber + 1 }))}>Next page</button>
                <button onClick={() => this.setState(prevState => ({ 
                        pageNumber: prevState.pageNumber - 1 }))}>Prev Page</button>

                <button onClick={this.printHandler}/>
            </div>
        );
    }
}

I want to get the PDF only one time and display the PDF using react-pdf. Also, I want to print the displayed PDF.

I tried to convert the response.data to base64 followed this line because not success: (this will lose the pdf content) Encode PDF to base64 in ReactJS

Code like:

    componentDidMount(){
        var that = this;

        axios.get("url\Pdf").then((response) => {
            let reader = new FileReader();
            var file = new Blob([response.data], { type: 'application/pdf' });

            reader.onloadend = () => {
                that.setState({
                    base64Pdf:reader.result
                });
            }
            reader.readAsDataURL(file);
        }).catch((error) => {
             console.warn(error);
        });
    }

Anyone can give me some suggestion? Or any better way to reach my goal?

Thanks

Answer

Reneta picture Reneta · Feb 4, 2019

Recently I got a similar use case with the pdf part, my request is Post, but you can make it Get with no problem. So, what is happening:

1) - I am using axios for making a request to the back-end:

2) - request is the object that I am sending, but you will not have such, since you will probably send only id, for example: axios.get('here.is.your/endpoint/id');

3) - I am using: file-saver for saving the file I receive.

The rest of the code should be self-explaining and I also added some comments.

import {saveAs} from "file-saver";
...

axios.post('here.is.your/endpoint', qs.parse(request), {
       headers: {
          'Content-Type': 'application/json'
       },   
       responseType: 'blob' // here I am forcing to receive data in a Blob Format
    })
    .then(response => {
        if (response.data) {
            //Create a Blob from the PDF Stream
            const file = new Blob(
                [response.data],
                {type: 'application/pdf'});
            const name = 'Report.pdf';
            saveAs(file, name);
        } else {
            throw new Error("Error in data type received.");
        }
    })
    .catch(error => {
        this.setState({
            modalMessage: "Here Add Custom Message"
        });
   });

I cannot get the error message from the back-end still, I will text back if I get some progress on it - for now, I show a custom message.

I hope that helps!

Wish you luck!