0

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"
  }
}
Athos Franco
  • 67
  • 2
  • 8
  • And you searched for `"react-pdf useInsertionEffect"` already to find out if anyone else had similar errors? Because a quick search suggests you're using incompatible versions of react/react-dom – Mike 'Pomax' Kamermans Jul 11 '23 at 17:56
  • Yeah i did it and no, both my react and react-dom versions are the latest and the same. – Athos Franco Jul 11 '23 at 17:58
  • And what does your package.json say the versions are? (remember "latest" isn't an actual version =) – Mike 'Pomax' Kamermans Jul 11 '23 at 19:01
  • react 18.2.0 and react-dom 18.2.0 – Athos Franco Jul 11 '23 at 19:41
  • I added my full package.json file to the question – Athos Franco Jul 11 '23 at 19:42
  • Well, it doesn't convert to thermal code. When I received the task, I looked up how to generate receipts for thermal printers with React and found complex solutions that I was working to implement. However, when I told him what I was doing, he told me to 'just generate a PDF file' because he's sure the printer will be able to print it. He even said that he 'already did this before' (printing PDFs with this thermal printer). So my best guess is that he has this 'Zebra' thermal printer you're talking about. – Athos Franco Jul 12 '23 at 17:32

0 Answers0