React-to-print: Can't Print Image On Some Components

Created on 18 Apr 2019  路  13Comments  路  Source: gregnb/react-to-print

I have three separate components all pulling images from google storage, one of which is able to print the images. I can't find anything that is different about the three. Is there something I should know about trying to print images from another source?

bug unverified

Most helpful comment

@MatthewHerbst yes that did the trick, thank you so much!

I solved it by using

import loadImage from "image-promise"

 <ReactToPrint
        onBeforePrint={() => {
          const image1 = 'imageUrl.....'
          const image2 = 'imageUrl.....'     

          return loadImage([image1, image2])
            .then(function(allImgs) {
              console.log(allImgs.length, "images loaded!", allImgs)
            })
            .catch(function(err) {
              console.error("One or more images have failed to load :(")
              console.error(err.errored)
              console.info("But these loaded fine:")
              console.info(err.loaded)
            })
        }}
        trigger={() => (
          <button>
          Print
          </button>
        )}
        content={() => pdfRef.current}
      />

All 13 comments

Hello. Can you please post some code, or ideally make a codesandbox or similar showing your problem? Thanks!

Okay, here's one of the components that aren't printing the image:

import React, { Component } from "react";

class ViewTimesheet extends Component {
  constructor(props) {
    super(props);
  }

  componentDidMount() {
    console.log(
      "expense report from ViewTimesheet : " +
        JSON.stringify(this.props.timesheet)
    );
  }
  render() {
    let date = new Date(this.props.timesheet.datetime);
    let dateString = date.toDateString();
    let imageString = this.props.timesheet.signature;
    console.log(date);
    return (
      <div className="viewReport" style={{ backgroundColor: "white" }}>
        <div
          className="dashboardPanelHeading"
          style={{ backgroundColor: "white" }}
        >
          <h3 className="display-5">
            Timesheet For Job #{this.props.timesheet.job}
          </h3>
          <p className="lead">
            Created By: {this.props.timesheet.createdBy.name}
            <br />
            Date: {dateString}
          </p>
        </div>
        <div>
          <hr />
          {this.props.timesheet.employeesAndHours.map((employee, i) => (
            <div key={i} className="employeeAndHours">
              {employee.name} - {employee.hours}
            </div>
          ))}
          <hr />
          <div className="pipeForm">
            <p className="lead font-weight-bold">
              Size and Type of HDPE Pipe Installed:
            </p>
            <p className="lead">
              {" "}
              {this.props.timesheet.hdpePipeForm.sizeType}
            </p>
            <p className="lead font-weight-bold">
              Size and Type of Wire Installed:
            </p>
            <p className="lead">
              {" "}
              {this.props.timesheet.hdpePipeForm.sizeTypeWire}
            </p>
            <p className="lead font-weight-bold">
              Total Usable Bore Footage Installed AT DEPTH:
            </p>
            <p className="lead">
              {" "}
              {this.props.timesheet.hdpePipeForm.boreFootage}
            </p>
          </div>
          <div className="pipeForm">
            <p className="lead font-weight-bold">
              Size and Type of Pipe Installed:
            </p>
            <p className="lead"> {this.props.timesheet.pipeForm1.sizeType}</p>
            <p className="lead font-weight-bold">
              Size and Type of Wire Installed:
            </p>
            <p className="lead">
              {" "}
              {this.props.timesheet.pipeForm1.sizeTypeWire}
            </p>
            <p className="lead font-weight-bold">
              Total Usable Bore Footage Installed AT DEPTH:
            </p>
            <p className="lead">
              {" "}
              {this.props.timesheet.pipeForm1.boreFootage}
            </p>
          </div>
          <div className="pipeForm">
            <p className="lead font-weight-bold">
              Size and Type of HDPE Pipe Installed:
            </p>
            <p className="lead"> {this.props.timesheet.pipeForm2.sizeType}</p>
            <p className="lead font-weight-bold">
              Size and Type of Wire Installed:{" "}
            </p>
            <p className="lead">
              {" "}
              {this.props.timesheet.pipeForm2.sizeTypeWire}
            </p>
            <p className="lead font-weight-bold">
              Total Usable Bore Footage Installed AT DEPTH:
            </p>
            <p className="lead">
              {" "}
              {this.props.timesheet.pipeForm2.boreFootage}
            </p>
          </div>
          <div className="pipeForm">
            <p className="lead font-weight-bold">
              Size and Type of HDPE Pipe Installed:{" "}
            </p>
            <p className="lead"> {this.props.timesheet.pipeForm3.sizeType}</p>
            <p className="lead font-weight-bold">
              Size and Type of Wire Installed:
            </p>
            <p className="lead">
              {" "}
              {this.props.timesheet.pipeForm3.sizeTypeWire}
            </p>
            <p className="lead font-weight-bold">
              Total Usable Bore Footage Installed AT DEPTH:{" "}
            </p>
            <p className="lead">
              {" "}
              {this.props.timesheet.pipeForm3.boreFootage}
            </p>
          </div>
          <div className="pipeForm">
            <p className="lead font-weight-bold">Notes:</p>
            <p className="lead">{this.props.timesheet.notes}</p>
          </div>
        </div>
        <div
          style={{
            display: "relative",
            width: "300px",
            border: "1px solid black"
          }}
          className="workerAndSignature timesheetReportSignature"
        >
          <p className="lead">{this.props.timesheet.createdBy.name}</p>
          <img
            style={{ width: "100%", height: "auto" }}
            src={this.props.timesheet.signature}
          />
        </div>
      </div>
    );
  }
}

export default ViewTimesheet;

And here's one that IS working, I just can't figure out the difference.

import React, { Component } from "react";

class ViewTailboard extends Component {
  constructor(props) {
    super(props);
  }

  componentDidMount() {
    console.log(
      "expense report from ViewTailboard : " +
        JSON.stringify(this.props.tailboard)
    );
  }
  render() {
    let questions = [
      "Has Everyone Involved Been Given Instructions To Perform Their Task?",
      "Crew Input And Concerns Addressed?",
      "Identified and discussed underground utilities? (If required, Miss Dig number Positive Response)",
      "Discussed ergonomic issues? (body posture/position, tasks requiring 2 employees, use of tools/equipment)",
      "Does everyone have the proper PPE to perform their tasks safely?",
      "Identified and discussed potential slip, trip and fall hazards?",
      "Implemented appropriate traffic control plan? Traffic Control Plan Used #",
      "Job hazard walk-through completed using Hazard and Control identification list?"
    ];
    let date = new Date(this.props.tailboard.dateTime);

    let dateString = date.toDateString();
    console.log(date);
    return (
      <div className="viewReport" style={{ backgroundColor: "white" }}>
        <div
          className="dashboardPanelHeading"
          style={{ backgroundColor: "white" }}
        >
          <h3 className="display-5">
            Tailboard For Job #{this.props.tailboard.job.id}
          </h3>
          <p className="lead">
            Created By: {this.props.tailboard.employee.name}
            <br />
            Date: {dateString}
          </p>
        </div>
        <div>
          <hr />
          <div className="generalSafetyQuestions">
            {questions.map((question, i) => (
              <p key={i} className="lead">
                {question} - YES
              </p>
            ))}
          </div>
          <div className="additionalHazards">
            <table className="table table-hover">
              <thead className="thead-dark">
                <tr>
                  <th className="sort" scope="col">
                    Additional Hazards
                  </th>
                  <th className="sort" scope="col">
                    Controls Implemented
                  </th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td>{this.props.tailboard.additionalHazards1}</td>
                  <td>{this.props.tailboard.controlsImplemented1}</td>
                </tr>
                <tr>
                  <td>{this.props.tailboard.additionalHazards2}</td>
                  <td>{this.props.tailboard.controlsImplemented2}</td>
                </tr>
                <tr>
                  <td>{this.props.tailboard.additionalHazards3}</td>
                  <td>{this.props.tailboard.controlsImplemented3}</td>
                </tr>
              </tbody>
            </table>
          </div>
          <div
            className="workersAndSignatures"
            style={{ display: "inline-block" }}
          >
            {this.props.tailboard.workersAndSignatures.map((worker, i) => (
              <div
                style={{
                  display: "inline-block",
                  width: "300px",
                  margin: "0 1em",
                  border: "1px solid black"
                }}
                className="workerAndSignature col-md-4"
              >
                <p className="lead">{worker.name}</p>
                <img
                  style={{ width: "100%", height: "auto" }}
                  src={worker.signaturePic}
                />
              </div>
            ))}
          </div>
        </div>
      </div>
    );
  }
}

export default ViewTailboard;

Hi @brettkoz any chance you could make a fully functional codesandbox for each please? Hard to see what's happening with that much code in a post and without all of its supporting code.

@brettkoz can you please upgrade to the newest version and see if that fixes the issue? We recently fixed some issues with fonts not loading properly which also impacted some image loading.

@brettkoz any update here please?

Im having the same issue, on version 2.1.3

Specifically it seems to be an issue only in chrome. When printing in firefox it always loads images, styles etc.

I also have issues with loading styles, and sometimes even text. My guess is that chrome does not always wait for the full render.

Ill see if i can get you a sandbox. Depends if my project manager will allow me to use the time.

@ArvidLangsoe would really appreciate a sandbox if you can make one, thank you

@MatthewHerbst Thank you for providing this library, you're making things really easier.

I am having the same issue with firebase storage / google cloud storage images.
I created a codesandbox.

Sorry, if it isn't stripped down to the very metal.

The picture in the header is from imgur, the one in the body from firebase storage. It sometimes needs 2-3 attempts to load the image from firestorage.

Any ideas how to fix this?

Thanks for sharing @Caruso33! So the issue is that react-to-print doesn't know that the images load async (it doesn't even know there are images in the thing to print). There is currently a 500ms timeout that we wait for async resources to load, so if the images load in under 500ms then they will show, if they load in over 500ms they will not.

I'm not sure if there is a way to know there are images and to wait for them to be loaded, though there may be. We can certainly make the timeout changeable via prop though if that would work for you?

Ok so I tried some workarounds with prefetching images but haven't got it working.
I thought somehow this should be possible.

Anyways, I guess as temporary solution the ability to increase the 500ms timeout would be sufficient.

@Caruso33 are you able to know in your code when the images have loaded? We recently published version 2.2.0 which allows for async operations in onBeforePrint. Maybe you could load the images into the browser cache in that function and then the timeout wouldn't matter?

@MatthewHerbst yes that did the trick, thank you so much!

I solved it by using

import loadImage from "image-promise"

 <ReactToPrint
        onBeforePrint={() => {
          const image1 = 'imageUrl.....'
          const image2 = 'imageUrl.....'     

          return loadImage([image1, image2])
            .then(function(allImgs) {
              console.log(allImgs.length, "images loaded!", allImgs)
            })
            .catch(function(err) {
              console.error("One or more images have failed to load :(")
              console.error(err.errored)
              console.info("But these loaded fine:")
              console.info(err.loaded)
            })
        }}
        trigger={() => (
          <button>
          Print
          </button>
        )}
        content={() => pdfRef.current}
      />

Awesome, glad to know that new feature has good use cases! Please let us know if you see anything else that's missing!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JoshuaKGoldberg picture JoshuaKGoldberg  路  3Comments

invious picture invious  路  4Comments

MikeSha picture MikeSha  路  4Comments

404sand808s picture 404sand808s  路  7Comments

hoangdoan267 picture hoangdoan267  路  8Comments