UNPKG

node-sped-pdf

Version:

> 📄 Conversão simples e prática de XML (NFe/NFC-e) para PDF (DANFE), compatível com Node.js e Browser.

941 lines (940 loc) • 85 kB
// src/libs/danfe.ts import { PDFDocument, StandardFonts, rgb } from "pdf-lib"; import { XMLParser } from "fast-xml-parser"; import JsBarcode from "jsbarcode"; var DANFe = async (data = {}) => { const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "@", parseTagValue: false // Evita conversão automática de valores }); function normalizarXML(xml2) { if (xml2 == "Bad Request") throw xml2; const clear = [ "S:Envelope", "S:Body", "soapenv:Envelope", "soapenv:Body", "soap:Envelope", "soap:Body", "nfeResultMsg", "nfeDistDFeInteresseResponse", "protNFe", "enviNFe", "nfeProc" ]; let index = 0; while (index < clear.length) { if (typeof xml2[clear[index]] !== "undefined") { xml2 = { ...xml2, ...xml2[clear[index]] }; delete xml2[clear[index]]; index = 0; } else { index++; } } return xml2; } var PDF = { doc: await PDFDocument.create(), pages: [], width: 0, height: 0, mtBlock: 0, barCode: null }, isBrowser = typeof window !== "undefined", xml = normalizarXML(parser.parse(data.xml || "")), consulta = typeof data.consulta != "undefined" ? parser.parse(data.consulta) : {}, logo = data.logo, imgDemo = data.imgDemo, protNFe = null; if (typeof consulta?.retConsSitNFe?.procEventoNFe != "undefined") consulta.retConsSitNFe.procEventoNFe = Array.isArray(consulta.retConsSitNFe.procEventoNFe) ? consulta.retConsSitNFe.procEventoNFe : [consulta.retConsSitNFe.procEventoNFe]; if (!xml?.NFe?.infNFe) { throw new Error("XML inv\xE1lido para DANFe: n\xE3o foi encontrada a tag NFe/infNFe."); } PDF.pages.push(PDF.doc.addPage()); PDF.width = PDF.pages[0].getWidth(); PDF.height = PDF.pages[0].getHeight(); async function addRet(page, x, y, w, h) { page.drawRectangle({ x: x + 4, y: PDF.height - h - (y + 4), width: x + w + 8 >= PDF.width ? PDF.width - x - 8 : w, height: h, borderColor: rgb(0, 0, 0), borderWidth: 1 }); } async function addLTH(page, x, y, h) { const startX = Math.max(x, 4); const endX = Math.min(x + h, PDF.width - 4); const fixedY = PDF.height - y - 4; page.drawLine({ start: { x: startX, y: fixedY }, end: { x: endX, y: fixedY }, color: rgb(0, 0, 0), thickness: 1, dashArray: [5, 3] }); } async function addLTV(page, x, y, w) { const fixedX = Math.max(4, Math.min(x, PDF.width - 4)); const startY = Math.max(PDF.height - y - 4, 4); const endY = Math.max(PDF.height - (y + w) - 4, 4); page.drawLine({ start: { x: fixedX, y: startY }, end: { x: fixedX, y: endY }, color: rgb(0, 0, 0), thickness: 1, dashArray: [5, 3] }); } async function addTXT({ page, text, x, y, maxWidth, fontStyle = "normal", size = 7, lineHeight, align = "left", cacl = false, opacity = 1 }) { let font; switch (fontStyle) { case "negrito": font = await PDF.doc.embedFont(StandardFonts.TimesRomanBold); break; case "italic": font = await PDF.doc.embedFont(StandardFonts.TimesRomanItalic); break; default: font = await PDF.doc.embedFont(StandardFonts.TimesRoman); } if (maxWidth + x > PDF.width) maxWidth = PDF.width - x - 5; const effectiveLineHeight = lineHeight ?? size * 0.9; const lines = wrapText(text, maxWidth, font, size); if (cacl) return lines.length; lines.forEach((line, index) => { const textWidth = font.widthOfTextAtSize(line, size); let drawX = x + 4; if (align === "center") { drawX = x + (maxWidth - textWidth) / 2; } else if (align === "right") { drawX = x + maxWidth - textWidth; } page.drawText(line, { x: drawX, y: PDF.height - effectiveLineHeight - (y + 4) - index * effectiveLineHeight, size, font, opacity: opacity || 1 }); }); return lines.length; } function wrapText(text, maxWidth, font, fontSize) { const words = text.split(" "); const lines = []; let line = ""; for (let i = 0; i < words.length; i++) { const word = words[i]; const testLine = line + word + " "; const testWidth = font.widthOfTextAtSize(testLine, fontSize); if (testWidth > maxWidth && line !== "") { lines.push(line.trim()); line = word + " "; } else { line = testLine; } } if (line.trim() !== "") { lines.push(line.trim()); } return lines; } function embCNPJCPF(valor) { const numeros = valor.replace(/\D/g, ""); if (numeros.length === 11) { return numeros.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4"); } else if (numeros.length === 14) { return numeros.replace(/(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/, "$1.$2.$3/$4-$5"); } else { return valor; } } async function gerarBlocos() { await bloco0(); await bloco1(); await bloco2(); await bloco3(); await bloco4(); await bloco5(); let fim = await bloco6(); await bloco7(); await bloco8(); while (!fim) { PDF.mtBlock = 0; PDF.pages.push(PDF.doc.addPage()); await bloco1(); fim = await bloco6(); } for (const [i, page] of PDF.pages.entries()) { addTXT({ page, size: 8, text: `Folha ${i + 1}/${PDF.pages.length}`, x: 235, y: i == 0 ? 142 : 82, maxWidth: PDF.width * 0.19, align: "center", fontStyle: "italic" }); if (xml.NFe.infNFe.ide.tpAmb == "2") { addTXT({ page, size: 30, text: `NFe EMITIDA EM HOMOLOGA\xC7\xC3O SEM VALOR FISCAL`, x: 0, y: PDF.height * 0.5, maxWidth: PDF.width, align: "center", opacity: 0.5, fontStyle: "negrito" }); } if (typeof consulta?.retConsSitNFe?.procEventoNFe != "undefined") { for (const event of consulta.retConsSitNFe.procEventoNFe) { if (event.retEvento.infEvento.tpEvento == "110111") { addTXT({ page, size: 50, text: `CANCELADA`, x: 0, y: PDF.height * 0.6, maxWidth: PDF.width, align: "center", fontStyle: "negrito" }); } } } } } async function bloco0(page = PDF.pages[PDF.pages.length - 1]) { addRet(page, 0, PDF.mtBlock + 0, PDF.width, 50); addRet(page, 0, PDF.mtBlock + 0, PDF.width * 0.8, 25); addRet(page, 0, PDF.mtBlock + 0, PDF.width * 0.8, 25); addRet(page, 0, PDF.mtBlock + 25, PDF.width * 0.8, 25); addRet(page, PDF.width * 0.17, PDF.mtBlock + 25, PDF.width * 0.63, 25); addTXT({ page, text: `RECEBEMOS DE ${xml.NFe.infNFe.emit.xNome} OS PRODUTOS E/OU SERVI\xC7OS CONSTANTES DA NOTA FISCAL ELETR\xD4NICA INDICADA ABAIXO. EMISS\xC3O: ${new Date(xml.NFe.infNFe.ide.dhEmi).toLocaleDateString("pt-BR")} VALOR TOTAL: ${parseFloat(xml.NFe.infNFe.total.ICMSTot.vNF).toLocaleString("pt-BR", { style: "currency", currency: "BRL" })} DESTINAT\xC1RIO: NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL - ${xml.NFe.infNFe.dest.enderDest.xLgr}, ${xml.NFe.infNFe.dest.enderDest.nro} ${xml.NFe.infNFe.dest.enderDest.xBairro} ${xml.NFe.infNFe.dest.enderDest.xMun}-${xml.NFe.infNFe.dest.enderDest.UF}`, x: 2, y: PDF.mtBlock + 2, maxWidth: PDF.width * 0.78 }); addTXT({ page, text: "DATA DE RECEBIMENTO", x: 2, y: PDF.mtBlock + 25, maxWidth: PDF.width * 0.78 }); addTXT({ page, text: "ASSINATURA DO RECEBEDOR", x: PDF.width * 0.173, y: PDF.mtBlock + 25, maxWidth: PDF.width }); addTXT({ page, size: 18, text: "NFe", x: PDF.width * 0.8, y: PDF.mtBlock, maxWidth: PDF.width * 0.8, align: "center", fontStyle: "negrito" }); addTXT({ page, size: 11, text: `N\xBA. ${xml.NFe.infNFe.ide.nNF.padStart(9, "0")}`, x: PDF.width * 0.8, y: PDF.mtBlock + 20, maxWidth: PDF.width * 0.8, align: "center", fontStyle: "negrito" }); addTXT({ page, size: 11, text: `S\xE9rie ${xml.NFe.infNFe.ide.serie.padStart(3, "0")}`, x: PDF.width * 0.8, y: PDF.mtBlock + 30, maxWidth: PDF.width * 0.8, align: "center", fontStyle: "negrito" }); addLTH(page, 0, PDF.mtBlock + 56, PDF.width); PDF.mtBlock += 60; } async function bloco1(page = PDF.pages[PDF.pages.length - 1]) { addRet(page, 0, PDF.mtBlock, PDF.width, 132); addRet(page, 0, PDF.mtBlock, PDF.width, 92); addRet(page, 0, PDF.mtBlock, PDF.width, 112); addRet(page, PDF.width * 0.401, PDF.mtBlock + 0, PDF.width, 92); addRet(page, PDF.width * 0.53, PDF.mtBlock + 38, 16, 20); addRet(page, PDF.width * 0.57, PDF.mtBlock + 0, PDF.width, 47); addRet(page, PDF.width * 0.57, PDF.mtBlock + 47, PDF.width, 23); addRet(page, PDF.width * 0.57, PDF.mtBlock + 70, PDF.width, 22); addRet(page, PDF.width * 0.57, PDF.mtBlock + 92, PDF.width, 20); addRet(page, PDF.width * 0.745, PDF.mtBlock + 112, PDF.width, 20); addRet(page, PDF.width * 0.497, PDF.mtBlock + 112, PDF.width, 20); addRet(page, PDF.width * 0.25, PDF.mtBlock + 112, PDF.width, 20); addTXT({ page, text: "IDENTIFICA\xC7\xC3O DO EMITENTE", x: 0, y: PDF.mtBlock + 2, maxWidth: PDF.width * 0.4, align: "center" }); let mt = 0; if (typeof logo !== "undefined") { await addIMG({ page, img: logo, x: PDF.width * 0.18, y: PDF.mtBlock + 14, h: 37, w: 37 }); mt += 12; } let sizeNome = 12; while (await addTXT({ page, size: sizeNome, text: `${xml.NFe.infNFe.emit.xNome}`, x: 1, y: PDF.mtBlock + 35 + mt, maxWidth: PDF.width * 0.4, align: "center", fontStyle: "negrito", cacl: true }) >= 2) { sizeNome--; } addTXT({ page, size: sizeNome, text: `${xml.NFe.infNFe.emit.xNome}`, x: 1, y: PDF.mtBlock + 35 + mt, maxWidth: PDF.width * 0.4, align: "center", fontStyle: "negrito" }); addTXT({ page, size: 9, text: `${xml.NFe.infNFe.emit.enderEmit.xLgr}, N\xB0${xml.NFe.infNFe.emit.enderEmit.nro}`, x: 0, y: PDF.mtBlock + 45 + mt, maxWidth: PDF.width * 0.42, align: "center" }); addTXT({ page, size: 9, text: `${xml.NFe.infNFe.emit.enderEmit.xBairro} - ${xml.NFe.infNFe.emit.enderEmit.CEP}`, x: 0, y: PDF.mtBlock + 55 + mt, maxWidth: PDF.width * 0.42, align: "center" }); addTXT({ page, size: 9, text: `${xml.NFe.infNFe.emit.enderEmit.xMun} - ${xml.NFe.infNFe.emit.enderEmit.UF} Fone: ${xml.NFe.infNFe.emit.enderEmit?.fone || ""}`, x: 0, y: PDF.mtBlock + 65 + mt, maxWidth: PDF.width * 0.42, align: "center" }); addTXT({ page, size: 16, text: "DANFE", x: PDF.width * 0.393, y: PDF.mtBlock + 3, maxWidth: PDF.width * 0.2, align: "center", fontStyle: "negrito" }); addTXT({ page, size: 8, text: "Documento Auxiliar da Nota Fiscal Eletr\xF4nica", x: PDF.width * 0.4, y: PDF.mtBlock + 19, maxWidth: PDF.width * 0.18, align: "center" }); addTXT({ page, size: 8, text: "0 - ENTRADA", x: PDF.width * 0.415, y: PDF.mtBlock + 42, maxWidth: PDF.width * 0.19, align: "left" }); addTXT({ page, size: 8, text: "1 - SA\xCDDA", x: PDF.width * 0.415, y: PDF.mtBlock + 50, maxWidth: PDF.width * 0.19, align: "left" }); addTXT({ page, size: 20, text: xml.NFe.infNFe.ide.tpNF, x: PDF.width * 0.534, y: PDF.mtBlock + 37, maxWidth: PDF.width * 0.19, align: "left" }); addTXT({ page, size: 10, text: `N\xBA. ${xml.NFe.infNFe.ide.nNF.padStart(9, "0")}`, x: PDF.width * 0.4, y: PDF.mtBlock + 63, maxWidth: PDF.width * 0.19, align: "center", fontStyle: "negrito" }); addTXT({ page, size: 10, text: `S\xE9rie ${xml.NFe.infNFe.ide.serie.padStart(3, "0")}`, x: PDF.width * 0.398, y: PDF.mtBlock + 72, maxWidth: PDF.width * 0.19, align: "center", fontStyle: "negrito" }); await addIMG({ page, img: await barCode(), x: PDF.width * 0.595, y: PDF.mtBlock + 6, w: PDF.width * 0.39, h: 44 }); addTXT({ page, text: "CHAVE DE ACESSO", x: PDF.width * 0.575, y: PDF.mtBlock + 47, maxWidth: PDF.width * 0.19 }); addTXT({ page, size: 8, text: xml.NFe.infNFe["@Id"].replace("NFe", "").replace(/(\d{4})(?=\d)/g, "$1 "), x: PDF.width * 0.595, y: PDF.mtBlock + 58, maxWidth: PDF.width * 0.39, align: "center", fontStyle: "negrito" }); addTXT({ page, size: 8, text: "Consulta de autenticidade no portal nacional da NF-e", x: PDF.width * 0.595, y: PDF.mtBlock + 70, maxWidth: PDF.width * 0.39, align: "center" }); addTXT({ page, size: 8, text: " www.nfe.fazenda.gov.br/portal ou no site da Sefaz Autorizadora", x: PDF.width * 0.595, y: PDF.mtBlock + 81, maxWidth: PDF.width * 0.39, align: "center" }); addTXT({ page, text: "PROTOCOLO DE AUTORIZA\xC7\xC3O DE USO", x: PDF.width * 0.575, y: PDF.mtBlock + 92, maxWidth: PDF.width * 0.29 }); addTXT({ page, size: 10, text: `${xml.protNFe?.infProt?.nProt || ""} - ${xml.protNFe?.infProt?.dhRecbto ? new Date(xml.protNFe.infProt.dhRecbto).toLocaleString("pt-BR") : ""}`, x: PDF.width * 0.595, y: PDF.mtBlock + 101, maxWidth: PDF.width * 0.39, align: "center", fontStyle: "negrito" }); addTXT({ page, text: "NATUREZA DA OPERA\xC7\xC3O", x: 3, y: PDF.mtBlock + 92, maxWidth: PDF.width * 0.29 }); addTXT({ page, size: 10, text: xml.NFe.infNFe.ide.natOp, x: 3, y: PDF.mtBlock + 101, maxWidth: PDF.width * 0.58, align: "center", fontStyle: "negrito" }); addTXT({ page, text: "INSCRI\xC7\xC3O ESTADUAL", x: 3, y: PDF.mtBlock + 112, maxWidth: PDF.width * 0.29 }); addTXT({ page, size: 10, text: xml.NFe.infNFe.emit.IE || "", x: 3, y: PDF.mtBlock + 121, maxWidth: PDF.width * 0.25, align: "center", fontStyle: "negrito" }); addTXT({ page, text: "INSCRI\xC7\xC3O MUNICIPAL", x: PDF.width * 0.255, y: PDF.mtBlock + 112, maxWidth: PDF.width * 0.29 }); addTXT({ page, size: 10, text: xml.NFe.infNFe.emit.IM || "", x: PDF.width * 0.355, y: PDF.mtBlock + 121, maxWidth: PDF.width * 0.05, align: "center", fontStyle: "negrito" }); addTXT({ page, text: "INSCRI\xC7\xC3O ESTADUAL DO SUBST. TRIBUT.", x: PDF.width * 0.5, y: PDF.mtBlock + 112, maxWidth: PDF.width * 0.29 }); addTXT({ page, size: 10, text: xml.NFe.infNFe.emit.IEST || "", x: PDF.width * 0.6, y: PDF.mtBlock + 121, maxWidth: PDF.width * 0.05, align: "center", fontStyle: "negrito" }); addTXT({ page, text: "CNPJ/CPF", x: PDF.width * 0.75, y: PDF.mtBlock + 112, maxWidth: PDF.width * 0.29 }); addTXT({ page, size: 10, text: embCNPJCPF(xml.NFe.infNFe.emit?.CNPJ || xml.NFe.infNFe.emit?.CPF || ""), x: PDF.width * 0.845, y: PDF.mtBlock + 121, maxWidth: PDF.width * 0.05, align: "center", fontStyle: "negrito" }); PDF.mtBlock += 133; } async function barCode() { if (PDF.barCode != null) return PDF.barCode; const isNode = typeof window === "undefined"; if (isNode) { const { createCanvas } = await import("canvas"); const canvas = createCanvas(400, 100); JsBarcode(canvas, xml.NFe.infNFe["@Id"], { format: "CODE128", displayValue: false, fontSize: 18 }); PDF.barCode = canvas.toDataURL("image/png"); return PDF.barCode; } else { return new Promise((resolve, reject) => { try { const canvas = document.createElement("canvas"); JsBarcode(canvas, xml.NFe.infNFe["@Id"], { format: "CODE128", displayValue: false, fontSize: 18 }); PDF.barCode = canvas.toDataURL("image/png"); resolve(PDF.barCode); } catch (err) { reject(err); } }); } } async function bloco2(page = PDF.pages[PDF.pages.length - 1]) { addRet(page, 0, PDF.mtBlock + 10, PDF.width * 0.603, 20); addRet(page, PDF.width * 0.603, PDF.mtBlock + 10, PDF.width * 0.222, 20); addRet(page, PDF.width * 0.825, PDF.mtBlock + 10, PDF.width * 0.2, 20); addRet(page, PDF.width * 0.665, PDF.mtBlock + 30, PDF.width, 20); addRet(page, PDF.width * 0.825, PDF.mtBlock + 50, PDF.width * 0.2, 20); addRet(page, PDF.width * 0.665, PDF.mtBlock + 30, PDF.width * 0.16, 40); addRet(page, PDF.width * 0.503, PDF.mtBlock + 50, PDF.width * 0.162, 20); addRet(page, PDF.width * 0.465, PDF.mtBlock + 50, PDF.width * 0.038, 20); addRet(page, PDF.width * 0, PDF.mtBlock + 50, PDF.width * 0.465, 20); addRet(page, PDF.width * 0, PDF.mtBlock + 30, PDF.width * 0.465, 20); addTXT({ page, text: "DESTINAT\xC1RIO / REMETENTE", x: 3, y: PDF.mtBlock + 2, maxWidth: PDF.width * 0.4, fontStyle: "negrito" }); addTXT({ page, text: "NOME / RAZ\xC3O SOCIAL", x: 3, y: PDF.mtBlock + 10, maxWidth: PDF.width * 0.4 }); addTXT({ page, size: 9, text: xml.NFe.infNFe.dest.xNome, x: 3, y: PDF.mtBlock + 20, maxWidth: PDF.width * 0.58, fontStyle: "negrito" }); addTXT({ page, text: "CNPJ/CPF", x: PDF.width * 0.61, y: PDF.mtBlock + 10, maxWidth: PDF.width * 0.4 }); addTXT({ page, size: 9, text: embCNPJCPF(xml.NFe.infNFe.dest?.CNPJ || xml.NFe.infNFe.dest?.CPF || ""), x: PDF.width * 0.51, y: PDF.mtBlock + 20, maxWidth: PDF.width * 0.42, align: "center", fontStyle: "negrito" }); addTXT({ page, text: "DATA DA EMISS\xC3O", x: PDF.width * 0.83, y: PDF.mtBlock + 10, maxWidth: PDF.width * 0.4 }); addTXT({ page, size: 9, text: new Date(xml.NFe.infNFe.ide.dhEmi).toLocaleDateString("pt-BR"), x: PDF.width * 0.83, y: PDF.mtBlock + 20, maxWidth: PDF.width * 0.42, align: "center", fontStyle: "negrito" }); addTXT({ page, text: "ENDERE\xC7O", x: 2, y: PDF.mtBlock + 31, maxWidth: PDF.width * 0.4 }); addTXT({ page, size: 9, text: `${xml.NFe.infNFe.dest.enderDest.xLgr}, N\xB0 ${xml.NFe.infNFe.dest.enderDest.nro}`, x: 3, y: PDF.mtBlock + 40, maxWidth: PDF.width * 0.42, align: "left", fontStyle: "negrito" }); addTXT({ page, text: "BAIRRO/DISTRITO", x: PDF.width * 0.47, y: PDF.mtBlock + 31, maxWidth: PDF.width * 0.4 }); addTXT({ page, size: 9, text: xml.NFe.infNFe.dest.enderDest?.xBairro || "", x: PDF.width * 0.47, y: PDF.mtBlock + 40, maxWidth: PDF.width * 0.21, align: "center", fontStyle: "negrito" }); addTXT({ page, text: "CEP", x: PDF.width * 0.67, y: PDF.mtBlock + 31, maxWidth: PDF.width * 0.4 }); addTXT({ page, size: 9, text: (xml.NFe.infNFe?.dest?.enderDest?.CEP || "").replace(/^(\d{5})(\d{3})$/, "$1-$2"), x: PDF.width * 0.67, y: PDF.mtBlock + 40, maxWidth: PDF.width * 0.17, align: "center", fontStyle: "negrito" }); addTXT({ page, text: "DATA DA SA\xCDDA/ENTRDA", x: PDF.width * 0.83, y: PDF.mtBlock + 31, maxWidth: PDF.width * 0.4 }); addTXT({ page, size: 9, text: new Date(xml.NFe.infNFe.ide.dhEmi).toLocaleDateString("pt-BR"), x: PDF.width * 0.83, y: PDF.mtBlock + 40, maxWidth: PDF.width * 0.17, align: "center", fontStyle: "negrito" }); addTXT({ page, text: "MUNICIPIO", x: 2, y: PDF.mtBlock + 50, maxWidth: PDF.width * 0.4 }); addTXT({ page, size: 9, text: xml.NFe.infNFe.dest?.enderDest?.xMun || "", x: 3, y: PDF.mtBlock + 60, maxWidth: PDF.width * 0.42, align: "left", fontStyle: "negrito" }); addTXT({ page, text: "UF", x: PDF.width * 0.47, y: PDF.mtBlock + 50, maxWidth: PDF.width * 0.4 }); addTXT({ page, size: 9, text: xml.NFe.infNFe.dest.enderDest?.UF || "", x: PDF.width * 0.473, y: PDF.mtBlock + 60, maxWidth: PDF.width * 0.21, align: "left", fontStyle: "negrito" }); addTXT({ page, text: "FONE/FAX", x: PDF.width * 0.505, y: PDF.mtBlock + 50, maxWidth: PDF.width * 0.4 }); addTXT({ page, size: 9, text: xml.NFe.infNFe.dest.enderDest?.fone || "", x: PDF.width * 0.505, y: PDF.mtBlock + 60, maxWidth: PDF.width * 0.17, align: "center", fontStyle: "negrito" }); addTXT({ page, text: "INSCRI\xC7\xC3O ESTADUAL", x: PDF.width * 0.67, y: PDF.mtBlock + 50, maxWidth: PDF.width * 0.4 }); addTXT({ page, size: 9, text: xml.NFe.infNFe.dest.IE || "", x: PDF.width * 0.67, y: PDF.mtBlock + 60, maxWidth: PDF.width * 0.17, align: "center", fontStyle: "negrito" }); addTXT({ page, text: "HORA DA SA\xCDDA/ENTRDA", x: PDF.width * 0.83, y: PDF.mtBlock + 50, maxWidth: PDF.width * 0.4 }); addTXT({ page, size: 9, text: new Date(xml.NFe.infNFe.ide.dhEmi).toLocaleTimeString("pt-BR"), x: PDF.width * 0.83, y: PDF.mtBlock + 60, maxWidth: PDF.width * 0.17, align: "center", fontStyle: "negrito" }); PDF.mtBlock += 72; } async function bloco3(page = PDF.pages[PDF.pages.length - 1]) { let IndexX = 0, contL = 0; if (xml.NFe.infNFe?.cobr?.dup != void 0) { addTXT({ page, text: "FATURA / DUPLICATA", x: 3, y: PDF.mtBlock, maxWidth: PDF.width * 0.25, fontStyle: "negrito" }); if (Array.isArray(xml.NFe.infNFe.cobr.dup) && xml.NFe.infNFe.cobr.dup.length > 14) { addRet(page, PDF.width * IndexX, PDF.mtBlock + 8 + contL * 22, PDF.width, 20); addTXT({ page, text: `Existem mais de 14 duplicatas registradas, portanto n\xE3o ser\xE3o exibidas, confira diretamente pelo XML.`, x: 3, y: PDF.mtBlock + 13, maxWidth: PDF.width, align: "center" }); IndexX += 0.25; } else { const cobrDup = Array.isArray(xml.NFe.infNFe.cobr.dup) ? xml.NFe.infNFe.cobr.dup : [xml.NFe.infNFe.cobr.dup]; console.log(cobrDup); for (const [index, dup] of cobrDup.entries()) { addRet(page, PDF.width * IndexX, PDF.mtBlock + 8 + contL * 22, PDF.width * 0.1428, 20); addTXT({ page, text: "Num.", x: PDF.width * IndexX + 1, y: PDF.mtBlock + 8 + contL * 22, maxWidth: PDF.width * 0.1458 }); addTXT({ page, text: dup.nDup, x: PDF.width * IndexX + 1, y: PDF.mtBlock + 8 + contL * 22, maxWidth: PDF.width * 0.1458, align: "right", fontStyle: "negrito" }); addTXT({ page, text: "Venc.", x: PDF.width * IndexX + 1, y: PDF.mtBlock + 14 + contL * 22, maxWidth: PDF.width * 0.1458 }); addTXT({ page, text: new Date(dup.dVenc).toLocaleDateString("pt-BR"), x: PDF.width * IndexX + 1, y: PDF.mtBlock + 14 + contL * 22, maxWidth: PDF.width * 0.1458, align: "right", fontStyle: "negrito" }); addTXT({ page, text: "Valor", x: PDF.width * IndexX + 1, y: PDF.mtBlock + 20 + contL * 22, maxWidth: PDF.width * 0.1458 }); addTXT({ page, text: dup.vDup, x: PDF.width * IndexX + 1, y: PDF.mtBlock + 20 + contL * 22, maxWidth: PDF.width * 0.1458, align: "right", fontStyle: "negrito" }); if (index + 1 < cobrDup.length) { if (IndexX + 0.1458 >= 1) { IndexX = 0; contL++; } else { IndexX += 0.146; } } } } } else { addTXT({ page, text: "PAGAMENTOS", x: 3, y: PDF.mtBlock, maxWidth: PDF.width * 0.25, fontStyle: "negrito" }); const pagamentos = Array.isArray(xml.NFe.infNFe.pag.detPag) ? xml.NFe.infNFe.pag.detPag : [xml.NFe.infNFe.pag.detPag]; const formaPagto = { "01": "Dinheiro", "02": "Cheque", "03": "Cart\xE3o de Cr\xE9dito", "04": "Cart\xE3o de D\xE9bito", "05": "Cr\xE9dito Loja", "10": "Vale Alimenta\xE7\xE3o", "11": "Vale Refei\xE7\xE3o", "12": "Vale Presente", "13": "Vale Combust\xEDvel", "15": "Boleto Banc\xE1rio", "16": "Dep\xF3sito Banc\xE1rio", "17": "PIX", "18": "Transfer\xEAncia", "19": "Fidelidade", "90": "Sem pagamento", "99": "Outros" }; for (const pag of pagamentos) { const forma = formaPagto[pag.tPag] || `C\xF3digo ${pag.tPag}`; const valor = parseFloat(pag.vPag).toLocaleString("pt-BR", { style: "currency", currency: "BRL" }); addRet(page, PDF.width * IndexX, PDF.mtBlock + 8 + contL * 22, PDF.width * 0.25, 20); addTXT({ page, text: "FORMA", x: PDF.width * IndexX + 3, y: PDF.mtBlock + 9 + contL * 22, maxWidth: PDF.width * 0.25 }); addTXT({ page, text: forma, x: PDF.width * IndexX + 3, y: PDF.mtBlock + 19 + contL * 22, maxWidth: PDF.width * 0.25 }); addTXT({ page, text: forma, x: PDF.width * IndexX + 3, y: PDF.mtBlock + 9 + contL * 22, maxWidth: PDF.width * 0.245, align: "right", fontStyle: "negrito" }); addTXT({ page, text: valor, x: PDF.width * IndexX + 3, y: PDF.mtBlock + 19 + contL * 22, maxWidth: PDF.width * 0.245, align: "right", fontStyle: "negrito" }); if (IndexX + 0.25 >= 1) { IndexX = 0.25; contL++; } else { IndexX += 0.25; } } } PDF.mtBlock += (contL + 1) * 22 + 7; } async function bloco4(page = PDF.pages[PDF.pages.length - 1]) { const ICMS = { vBC: "Base Calc. ICMS", vICMS: "Valor ICMS", vICMSDeson: "ICMS Desonerado", vBCST: "Base Calc. ICMS ST", vST: "ICMS Subst. Trib.", vFCPST: "Valor FCP ST", vFCPSTRet: "FCP Retido ST", vProd: "Valor Produtos", vFrete: "Valor Frete", vSeg: "Valor Seguro", vDesc: "Valor Desconto", vII: "Valor Imp. Import.", vIPI: "Valor IPI", vIPIDevol: "IPI Devolvido", vPIS: "Valor PIS", vCOFINS: "Valor COFINS", vOutro: "Outras Desp. Acess.", vNF: "Valor Total NF-e" }; addTXT({ page, text: "TOTAIS", x: 3, y: PDF.mtBlock, maxWidth: PDF.width * 0.25, fontStyle: "negrito" }); let nextY = PDF.mtBlock + 8, nextX = 0, limitY = PDF.width - 8; for (const key of Object.keys(ICMS)) { const valor = xml.NFe.infNFe.total.ICMSTot[key]; const texto = valor ? parseFloat(valor).toLocaleString("pt-BR", { style: "currency", currency: "BRL" }) : "0,00"; await addRet(page, limitY * 0.111 * nextX, nextY, limitY * 0.111, 20); addTXT({ page, text: ICMS[key], x: 2 + limitY * 0.111 * nextX, y: nextY + 1, maxWidth: limitY * 0.111 }); addTXT({ page, size: 10, text: texto.replace("R$", ""), x: limitY * 0.111 * nextX, y: nextY + 9, maxWidth: limitY * 0.111, align: "right", fontStyle: "negrito" }); nextX++; if (nextX >= 9) { nextX = 0; nextY += 20; } } PDF.mtBlock += nextY - PDF.mtBlock + 3; } async function bloco5(page = PDF.pages[PDF.pages.length - 1]) { const transp = xml.NFe.infNFe.transp || {}; const vol = Array.isArray(transp.vol) ? transp.vol[0] : transp.vol || {}; const modFreteMap = { "0": "0-Emitente", "1": "1-Destinat\xE1rio", "2": "2-Terceiros", "3": "3-Pr\xF3prio por conta do remetente", "4": "4-Pr\xF3prio por conta do destinat\xE1rio", "9": "9-Sem Transporte" }; addTXT({ page, text: "TRANSPORTADOR / VOLUMES TRANSPORTADOS", x: 3, y: PDF.mtBlock, maxWidth: PDF.width, fontStyle: "negrito" }); addRet(page, 0, PDF.mtBlock + 8, PDF.width * 0.29, 20); addRet(page, PDF.width * 0.29, PDF.mtBlock + 8, PDF.width * 0.15, 20); addRet(page, PDF.width * 0.44, PDF.mtBlock + 8, PDF.width * 0.14, 20); addRet(page, PDF.width * 0.58, PDF.mtBlock + 8, PDF.width * 0.15, 20); addRet(page, PDF.width * 0.73, PDF.mtBlock + 8, PDF.width * 0.04, 20); addRet(page, PDF.width * 0.77, PDF.mtBlock + 8, PDF.width, 20); addRet(page, PDF.width * 0.77, PDF.mtBlock + 28, PDF.width, 20); addRet(page, PDF.width * 0.8, PDF.mtBlock + 48, PDF.width, 20); addRet(page, PDF.width * 0.6, PDF.mtBlock + 48, PDF.width, 20); addRet(page, PDF.width * 0.44, PDF.mtBlock + 48, PDF.width, 20); addRet(page, PDF.width * 0.27, PDF.mtBlock + 48, PDF.width, 20); addRet(page, PDF.width * 0.1, PDF.mtBlock + 48, PDF.width, 20); addRet(page, 0, PDF.mtBlock + 48, PDF.width, 20); addRet(page, 0, PDF.mtBlock + 28, PDF.width * 0.44, 20); addRet(page, 0, PDF.mtBlock + 28, PDF.width * 0.73, 20); addTXT({ page, text: "NOME / RAZ\xC3O SOCIAL", x: 3, y: PDF.mtBlock + 8, maxWidth: PDF.width * 0.29 }); addTXT({ page, text: transp.transporta?.xNome || "", x: 3, y: PDF.mtBlock + 18, maxWidth: PDF.width * 0.29, fontStyle: "negrito" }); addTXT({ page, text: "FRETE", x: PDF.width * 0.293, y: PDF.mtBlock + 8, maxWidth: PDF.width * 0.15 }); addTXT({ page, text: modFreteMap[transp.modFrete] || `C\xF3digo ${transp.modFrete || ""}`, x: PDF.width * 0.293, y: PDF.mtBlock + 18, maxWidth: PDF.width * 0.15, fontStyle: "negrito" }); addTXT({ page, text: "C\xD3DIGO ANTT", x: PDF.width * 0.443, y: PDF.mtBlock + 8, maxWidth: PDF.width * 0.15 }); addTXT({ page, text: transp.veicTransp?.RNTC || "", x: PDF.width * 0.443, y: PDF.mtBlock + 18, maxWidth: PDF.width * 0.15, fontStyle: "negrito" }); addTXT({ page, text: "PLACA DO VE\xCDCULO", x: PDF.width * 0.583, y: PDF.mtBlock + 8, maxWidth: PDF.width * 0.15 }); addTXT({ page, text: transp.veicTransp?.placa || "", x: PDF.width * 0.583, y: PDF.mtBlock + 18, maxWidth: PDF.width * 0.15, fontStyle: "negrito" }); addTXT({ page, text: "UF", x: PDF.width * 0.733, y: PDF.mtBlock + 8, maxWidth: PDF.width * 0.15 }); addTXT({ page, text: transp.veicTransp?.UF || "", x: PDF.width * 0.733, y: PDF.mtBlock + 18, maxWidth: PDF.width * 0.15, fontStyle: "negrito" }); addTXT({ page, text: "CNPJ/CPF", x: PDF.width * 0.773, y: PDF.mtBlock + 8, maxWidth: PDF.width * 0.15 }); addTXT({ page, text: embCNPJCPF(transp.transporta?.CNPJ || transp.transporta?.CPF || ""), x: PDF.width * 0.773, y: PDF.mtBlock + 18, maxWidth: PDF.width * 0.15, fontStyle: "negrito" }); addTXT({ page, text: "ENDERE\xC7O", x: 3, y: PDF.mtBlock + 29, maxWidth: PDF.width * 0.29 }); addTXT({ page, text: transp.transporta?.xEnder || "", x: 3, y: PDF.mtBlock + 39, maxWidth: PDF.width * 0.29, fontStyle: "negrito" }); addTXT({ page, text: "MUNIC\xCDPIO", x: PDF.width * 0.443, y: PDF.mtBlock + 29, maxWidth: PDF.width * 0.29 }); addTXT({ page, text: transp.transporta?.xMun || "", x: PDF.width * 0.443, y: PDF.mtBlock + 39, maxWidth: PDF.width * 0.29, fontStyle: "negrito" }); addTXT({ page, text: "UF", x: PDF.width * 0.733, y: PDF.mtBlock + 29, maxWidth: PDF.width * 0.29 }); addTXT({ page, text: transp.transporta?.UF || "", x: PDF.width * 0.733, y: PDF.mtBlock + 39, maxWidth: PDF.width * 0.29, fontStyle: "negrito" }); addTXT({ page, text: "INSCRI\xC7\xC3O ESTADUAL", x: PDF.width * 0.773, y: PDF.mtBlock + 29, maxWidth: PDF.width * 0.29 }); addTXT({ page, text: transp.transporta?.IE || "", x: PDF.width * 0.773, y: PDF.mtBlock + 39, maxWidth: PDF.width * 0.29, fontStyle: "negrito" }); addTXT({ page, text: "QUANTIDADE", x: 3, y: PDF.mtBlock + 49, maxWidth: PDF.width * 0.29 }); addTXT({ page, text: vol.qVol || "", x: 3, y: PDF.mtBlock + 59, maxWidth: PDF.width * 0.29, fontStyle: "negrito" }); addTXT({ page, text: "ESP\xC9CIE", x: PDF.width * 0.102, y: PDF.mtBlock + 49, maxWidth: PDF.width * 0.29 }); addTXT({ page, text: vol.esp || "", x: PDF.width * 0.102, y: PDF.mtBlock + 59, maxWidth: PDF.width * 0.29, fontStyle: "negrito" }); addTXT({ page, text: "MARCA", x: PDF.width * 0.273, y: PDF.mtBlock + 49, maxWidth: PDF.width * 0.29 }); addTXT({ page, text: vol.marca || "", x: PDF.width * 0.273, y: PDF.mtBlock + 59, maxWidth: PDF.width * 0.29, fontStyle: "negrito" }); addTXT({ page, text: "NUMERA\xC7\xC3O", x: PDF.width * 0.443, y: PDF.mtBlock + 49, maxWidth: PDF.width * 0.29 }); addTXT({ page, text: vol.nVol || "", x: PDF.width * 0.443, y: PDF.mtBlock + 59, maxWidth: PDF.width * 0.29, fontStyle: "negrito" }); addTXT({ page, text: "PESO BRUTO", x: PDF.width * 0.603, y: PDF.mtBlock + 49, maxWidth: PDF.width * 0.29 }); addTXT({ page, text: vol.pesoB || "", x: PDF.width * 0.603, y: PDF.mtBlock + 59, maxWidth: PDF.width * 0.29, fontStyle: "negrito" }); addTXT({ page, text: "PESO L\xCDQUIDO", x: PDF.width * 0.803, y: PDF.mtBlock + 49, maxWidth: PDF.width * 0.29 }); addTXT({ page, text: vol.pesoL || "", x: PDF.width * 0.803, y: PDF.mtBlock + 59, maxWidth: PDF.width * 0.29, fontStyle: "negrito" }); PDF.mtBlock += 70; } async function bloco6(page = PDF.pages[PDF.pages.length - 1]) { xml.NFe.infNFe.det = Array.isArray(xml.NFe.infNFe.det) ? xml.NFe.infNFe.det : [xml.NFe.infNFe.det]; addTXT({ page, text: "DADOS DOS PRODUTOS / SERVI\xC7OS", x: 3, y: PDF.mtBlock, maxWidth: PDF.width, fontStyle: "negrito" }); let blockH; if (PDF.pages.length == 1) { blockH = PDF.height - PDF.mtBlock - 72; } else { blockH = PDF.height - PDF.mtBlock - 18; } addRet(page, 0, PDF.mtBlock + 8, PDF.width, blockH); addRet(page, 0, PDF.mtBlock + 8, PDF.width, 15); const colunas = [0.1, 0.34, 0.403, 0.453, 0.488, 0.525, 0.6, 0.655, 0.712, 0.76, 0.815, 0.875, 0.92, 0.957]; for (const x of colunas) addLTV(page, PDF.width * x, PDF.mtBlock + 8, blockH); addTXT({ page, text: "C\xD3DIGO PRODUTO", x: PDF.width * 3e-3, y: PDF.mtBlock + 8, maxWidth: PDF.width * 0.09, align: "center" }); addTXT({ page, text: "DESCRI\xC7\xC3O DO PRODUTO / SERVI\xC7O", x: PDF.width * 0.1, y: PDF.mtBlock + 12, maxWidth: PDF.width * 0.24, align: "center" }); addTXT({ page, text: "NCM/SH", x: PDF.width * 0.34, y: PDF.mtBlock + 12, maxWidth: PDF.width * 0.06, align: "center" }); addTXT({ page, text: "O/CSOSN", x: PDF.width * 0.4, y: PDF.mtBlock + 12, maxWidth: PDF.width * 0.06, align: "center" }); addTXT({ page, text: "CFOP", x: PDF.width * 0.46, y: PDF.mtBlock + 12, maxWidth: PDF.width * 0.025, align: "center" }); addTXT({ page, text: "UN", x: PDF.width * 0.495, y: PDF.mtBlock + 12, maxWidth: PDF.width * 0.025, align: "center" }); addTXT({ page, text: "QUANT.", x: PDF.width * 0.525, y: PDF.mtBlock + 12, maxWidth: PDF.width * 0.07, align: "center" }); addTXT({ page, text: "VALOR UNIT", x: PDF.width * 0.592, y: PDF.mtBlock + 8.5, maxWidth: PDF.width * 0.07, align: "center" }); addTXT({ page, text: "VALOR TOTAL", x: PDF.width * 0.65, y: PDF.mtBlock + 8.5, maxWidth: PDF.width * 0.07, align: "center" }); addTXT({ page, text: "VALOR DESC", x: PDF.width * 0.7, y: PDF.mtBlock + 8.5, maxWidth: PDF.width * 0.07, align: "center" }); addTXT({ page, text: "B.C\xC1LC ICMS", x: PDF.width * 0.75, y: PDF.mtBlock + 8.5, maxWidth: PDF.width * 0.07, align: "center" }); addTXT({ page, text: "VALOR ICMS", x: PDF.width * 0.81, y: PDF.mtBlock + 8.5, maxWidth: PDF.width * 0.07, align: "center" }); addTXT({ page, text: "VALOR IPI", x: PDF.width * 0.862, y: PDF.mtBlock + 8.5, maxWidth: PDF.width * 0.07, align: "center" }); addTXT({ page, text: "AL\xCDQ. ICMS", x: PDF.width * 0.924, y: PDF.mtBlock + 8.5, maxWidth: PDF.width * 0.03, align: "center" }); addTXT({ page, text: "AL\xCDQ. IPI", x: PDF.width * 0.961, y: PDF.mtBlock + 8.5, maxWidth: PDF.width * 0.03, align: "center" }); let line = 23, lLimite = blockH / 7.1, lIndex = 0; for (const [iDet, det] of xml.NFe.infNFe.det.entries()) { let prod = det.prod; prod.xProd = prod.xProd.split("\n").join(" "); lIndex += await addTXT({ page, text: prod.xProd, x: 0, y: 0, maxWidth: PDF.width * 0.237, align: "center", cacl: true }); if (lIndex >= lLimite) { xml.NFe.infNFe.det.splice(0, iDet); PDF.mtBlock += blockH + 10; return false; } const imposto = det.imposto || {}; const ICMS = imposto.ICMS?.ICMSSN102 || imposto.ICMS?.ICMS00 || {}; const IPI = imposto.IPI?.IPITrib || {}; const fmt = (v) => parseFloat(v || "0.00").toLocaleString("pt-BR", { minimumFractionDigits: 2 }); const xProdH = await addTXT({ page, text: prod.xProd, x: PDF.width * 0.096, y: PDF.mtBlock + line, maxWidth: PDF.width * 0.237, align: "left" }); const y = PDF.mtBlock + line + (xProdH - 1) * 2.7; addTXT({ page, text: prod.cProd, x: 0, y, maxWidth: PDF.width * 0.1, align: "center" }); addTXT({ page, text: prod.NCM, x: PDF.width * 0.34, y, maxWidth: PDF.width * 0.061, align: "center" }); addTXT({ page, text: ICMS.CSOSN || ICMS.CST || "", x: PDF.width * 0.398, y, maxWidth: PDF.width * 0.061, align: "center" }); addTXT({ page, text: prod.CFOP, x: PDF.width * 0.44, y, maxWidth: PDF.width * 0.061, align: "center" }); addTXT({ page, text: prod.uCom, x: PDF.width * 0.476, y, maxWidth: PDF.width * 0.061, align: "center" }); addTXT({ page, text: fmt(prod.qCom), x: PDF.width * 0.533, y, maxWidth: PDF.width * 0.061, align: "center" }); addTXT({ page, text: fmt(prod.vUnCom), x: PDF.width * 0.597, y, maxWidth: PDF.width * 0.061, align: "center" }); addTXT({ page, text: fmt(prod.vProd), x: PDF.width * 0.655, y, maxWidth: PDF.width * 0.061, align: "center" }); addTXT({ page, text: fmt(prod.vDesc), x: PDF.width * 0.705, y, maxWidth: PDF.width * 0.061, align: "center" }); addTXT({ page, text: fmt(prod.vBC), x: PDF.width * 0.756, y, maxWidth: PDF.width * 0.061, align: "center" }); addTXT({ page, text: fmt(prod.vICMS), x: PDF.width * 0.816, y, maxWidth: PDF.width * 0.061, align: "center" }); addTXT({ page, text: fmt(prod.vIPI), x: PDF.width * 0.868, y, maxWidth: PDF.width * 0.061, align: "center" }); addTXT({ page, text: fmt(ICMS.pICMS), x: PDF.width * 0.908, y, maxWidth: PDF.width * 0.061, align: "center" }); addTXT({ page, text: fmt(IPI.pIPI), x: PDF.width * 0.954, y, maxWidth: PDF.width * 0.061, align: "center" }); line += xProdH * 6.9; } PDF.mtBlock += blockH + 10; return true; } async function bloco7(page = PDF.pages[PDF.pages.length - 1]) { addTXT({ page, text: "DADOS ADICIONAIS", x: 3, y: PDF.mtBlock, maxWidth: PDF.width, fontStyle: "negrito" }); addRet(page, 0, PDF.mtBlock + 8, PDF.width, 40); addRet(page, 0, PDF.mtBlock + 8, PDF.width * 0.65, 40); addTXT({ page, text: "INFORMA\xC7\xD5ES COMPLEMENTARES", x: 3, y: PDF.mtBlock + 8, maxWidth: PDF.width * 0.5, align: "left", fontStyle: "negrito" }); addTXT({ page, text: "RESERVADO AO FISCO", x: PDF.width * 0.652, y: PDF.mtBlock + 8, maxWidth: PDF.width * 0.5, align: "left", fontStyle: "negrito" }); if (await addTXT({ page, text: xml.NFe.infNFe.infAdic?.infCpl || "", x: 3, y: PDF.mtBlock + 14, maxWidth: PDF.width * 0.65, align: "left", cacl: true }) >= 5) { addTXT({ page, text: xml.NFe.infNFe.infAdic?.infCpl.slice(0, 600) || "", x: 3, y: PDF.mtBlock + 14, maxWidth: PDF.width * 0.65, align: "left" }); } else { addTXT({ page, text: xml.NFe.infNFe.infAdic?.infCpl || "", x: 3, y: PDF.mtBlock + 14, maxWidth: PDF.width * 0.65, align: "left" }); } ; PDF.mtBlock += 40; } async function bloco8(page = PDF.pages[PDF.pages.length - 1]) { const agora = /* @__PURE__ */ new Date(); const dataFormatada = agora.toLocaleDateString("pt-BR"); const horaFormatada = agora.toLocaleTimeString("pt-BR"); const textoEsquerda = `Impresso em ${dataFormatada} \xE0s ${horaFormatada}. ${xml.NFe.infNFe?.infRespTec?.xContato || ""}`; addTXT({ page, text: textoEsquerda, x: 3, y: PDF.mtBlock + 8, maxWidth: PDF.width, align: "left" }); addTXT({ page, text: "Powered by @node-sped-pdf", x: 3, y: PDF.mtBlock + 8, maxWidth: PDF.width * 0.985, align: "right", fontStyle: "italic" }); } async function addIMG({ page, img, x, y, h, w }) { if (typeof img != void 0) { if (img.includes("http") || img.includes("wwww")) img = await fetch(img || "").then((response) => response.blob()).then((blob) => blob2base64(blob)); const bytes = Uint8Array.from(atob(img.split(",")[1]), (c) => c.charCodeAt(0)); const isPng = img?.startsWith("data:image/png"); const image = isPng ? await PDF.doc.embedPng(bytes) : await PDF.doc.embedJpg(bytes); await page.drawImage(image, { x, y: PDF.height - y - h, // Corrige porque pdf-lib desenha do canto inferior da imagem width: w, height: h }); } } async function blob2base64(blobOrBuffer) { const isBrowser2 = typeof window !== "undefined" && typeof window.FileReader !== "undefined"; if (isBrowser2) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(blobOrBuffer); reader.onloadend = () => resolve(reader.result); reader.onerror = reject; }); } else { try { let buffer; if (blobOrBuffer instanceof Blob) { const arrayBuffer = await blobOrBuffer.arrayBuffer(); buffer = Buffer.from(arrayBuffer); } else if (Buffer.isBuffer(blobOrBuffer)) { buffer = blobOrBuffer; } else { buffer = Buffer.from(blobOrBuffer); } return buffer.toString("base64"); } catch (err) { throw new Error(`Falha ao converter: ${err}`); } } } async function blocoDEMO(page = PDF.pages[PDF.pages.length - 1]) { imgDemo = await fetch(imgDemo || "").then((response) => response.blob()).then((blob) => blob2base64(blob)); const base64Data = imgDemo?.split(",")[1]; const bytes = Uint8Array.from(atob(base64Data), (c) => c.charCodeAt(0)); const isPng = imgDemo?.startsWith("data:image/png"); const image = isPng ? await PDF.doc.embedPng(bytes) : await PDF.doc.embedJpg(bytes); page.drawImage(image, { x: 0, y: 0, // Corrige porque pdf-lib desenha do canto inferior da imagem width: PDF.width, height: PDF.height }); } return new Promise(async (resolve, reject) => { await gerarBlocos(); resolve(await PDF.doc.save()); }); }; // src/libs/danfce.ts import { PDFDocument as PDFDocument2, StandardFonts as StandardFonts2, rgb as rgb2 } from "pdf-lib"; import { XMLParser as XMLParser2 } from "fast-xml-parser"; import qrcode from "qrcode"; var DANFCe = async (data = {}) => { const parser = new XMLParser2({ ignoreAttributes: false, attributeNamePrefix: "@", parseTagValue: false // Evita conversão automática de valores }); function normalizarXML(xml2) { if (xml2 == "Bad Request") throw xml2; const clear = [ "S:Envelope", "S:Body", "soapenv:Envelope", "soapenv:Body", "soap:Envelope", "soap:Body", "nfeResultMsg", "nfeDistDFeInteresseResponse", "protNFe", "enviNFe", "nfeProc" ]; let index = 0; while (index < clear.length) { if (typeof xml2[clear[index]] !== "undefined") { xml2 = { ...xml2, ...xml2[clear[index]] }; delete xml2[clear[index]]; index = 0; } else { index++; } } return xml2; } var PDF = { doc: await PDFDocument2.create(), pages: [], width: 0, height: 0, mtBlock: 0, barCode: null }, isBrowser = typeof window !== "undefined", xml = normalizarXML(parser.parse(data.xml || "")), xmlRes = data.xmlRes, logo = data.logo, imgDemo = data.imgDemo, extras = data.extras || []; if (!xml?.NFe?.infNFe) { throw new Error("XML inv\xE1lido para DANFCe: n\xE3o foi encontrada a tag NFe/infNFe."); } PDF.pages.push(PDF.doc.addPage([ 230, await bloco0(null) + await bloco1(null) + await bloco2(null) + await bloco3(null) + await bloco4(null) ])); PDF.width = PDF.pages[0].getWidth(); PDF.height = PDF.pages[0].getHeight(); async function addRet(page, x, y, w, h) { page.drawRectangle({ x: x + 4, y: PDF.height - h - (y + 4), width: x + w + 8 >= PDF.width ? PDF.width - x - 8 : w, height: h, borderColor: rgb2(0, 0, 0), borderWidth: 1 }); } async function addLTH(page, x, y, h) { const startX = Math.max(x, 4); const endX = Math.min(x + h, PDF.width - 4); const fixedY = PDF.height - y - 4; page.drawLine({ start: { x: startX, y: fixedY }, end: { x: endX, y: fixedY }, color: rgb2(0, 0, 0), thickness: 1, dashArray: [5, 3] }); } async function addLTV(page, x, y, w) { const fixedX = Math.max(4, Math.min(x, PDF.width - 4)); const startY = Math.max(PDF.height - y - 4, 4); const endY = Math.max(PDF.height - (y + w) - 4, 4); page.drawLine({ start: { x: fixedX, y: startY }, end: { x: fixedX, y: endY }, color: rgb2(0, 0, 0), thickness: 1, dashArray: [5, 3] }); } async function addTXT({ page, text, x, y, maxWidth, fontStyle = "normal", size = 7, lineHeight, align = "left", cacl = false }) { let font; switch (fontStyle) { case "negrito": font = await PDF.doc.embedFont(StandardFonts2.TimesRomanBold); break; case "italic": font = await PDF.doc.embedFont(StandardFonts2.TimesRomanItalic); break; default: font = await PDF.doc.embedFont(StandardFonts2.TimesRoman); } if (maxWidth + x > PDF.width) maxWidth = PDF.width - x - 2; const effectiveLineHeight = lineHeight ?? size * 0.9; const lines = wrapText(text, maxWidth, font, size); if (cacl) return lines.length; lines.forEach((line, index) => { const textWidth = font.widthOfTextAtSize(line, size); let drawX = x + 4; if (align === "center") { drawX = x + (maxWidth - textWidth) / 2; } else if (align === "right") { drawX = x + maxWidth - textWidth; } page.drawText(line, { x: drawX, y: PDF.height - effectiveLineHeight - (y + 4) - index * effectiveLineHeight, size, font }); }); return lines.length; } function wrapText(text, maxWidth, font, fontSize) { const words = text.split(" "); const lines = []; let line = ""; for (let i = 0; i < words.length; i++) { const word = words[i]; const testLine = line + word + " "; const testWidth = font.widthOfTextAtSize(testLine, fontSize); if (testWidth > maxWidth && line !== "") { lines.push(line.trim()); line = word + " "; } else { line = testLine; } } if (line.trim() !== "") { lines.push(line.trim()); } return lines; } async function gerarBlocos() { await bloco0(); await bloco1(); await bloco2(); await bloco3(); await bloco4(); } async function bloco0(page = PDF.pages[PDF.pages.length - 1]) { if (!page) return 74; let me = 0; if (typeof logo !== "undefined") { await addIMG({ page, img: logo, x: 3, y: PDF.mtBlock + 3, h: 60, w: 60 }); me += 62; } const emit = xml?.NFe?.infNFe?.emit || {}; const enderEmit = emit.enderEmit || {}; let line = await addTXT({ page, text: `${emit.xNome || "Emitente desconhecido"}`, x: 1 + me, y: PDF.mtBlock + 5, maxWidth: PDF.width, align: "center", fontStyle: "negrito" }); PDF.mtBlock = (line - 1) * 2.7 + 10; await addTXT({ page, text: `CNPJ: ${emit.CNPJ || "N/D"} - I.E.: ${emit.IE || "N/D"}`, x: 1 + me, y: PDF.mtBlock + 5, maxWidth: PDF.width, align: "center" }); await addTXT({ page, text: `${enderEmit.xLgr || "Logradouro desconhecido"}, N\xB0${enderEmit.nro || "S/N"}`, x: 0 + me, y: PDF.mtBlock + 13, maxWidth: PDF.width, align: "center" }); await addTXT({ page, text: `${enderEmit.xBairro || "Bairro N/D"} - ${enderEmit.CEP || "CEP N/D"}`, x: 0 + me, y: PDF.mtBlock + 20, maxWidth: PDF.width, align: "center" }); await addTXT({ page, text: `${enderEmit.xMun || "Cidade N/D"} - ${enderEmit.UF || "UF"} Fone: ${enderEmit.fone || "N/D"}`, x: 0 + me, y: PDF.mtBlock + 27, maxWidth: PDF.width, align: "center" }); addLTH(page, 0, PDF.mtBlock + 55, PDF.width); await addTXT({ page, text: `DOCUMENTO AUXILIAR DA NOTA FISCAL DE CONSUMIDOR ELETR\xD4NICA`, x: 0, y: PDF.mtBlock + 57, maxWidth: PDF.width, align: "center", fontStyle: "negrito" }); addLTH(page, 0, PDF.mtBlock + 72, PDF.width); PDF.mtBlock += 74; return 1; } async function bloco1(page = PDF.pages[PDF.pages.length - 1]) { const produtos = Array.isArray(xml?.NFe?.infNFe?.det) ? xml.NFe.infNFe.det : xml?.NFe?.infNFe?.det ? [xml.NFe.infNFe.det] : []; if (page == null) { let lIndex = 0; for (const det of produtos) { const prod = det?.prod || {}; const text = prod.xProd || ""; const wrappedLines = wrapText( text, 230 * 0.42, await PDF.doc.embedFont(StandardFonts2.TimesRoman), 7 ); lIndex += wrappedLines.length; } return 24 + lIndex * 10; } else { let line = 7, lIndex = 0; addTXT({ page, text: `CODIGO | DESCRI\xC7\xC3O`, x: PDF.width * 0, y: PDF.mtBlock, maxWidth: PDF.width * 0.5, align: "left" }); addTXT({ page, text: `QTDE | UN | VL. UNIT | VL. TOTAL`, x: 0, y: PDF.mtBlock, maxWidth: PDF.width * 0.98, align: "right" }); for (const det of produtos) { const prod = det?.prod || {}; const fmt = (v) => parseFloat(v || "0.00").toLocaleString("pt-BR", { minimumFractionDigits: 2 }); const xProd = `${prod.cProd} | ${prod.xProd}`; const xProdH = await addTXT({ page, text: xProd, x: 0, y: PDF.mtBlock + line, maxWidth: PDF.width * 0.5, align: "left" }); const y = PDF.mtBlock + line + (xProdH - 1) * 2.7; addTXT({ page, text: `${fmt(prod.qCom)} | ${prod.uCom || "UN"} | ${fmt(prod.vUnCom)} | ${fmt(prod.vProd)}`, x: 0, y, maxWidth: PDF.width * 0.98, align: "right" }); line += xProdH * 6.9; lIndex += xProdH; } addLTH(page, 0, 7 + PDF.mtBlock + lIndex * 10, PDF.width); PDF.mtBlock += 8 + lIndex * 10; return 1; } } async function bloco2(page = PDF.pages[PDF.pages.length - 1]) { if (!page) { const pag2 = xml?.NFe?.infNFe?.pag || {}; const detPag2 = Array.isArray(pag2.detPag) ? pag2.detPag : [pag2.detPag]; return 40 + detPag2.length * 7; } ; const total = xml?.NFe?.infNFe?.total?.ICMSTot || {}; const pag = xml?.NFe?.infNFe?.pag || {}; const detPag = Array.isArray(pag.detPag) ? pag.detPag : [pag.detPag]; const vTroco = parseFloat(pag.vTroco || "0.00"); const qtdItens = Array.isArray(xml?.NFe?.infNFe?.det) ? xml.NFe.infNFe.det.length : xml?.NFe?.infNFe?.det ? 1 : 0; const fmt = (v) => parseFloat(v || "0.00").toLocaleString("pt-BR", { minimumFractionDigits: 2 }); await addTXT({ page, text: `Qtde. Total de Itens`, x: 0, y: PDF.mtBlock, maxWidth: PDF.width, align: "left" }); await addTXT({ page, text: `${qtdItens}`, x: 0, y: PDF.mtB