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:
- ThermalPrinterDialog, which is the dialog window that holds the PDF and the print button.
- ThermalPrinterPdf, which has the react-pdf generated PDF component
This is how the screen looks right now when the user opens the 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:
And this is what i expected to achieve when the user clicks the print button (edited image):
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"
}
}