Failing to print a PDF file generated with "react-pdf" library. Weird error messages at console

700 views Asked by At

This project was created using Vite, React and TypeScript.

I'm using the "react-pdf" library to generate a small PDF with some information needed by the user. Its supposed to look like a receipt and it will be printed by a thermal printer that can print basically any PDF if it is configured properly (page size etc). I can generate the PDF without any problems and render it to the screen. But at the moment I need to implement the 'print' feature of this same PDF, but I'm not having success into making this happen. Im using "printJS" library too.

Currently i have two files:

  1. ThermalPrinterDialog, which is the dialog window that holds the PDF and the print button.
  2. ThermalPrinterPdf, which has the react-pdf generated PDF component

This is how the screen looks right now when the user opens the ThermalPrinterDialog:

Screenshot of ThermalPrinterDialog

At ThermalPrinterDialog, when the user clicks on the "Imprimir" (print) button, it calls the handlePrint function. The handlePrint function looks like this atm:

  const handlePrint = async () => {
    const pdfBlob = await pdf(
      <ThermalPrinterPdf seller={seller} order={order} />
    ).toBlob();

    const pdfUrl = URL.createObjectURL(pdfBlob);
    
    console.log("result:", pdfUrl); 

    printJS(pdfUrl);
  };

Im using toBlob() function to first convert to blob to then be able to use printJS, as recommended by react-pdf docs, but i get this weird error at console.log whenever i call the handlePrint function:

Error

And this is what i expected to achieve when the user clicks the print button (edited image): Browser print window with the generated PDF

This is the full code of "ThermalPrinterDialog.tsx" file:

import { Close, PrintTwoTone, ReceiptTwoTone } from "@mui/icons-material";
import {
  Box,
  Button,
  Dialog,
  DialogActions,
  DialogTitle,
  Divider,
  IconButton,
  Stack,
} from "@mui/material";
import { useState } from "react";
import ThermalPrinterPdf from "./ThermalPrinterPdf";
import { pdf } from "@react-pdf/renderer";

import useSeller from "../../../stores/useSeller";
import printJS from "print-js";

interface ThermalPrinterDialogProps {
  order: any;
}

export default function ThermalPrinterDialog({
  order,
}: ThermalPrinterDialogProps) {
  const [isPrinterDialogOpen, setIsPrinterDialogOpen] = useState(false);


  const handleOpenPrinterDialog = (e: any) => {
    e.stopPropagation();
    setIsPrinterDialogOpen(true);
  };

  const handleClosePrinterDialog = () => {
    setIsPrinterDialogOpen(false);
  };

  const seller = useSeller((state) => state.seller);

  const handlePrint = async () => {
    const pdfBlob = await pdf(
      <ThermalPrinterPdf seller={seller} order={order} />
    ).toBlob();

    console.log("pdfBlob:", pdfBlob);

    const pdfUrl = URL.createObjectURL(pdfBlob);

    printJS(pdfUrl);
  };

  return (
    <>
      <IconButton onClick={(e) => handleOpenPrinterDialog(e)}>
        <ReceiptTwoTone />
      </IconButton>
      <Dialog open={isPrinterDialogOpen} onClose={handleClosePrinterDialog}>
        <Box sx={{ padding: 1 }}>
          <Stack
            direction="row"
            alignItems="center"
            justifyContent="space-between"
          >
            <DialogTitle>Extratos</DialogTitle>
            <IconButton onClick={handleClosePrinterDialog}>
              <Close />
            </IconButton>
          </Stack>
          <ThermalPrinterPdf order={order} seller={seller} />

          <Divider sx={{ mt: 2 }} />
          <DialogActions>
            <Button
              startIcon={<PrintTwoTone />}
              variant="contained"
              onClick={handlePrint}
            >
              Imprimir
            </Button>
          </DialogActions>
        </Box>
      </Dialog>
    </>
  );
}

And this is the full code of "ThermalPrinterPdf":

import { Divider } from "@mui/material";
import {
  Page,
  Text,
  View,
  Document,
  StyleSheet,
  Font,
} from "@react-pdf/renderer";
import Barcode from "../BarCode/BarCode";
import useSeller from "../../../stores/useSeller";
import { formatCNPJ } from "../../../utils/generalFunctions";
import dayjs from "dayjs";

const courierNewFont = {
  family: "Courier New",
  src: `https://fonts.gstatic.com/s/couriernew/v15/CourierNewPSMT.ttf`,
};

Font.register(courierNewFont);

const PDF_WIDTH = 287.244;
const styles = StyleSheet.create({
  page: {
    display: "flex",
    flexDirection: "column",
    backgroundColor: "#fff",
    border: "1px solid lightgray",
    padding: "1rem",
    width: PDF_WIDTH,
    fontFamily: "Courier New",
  },
  section: {
    display: "flex",
    justifyContent: "space-between",
    width: "100%",
    margin: "5px 0",
  },

  text: {
    fontSize: "10px",
  },
  smallCol: {
    fontSize: "10px",
    width: "16%",
  },
  largeCol: {
    fontSize: "10px",
    marginRight: "10px",
    width: "50%",
  },
  price: {
    fontSize: "10px",
  },

  divider: {
    backgroundColor: "#242424",
    height: "1px",
    margin: "10px 0",
  },
  barcode: {
    display: "flex",
    justifyContent: "center",
    marginTop: "10px",
  },
  sellerName: {
    fontSize: "16px",
    fontWeight: "bold",
  },
  header: {
    border: "1px solid lightgray",
    padding: "5px",
    display: "flex",
    flexDirection: "column",
    marginBottom: "10px",
  },
});

interface ThermalPrinterPdfProps {
  order: any;
  seller: any;
}

export default function ThermalPrinterPdf({
  order,
  seller,
}: ThermalPrinterPdfProps) {
  return (
    <Document>
      <Page size={{ width: PDF_WIDTH }} style={styles.page}>
        <View style={styles.header}>
          <Text style={styles.sellerName}>
            {seller?.sellerInfo.seller_name}
          </Text>
          <Text style={styles.text}>
            CNPJ: {formatCNPJ(seller?.sellerInfo.seller_cnpj)}
          </Text>
        </View>
        <View style={styles.section}>
          <Text style={styles.text}>OC: {order.order_id}</Text>
          <Text style={styles.text}>
            Data: {dayjs(order.iso_date).format("DD/MM/YYYY")}
          </Text>
        </View>

        <Divider />
        <View style={styles.section}>
          <Text style={styles.smallCol}>COD. INT.</Text>
          <Text style={styles.smallCol}>COD. MP.</Text>
          <Text style={styles.largeCol}>DESCRIÇÃO DO ITEM</Text>
          <Text style={styles.smallCol}>QTD.</Text>
        </View>
        <Divider />
        {order?.items.map((item: any) => {
          return (
            <View style={styles.section}>
              <Text style={styles.smallCol}>{item.seller_product_id}</Text>
              <Text style={styles.smallCol}>{item.product_id}</Text>
              <Text style={styles.largeCol}>{item.description}</Text>
              <Text style={styles.smallCol}>
                {Number(item.quantity).toFixed()} UN.
              </Text>
            </View>
          );
        })}

        <Divider />
        <View style={styles.barcode}>
          <Barcode
            value={order.order_id}
            options={{ width: 2.9, height: 40, text: " " }}
          />
        </View>
      </Page>
    </Document>
  );
}

Can someone help me somehow? I already tried with base64 and failed too, with the same error.

EDIT: package.json file below

{
  "name": "dash-frontend-2",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "@apollo/client": "^3.7.1",
    "@emotion/react": "^11.10.5",
    "@emotion/styled": "^11.10.5",
    "@mui/icons-material": "^5.10.14",
    "@mui/material": "^5.10.14",
    "@mui/types": "^7.2.1",
    "@mui/x-date-pickers": "^5.0.8",
    "@react-pdf/renderer": "^3.0.1",
    "@types/draft-convert": "^2.1.4",
    "@types/file-saver": "^2.0.5",
    "@types/react-chartjs-2": "^2.5.7",
    "@types/react-draft-wysiwyg": "^1.13.4",
    "@types/react-image-gallery": "^1.2.0",
    "@types/react-pdf": "^6.2.0",
    "@types/recharts": "^1.8.24",
    "apollo-link-token-refresh": "^0.4.0",
    "axios": "^1.3.0",
    "chart.js": "^4.0.1",
    "chartjs-plugin-datalabels": "^2.1.0",
    "dayjs": "^1.11.6",
    "draft-convert": "^2.1.13",
    "draft-js": "^0.11.7",
    "file-saver": "^2.0.5",
    "graphql": "^16.6.0",
    "jsbarcode": "^3.11.5",
    "jwt-decode": "^3.1.2",
    "leaflet": "^1.9.3",
    "mui-file-dropzone": "^4.0.2",
    "pdf-lib": "^1.17.1",
    "print-js": "^1.6.0",
    "react": "^18.2.0",
    "react-barcode": "^1.4.6",
    "react-chartjs-2": "^5.0.1",
    "react-dom": "^18.2.0",
    "react-draft-wysiwyg": "^1.15.0",
    "react-geocode": "^0.2.3",
    "react-hook-form": "^7.39.4",
    "react-horizontal-scrolling-menu": "^3.2.3",
    "react-image-gallery": "^1.2.11",
    "react-infinite-scroll-component": "^6.1.0",
    "react-json-tree": "^0.18.0",
    "react-json-view": "^1.21.3",
    "react-leaflet": "^4.2.0",
    "react-number-format": "^5.1.4",
    "react-pdf": "^6.2.0",
    "react-router": "^6.4.3",
    "react-router-dom": "^6.4.3",
    "react-tabs": "^6.0.0",
    "react-to-print": "^2.14.13",
    "react-toastify": "^9.1.1",
    "react-waypoint": "^10.3.0",
    "recharts": "^2.3.2",
    "use-detect-print": "^0.0.2",
    "xlsx": "^0.18.5",
    "zustand": "^4.1.4"
  },
  "devDependencies": {
    "@types/leaflet": "^1.9.1",
    "@types/react": "^18.0.24",
    "@types/react-dom": "^18.0.8",
    "@types/react-geocode": "^0.2.1",
    "@types/react-leaflet": "^2.8.3",
    "@vitejs/plugin-react": "^2.2.0",
    "typescript": "^4.6.4",
    "vite": "^3.2.3"
  }
}
0

There are 0 answers