Untitled

 avatar
unknown
javascript
2 months ago
6.0 kB
5
Indexable
import { jsPDF } from "jspdf"
import html2canvas from "html2canvas"

interface PrintHtmlProps {
  htmlString: string
}

export const printHtml = ({ htmlString }: PrintHtmlProps): Promise<void> => {
  return new Promise((resolve, reject) => {
    try {
      const iframe = document.createElement("iframe")
      iframe.style.position = "fixed"
      iframe.style.right = "0"
      iframe.style.bottom = "0"
      iframe.style.width = "0"
      iframe.style.height = "0"
      iframe.style.border = "0"
      document.body.appendChild(iframe)

      const iframeWindow = iframe.contentWindow

      if (!iframeWindow) {
        document.body.removeChild(iframe)
        // Rejeita a Promise se o navegador bloquear o acesso ao iframe
        return reject(
          new Error(
            "Não foi possível acessar a janela do iframe para impressão."
          )
        )
      }

      const iframeDocument = iframeWindow.document

      const style = iframeDocument.createElement("style")
      style.textContent = `
        @page { margin: 0; }
        
        body {
          margin: 0;
          padding: 0;
          width: 100vw;
        }

        .scale-container {
          width: fit-content;
          /* margin: 0 auto; */
        }
      `
      iframeDocument.head.appendChild(style)

      const container = iframeDocument.createElement("div")
      container.className = "scale-container"
      container.innerHTML = htmlString
      iframeDocument.body.appendChild(container)

      // Timeout 1: Aguarda a renderização inicial para podermos medir
      setTimeout(() => {
        const apiWidth = container.scrollWidth

        if (apiWidth > 0) {
          container.style.zoom = `calc(100vw / ${apiWidth})`
        }

        // Timeout 2: Aguarda o zoom ser aplicado antes de focar e imprimir
        setTimeout(() => {
          iframeWindow.focus()

          // Abre a caixa de impressão. A execução do JS pausa aqui na maioria dos navegadores
          // até que a caixa seja fechada (seja por confirmar ou cancelar).
          iframeWindow.print()

          // Timeout 3: Limpeza do DOM e resolução da Promise
          setTimeout(() => {
            document.body.removeChild(iframe)
            resolve() // Sucesso! O processo de impressão terminou.
          }, 1000)
        }, 150)
      }, 100)
    } catch (error) {
      // Captura qualquer erro inesperado durante a execução
      reject(error)
    }
  })
}

type GeneratePdfOptions = {
  htmlString: string | string[]
  pageWidthMm?: number
  pageHeightMm?: number
  containerWidthPx?: number
}

export const generatePdfFromHtml = async ({
  htmlString,
  pageWidthMm = 80,
  pageHeightMm = 80,
  containerWidthPx = 464
}: GeneratePdfOptions): Promise<string> => {
  const htmlStrings = Array.isArray(htmlString) ? htmlString : [htmlString]

  // 1. Configurações centrais do PDF (mm)
  const doc = new jsPDF({
    unit: "mm",
    format: [pageWidthMm, pageHeightMm],
    orientation: "portrait",
    compress: true
  })

  /**
   * Helper para envolver o HTML em um container padronizado que evita cortes
   * e garante que o fundo seja branco durante a captura.
   */
  const wrapContent = (content: string) => `
    <div style="
      width: ${containerWidthPx}px;
      padding: 8px 16px;
      box-sizing: border-box;
      background: white;
      word-wrap: break-word;
      font-smoothing: antialiased;
      -webkit-font-smoothing: antialiased;
    ">
      ${content}
    </div>
  `

  // 2. Construção progressiva do PDF
  // Ao usar html2canvas diretamente, evitamos a criação de iframes, o que limpa os erros de console.
  for (let i = 0; i < htmlStrings.length; i++) {
    // Adicionamos uma página nova se não for a primeira
    if (i > 0) {
      doc.addPage([pageWidthMm, pageHeightMm], "p")
    }

    // Criamos um elemento temporário no nosso DOM para captura direta
    const container = document.body.appendChild(document.createElement("div"))
    container.style.position = "absolute"
    container.style.left = "-9999px"
    container.style.top = "0"
    container.innerHTML = wrapContent(htmlStrings[i])

    try {
      const canvas = await html2canvas(container, {
        scale: 4, // Alta qualidade
        useCORS: true,
        logging: false,
        backgroundColor: "white",
        width: containerWidthPx,
        windowWidth: containerWidthPx + 32 // Evita cortes laterais
      })

      const imgData = canvas.toDataURL("image/png", 1.0)

      // Ajusta a imagem para preencher a largura total da página respeitando a proporção
      const imgWidth = pageWidthMm
      const imgHeight = (canvas.height * imgWidth) / canvas.width

      doc.addImage(imgData, "PNG", 0, 0, imgWidth, imgHeight)
    } finally {
      // Limpeza imediata do elemento após a captura
      document.body.removeChild(container)
    }
  }

  // 3. Finalização e retorno do Base64
  return doc.output("datauristring")
}

export function printPdf(pdfBase64: string) {
  if (!pdfBase64) return

  const cleanedBase64 = pdfBase64.replace(
    "data:application/pdf;filename=generated.pdf;base64,",
    ""
  )

  const byteCharacters = atob(cleanedBase64)
  const byteNumbers = Array.from(byteCharacters, char => char.charCodeAt(0))
  const byteArray = new Uint8Array(byteNumbers)
  const blob = new Blob([byteArray], { type: "application/pdf" })
  const blobUrl = URL.createObjectURL(blob)

  const iframe = document.createElement("iframe")

  iframe.style.position = "fixed"
  iframe.style.right = "0"
  iframe.style.bottom = "0"
  iframe.style.width = "0"
  iframe.style.height = "0"
  iframe.style.border = "0"
  iframe.src = blobUrl

  iframe.onload = () => {
    iframe.contentWindow?.focus()
    iframe.contentWindow?.print()
  }

  document.body.appendChild(iframe)
}
Editor is loading...
Leave a Comment