98 lines
2.8 KiB
TypeScript
98 lines
2.8 KiB
TypeScript
import jsPDF from "jspdf";
|
|
import QRCode from "qrcode";
|
|
|
|
interface LabelData {
|
|
partNumber: string;
|
|
quantity: number;
|
|
}
|
|
|
|
/** Load an image from a URL and return its base64 data URL */
|
|
async function loadImageAsBase64(url: string): Promise<string> {
|
|
return new Promise((resolve, reject) => {
|
|
const img = new Image();
|
|
img.crossOrigin = "anonymous";
|
|
img.onload = () => {
|
|
const canvas = document.createElement("canvas");
|
|
canvas.width = img.naturalWidth;
|
|
canvas.height = img.naturalHeight;
|
|
const ctx = canvas.getContext("2d");
|
|
if (!ctx) return reject(new Error("Canvas not supported"));
|
|
ctx.drawImage(img, 0, 0);
|
|
resolve(canvas.toDataURL("image/png"));
|
|
};
|
|
img.onerror = () => reject(new Error("Failed to load image"));
|
|
img.src = url;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Generate a 49mm x 12mm label PDF with:
|
|
* - Company icon (left, square)
|
|
* - Code, Quantity, Count date (center)
|
|
* - QR code (right) containing "PartNumber,Quantity"
|
|
*/
|
|
export async function generateLabelPdf(data: LabelData): Promise<jsPDF> {
|
|
const labelW = 49; // mm
|
|
const labelH = 12; // mm
|
|
const contentLeft = 1; // small padding
|
|
const contentRight = labelW - 1;
|
|
|
|
const doc = new jsPDF({
|
|
orientation: "landscape",
|
|
unit: "mm",
|
|
format: [labelW, labelH],
|
|
});
|
|
|
|
const now = new Date();
|
|
const countDate = `${String(now.getDate()).padStart(2, "0")}/${String(now.getMonth() + 1).padStart(2, "0")}/${now.getFullYear()}`;
|
|
|
|
// --- Icon (left side, square, filling most of the label height) ---
|
|
const iconSize = 9;
|
|
const iconX = contentLeft;
|
|
const iconY = (labelH - iconSize) / 2;
|
|
try {
|
|
const iconBase64 = await loadImageAsBase64("/images/linse-icon.png");
|
|
doc.addImage(iconBase64, "PNG", iconX, iconY, iconSize, iconSize);
|
|
} catch {
|
|
// If icon fails, skip
|
|
}
|
|
|
|
// --- Text (center, maximised for 12mm height) ---
|
|
const textX = iconX + iconSize + 1.5;
|
|
|
|
// Code
|
|
doc.setFontSize(7);
|
|
doc.setFont("helvetica", "bold");
|
|
doc.setTextColor(0, 0, 0);
|
|
doc.text(`Code: ${data.partNumber}`, textX, 3.8);
|
|
|
|
// Quantity
|
|
doc.text(`Quantity: ${data.quantity}`, textX, 7.2);
|
|
|
|
// Count date
|
|
doc.setFontSize(5.5);
|
|
doc.setFont("helvetica", "normal");
|
|
doc.text(`Count date: ${countDate}`, textX, 10.4);
|
|
|
|
// --- QR Code (right side, filling most of the label height) ---
|
|
const qrContent = `${data.partNumber},${data.quantity}`;
|
|
try {
|
|
const qrDataUrl = await QRCode.toDataURL(qrContent, {
|
|
width: 200,
|
|
margin: 0,
|
|
errorCorrectionLevel: "M",
|
|
color: { dark: "#000000", light: "#ffffff" },
|
|
});
|
|
|
|
const qrSize = 10;
|
|
const qrX = contentRight - qrSize;
|
|
const qrY = (labelH - qrSize) / 2;
|
|
doc.addImage(qrDataUrl, "PNG", qrX, qrY, qrSize, qrSize);
|
|
} catch {
|
|
doc.setFontSize(4);
|
|
doc.text("QR Error", contentRight - 10, labelH / 2);
|
|
}
|
|
|
|
return doc;
|
|
}
|