Ir al contenido

Inicio rápido — primer renderizado en el edge

En esta página se muestra cómo convertir una cadena HTML en un archivo PDF. Cada llamada mostrada aquí corresponde a un método, y el comportamiento de cada método se verifica en tests/Unit/Cloudflare/CloudflareHtmlRendererTest.php.

  • Un endpoint de Worker que expone el contrato de renderizado sobre HTTPS.
  • Un token bearer aceptado por el Worker.
  • Un cliente PSR-18 y fábricas PSR-17 disponibles en la ruta (consultar /integrations/cloudflare/install/).

El contrato del Worker observado desde CloudflareResponseParser es el siguiente: si la operación tiene éxito, devuelve HTTP 200. La respuesta lleva Content-Type: application/pdf (bytes PDF en bruto) o bien Content-Type: application/json con un campo pdf en base64. Cualquier estado distinto de 200, o cualquier cuerpo que no empiece por el marcador %PDF, se trata como un fallo.

<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Cloudflare\CloudflareRendererConfig;
$config = new CloudflareRendererConfig(
workerUrl: 'https://pdf-renderer.example.workers.dev/render',
apiToken: getenv('CF_PDF_TOKEN') ?: throw new RuntimeException('CF_PDF_TOKEN not set'),
);

El token se lee del entorno; nunca se codifica de forma fija. workerUrl debe ser HTTPS. Si se pasa una URL http://, el puente la rechaza con Worker URL must use HTTPS antes de enviar ninguna solicitud.

use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;
use NextPDF\Cloudflare\CloudflareHtmlRenderer;
$httpFactory = new HttpFactory();
$renderer = new CloudflareHtmlRenderer(
config: $config,
httpClient: new Client(), // PSR-18 ClientInterface
requestFactory: $httpFactory, // PSR-17 RequestFactoryInterface
streamFactory: $httpFactory, // PSR-17 StreamFactoryInterface
logger: null, // optional PSR-3 LoggerInterface
responseFactory: $httpFactory, // PSR-17; enables the pinned transport
);

El constructor requiere cuatro argumentos: la configuración, el cliente PSR-18, la fábrica de solicitudes y la fábrica de flujos. Otros cuatro argumentos son opcionales: el logger, la fábrica del renderizador local, una política de seguridad HTML explícita y la fábrica de respuestas. Cuando se proporciona la fábrica de respuestas, esta habilita el transporte cURL fijado si está presente un conjunto de IP resueltas o un conjunto de pines SPKI (consultar /integrations/cloudflare/security-and-operations/).

use NextPDF\Cloudflare\Exception\CloudflareNotAvailableException;
use NextPDF\Cloudflare\Exception\CloudflareRenderException;
try {
$result = $renderer->render('<h1>Hello from the edge</h1>');
if (!$result->isValid()) {
throw new RuntimeException('Worker did not return a valid PDF');
}
file_put_contents('output.pdf', $result->pdfData);
printf("Wrote %d bytes from edge %s in %.1f ms\n",
$result->size(),
$result->renderLocation !== '' ? $result->renderLocation : 'unknown',
$result->renderTimeMs,
);
} catch (CloudflareRenderException $e) {
// Worker answered but the render failed (HTTP error or malformed body).
fwrite(STDERR, 'Render failed: ' . $e->getMessage() . PHP_EOL);
exit(1);
} catch (CloudflareNotAvailableException $e) {
// Worker unreachable and no usable fallback.
fwrite(STDERR, 'Edge unavailable: ' . $e->getMessage() . PHP_EOL);
exit(2);
}

render() usa por defecto el ancho A4 (595.28 puntos PDF) y la altura detectada automáticamente (heightPt: 0). Los dos tipos de excepción se diferencian deliberadamente. CloudflareRenderException indica un fallo del lado del Worker y no se reintenta con un mecanismo de reserva. CloudflareNotAvailableException significa que no se pudo alcanzar el edge y no había ningún mecanismo de reserva local disponible.

CloudflareRenderResult es final readonly. Los siguientes campos se completan a partir de las cabeceras de respuesta en la ruta binaria, o a partir de campos JSON en la ruta JSON.

MiembroOrigen
pdfDataBytes PDF en bruto
widthPtEl ancho solicitado
heightPtCabecera X-Pdf-Height-Pt / JSON heightPt; usa por defecto 841.89 (altura A4) cuando está ausente o no es positiva
contentHeightPxX-Content-Height-Px / JSON contentHeightPx
renderLocationDerivado del sufijo de la cabecera CF-Ray (ruta binaria) o JSON renderLocation
renderTimeMsX-Render-Time-Ms / JSON renderTimeMs
size()strlen($pdfData)
isValid()true cuando los bytes empiezan por %PDF

Opcional — tamaño de papel, fuentes y CSS personalizados

Sección titulada «Opcional — tamaño de papel, fuentes y CSS personalizados»
$result = $renderer->render(
html: '<div style="font-family: NotoSansCJK;">繁體中文文件</div>',
widthPt: 595.28,
heightPt: 841.89, // explicit A4; 0 lets the Worker auto-detect
fontFiles: ['NotoSansCJKtc-Regular.ttf'],
);

fontFiles solo tiene sentido cuando la configuración establece r2FontBucket. La carga útil incluye entonces el nombre del bucket y las rutas de los archivos de fuentes solicitados, para que el Worker pueda cargarlos.

if ($renderer->isAvailable()) {
$result = $renderer->render($html);
}

isAvailable() envía una solicitud HTTP HEAD autenticada a la URL del Worker. Devuelve true cuando el estado es inferior a 500. Devuelve false sin lanzar una excepción cuando la configuración no es válida o la solicitud falla. Debe tratarse como una indicación, no como una garantía. El Worker aún puede fallar en el POST posterior.

  • /integrations/cloudflare/production-usage/ — conexión del mecanismo de reserva, telemetría y archivado en R2.
  • /integrations/cloudflare/troubleshooting/ — cada fallo asociado con su excepción y mensaje.
  • /integrations/cloudflare/configuration/ — la referencia completa de los campos.