Renderização de PDF

capabilities.pdf.render transforma HTML em PDF sem conhecer endpoint, credencial ou implementação — com limites, cancelamento e erros com retryable.

1. Declare a dependência

Em oon.deploy.json (não em central.app.json):

{
  "capabilities": {
    "pdfRendering": { "required": true, "minVersion": "1.0.0" }
  }
}

Não adicione URL, usuário, senha ou implementation reference — o runtime resolve na publicação.

2. Crie a rota

const { capabilities, defineRoutes, GenericError } = require("@oondemand/oon-core-back");

defineRoutes("/pdf", (router) => {
  router.private.post("/render", { permission: "pdf.render" }, async (req, res) => {
    if (typeof req.body?.html !== "string" || !req.body.html.trim()) {
      throw new GenericError("Informe o HTML.", { statusCode: 422, code: "PDF_HTML_REQUIRED" });
    }

    const controller = new AbortController();
    const abort = () => controller.abort();
    req.once("aborted", abort);
    res.once("close", abort);

    try {
      const pdf = await capabilities.pdf.render(
        { html: req.body.html, format: req.body.format || "A4", printBackground: true },
        {
          ...req.accessContext,
          correlationId: req.correlationId,
          environment: process.env.APP_ENVIRONMENT,
          signal: controller.signal,
        },
      );
      res.type("application/pdf");
      res.set("Content-Disposition", 'attachment; filename="documento.pdf"');
      res.send(pdf);
    } finally {
      req.off("aborted", abort);
      res.off("close", abort);
    }
  });
});

Declare pdf.render em rbac.permissions e conceda só aos perfis necessários.

Assinatura

capabilities.pdf.render(input, context): Promise<Buffer>
input Tipo Regra / padrão
html string obrigatório; máximo padrão de 2 MiB
format A3 | A4 | A5 | Letter | Legal A4
landscape boolean false
marginTop/Bottom/Left/Right number polegadas, 0 a 4; 0.39
printBackground boolean true
timeoutMs number mín. 1.000; padrão 30.000; máx. do runtime 60.000

context aceita tenantId, appCode, environment, operationId, correlationId/requestId, signal e, só em testes, fetchImpl. A saída começa com %PDF- e tem limite padrão de 20 MiB. O alias capabilities.pdfRendering.render é compatível.

3. Preview e download no frontend

<iframe title="Prévia" sandbox="" srcDoc={html} />
import { useOonApi } from "@oondemand/oon-core-front";

function DownloadPdfButton({ html }: { html: string }) {
  const { http } = useOonApi();
  async function download() {
    const response = await http.post("/pdf/render", { html, format: "A4" }, { responseType: "blob" });
    const url = URL.createObjectURL(response.data);
    const link = document.createElement("a");
    link.href = url;
    link.download = "documento.pdf";
    link.click();
    URL.revokeObjectURL(url);
  }
  return <button onClick={download}>Gerar PDF</button>;
}

Nunca envie a geração diretamente a um endpoint de infraestrutura pelo navegador.

Erros

PdfRenderingError expõe code, statusCode, retryable e detalhes sanitizados.

Código Retry Significado
PDF_RENDERING_INVALID_INPUT não HTML vazio ou acima do limite
PDF_RENDERING_INVALID_OPTIONS não formato, margem ou opção inválida
PDF_RENDERING_UNAVAILABLE sim capability não resolvida no ambiente
PDF_RENDERING_CREDENTIAL_UNAVAILABLE sim credencial interna não injetada
PDF_RENDERING_IMPLEMENTATION_ERROR sim implementação respondeu com falha
PDF_RENDERING_TIMEOUT sim prazo excedido ou sinal abortado
PDF_RENDERING_SATURATED sim concorrência/fila excedida
PDF_RENDERING_TRANSPORT_ERROR sim falha transitória de transporte
PDF_RENDERING_INVALID_OUTPUT sim saída inválida ou acima do limite

Repita apenas quando error.retryable === true, com backoff e idempotência na operação chamadora.

Segurança

  • trate HTML do usuário como não confiável; sanitize e use interpolação escapada;
  • prefira HTML autocontido — recursos remotos tornam o resultado não determinístico;
  • nunca inclua segredos no HTML;
  • não registre HTML nem bytes do PDF; registre correlação, duração, tamanhos e código;
  • propague cancelamento por AbortSignal;
  • no runtime local, falha fechado com PDF_RENDERING_UNAVAILABLE;
  • em testes, injete um transport fake por capabilities.configurePdfRendering que devolva bytes iniciados por %PDF-.