Ir al contenido

Incrustar y crear un subconjunto de una fuente TrueType

Registrar una fuente TrueType, renderizar texto con ella y dejar que el escritor incruste un subconjunto de la tipografía. Esta receta sigue la misma ruta de contenido que examples/04-text-and-fonts.php, con una fuente TrueType (.ttf) registrada adicional.

Ventana de terminal
composer require nextpdf/core:^3

Esta restricción coincide con el paquete nextpdf/core, y el ejemplo se ejecuta en PHP 8.4. El fixture de prueba incluido LiberationSans-Regular.ttf mantiene esta receta autocontenida.

Registrar una tipografía con FontRegistry::register($path, $alias). El registro analiza el archivo y devuelve un FontInfo; usa TrueTypeParser para los archivos .ttf y .otf. Para activar esa tipografía, seleccionar su alias con setFont($alias, ...). Esta llamada también registra los puntos de código utilizados.

La creación de subconjuntos se ejecuta automáticamente en save(). El escritor de fuentes PDF recopila los puntos de código utilizados y llama a FontSubsetter::subset(), o a CffSubsetter para las tipografías Compact Font Format (CFF) u OpenType. Cuando el subconjunto es más pequeño que el programa completo, el escritor incrusta el subconjunto y reescribe BaseFont y FontName con una etiqueta de subconjunto de seis letras mayúsculas unida con un signo más. Este es el formato ABCDEF+FontName que ISO 32000-2 requiere para un subconjunto de fuente. El escritor almacena el programa TrueType incrustado como FontFile2 en el descriptor de fuente (ISO 32000-2).

El prefijo de subconjunto se genera de forma determinista a partir del nombre PostScript, de modo que una compilación determinista produce una etiqueta estable. Por eso el perfil de reproducibilidad de la receta es structural. El perfil structural normaliza y elimina el prefijo de subconjunto y el /ID del tráiler, en lugar de validarlos byte a byte.

  • FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfoNextPDF\Typography\FontRegistry.
  • setFont(string $family, string $style = '', float $size = 12.0): staticNextPDF\Core\Concerns\HasTypography; pasa el alias registrado como $family.
  • La creación de subconjuntos es interna al escritor (NextPDF\Writer\PdfFontWriter -> NextPDF\Typography\FontSubsetter). No existe un interruptor público para activarla o desactivarla: el escritor siempre crea subconjuntos cuando los puntos de código se conocen y el subconjunto es más pequeño.

La tabla completa de PHPDoc se genera a partir del código fuente.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
use NextPDF\Typography\FontRegistry;
$registry = new FontRegistry();
$registry->register(__DIR__ . '/MyFont-Regular.ttf', alias: 'MyFont');
$doc = Document::createStandalone();
$doc->addPage();
$doc->setFont('MyFont', '', 14);
$doc->cell(0, 10, 'Rendered with an embedded, subset TrueType face.', newLine: true);
$doc->save(__DIR__ . '/out.pdf');

Document::createStandalone() construye su propio registro. Para usar un registro poblado previamente, construir el documento a través de DocumentFactory, como muestra el ejemplo de producción. La fábrica garantiza que el escritor lea la tipografía registrada.

Este ejemplo es autocontenido y se puede ejecutar en el arnés de pruebas. Registra el archivo LiberationSans-Regular.ttf incluido y renderiza a través de DocumentFactory, de modo que se usa el registro poblado y se apoya en la creación automática de subconjuntos al guardar.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;
use NextPDF\Graphics\ImageRegistry;
use NextPDF\Typography\FontRegistry;
// A bundled TrueType test fixture keeps this recipe self-contained.
// Replace with a font you have the right to embed.
$fontPath = __DIR__ . '/../../fonts/test-fixtures/LiberationSans/LiberationSans-Regular.ttf';
if (!is_file($fontPath)) {
// Fall back to the repository-relative fixture location.
$fontPath = dirname(__DIR__, 2) . '/fonts/test-fixtures/LiberationSans/LiberationSans-Regular.ttf';
}
$fontRegistry = new FontRegistry();
$fontRegistry->register($fontPath, alias: 'LiberationSans');
$imageRegistry = new ImageRegistry(maxCacheBytes: 0);
$documentFactory = new DocumentFactory($fontRegistry, $imageRegistry);
$doc = $documentFactory->create();
$doc->setTitle('Embedded Subset Font');
$doc->addPage();
$doc->setFont('LiberationSans', '', 20);
$doc->cell(0, 14, 'Embedded TrueType face', newLine: true);
$doc->setFont('LiberationSans', '', 12);
$doc->multiCell(0, 7, 'Only the glyphs used by this document are embedded. '
. 'The writer subsets the font program and rewrites the BaseFont with a '
. 'deterministic six-letter subset prefix, for example ABCDEF+LiberationSans.');
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
$doc->save($out !== false ? $out : __DIR__ . '/embed-and-subset-fonts.pdf');
echo "Wrote embed-and-subset-fonts.pdf\n";

Salida esperada en STDOUT:

Wrote embed-and-subset-fonts.pdf

Para confirmar el subconjunto, abrir la salida e inspeccionar el diccionario de fuentes. El BaseFont se lee como <TAG>+LiberationSans, y el descriptor lleva un FontFile2. Ejecutar qpdf --check no informa errores estructurales.

  • Propiedad del registro. Document::createStandalone() construye su propio registro. Una fuente registrada en un FontRegistry aparte no es visible para él. Usar DocumentFactory para pasar el registro, como hace el ejemplo de producción.
  • Derechos de incrustación. La creación de subconjuntos no cambia las condiciones de licencia. Incrustar únicamente fuentes con licencia de incrustación. Algunas fuentes establecen bits de restricción de incrustación; el analizador lee esos bits, pero la responsabilidad del cumplimiento sigue siendo del integrador.
  • Ruta CFF/OpenType. Las tipografías .otf y CFF se crean en subconjuntos mediante CffSubsetter, no FontSubsetter. El comportamiento y la reescritura de la etiqueta de subconjunto son equivalentes, y solo difiere la ruta de código.
  • Sin ahorro de tamaño. A veces el subconjunto no es más pequeño que el original, lo que ocurre con fuentes muy pequeñas o cuando se usan todos los glifos. En ese caso el escritor incrusta el programa original sin una etiqueta de subconjunto. Esto es correcto, no un fallo.
  • Fuentes CJK. Las tipografías grandes de chino, japonés y coreano (CJK) usan una estrategia de creación de subconjuntos por niveles según ADR-008, con un subproceso aislado y un mecanismo de respaldo nativo de PHP. Consulta Componer texto CJK con codificación basada en cmap para conocer los detalles de CJK y el estado actual de la canalización.

El análisis realiza una sola pasada sobre las tablas de la fuente, y el costo de la creación de subconjuntos crece con el número de glifos. La creación de subconjuntos de las tipografías latinas no CJK se realiza dentro del proceso, dentro del presupuesto wall_ms: 1500, peak_mb: 96. Las tipografías CJK grandes se enrutan a un subproceso aislado con un tiempo límite de reloj de pared de dos segundos y un mecanismo de respaldo nativo de PHP (ADR-008). Este enrutamiento significa que un subconjunto lento o con fallos no puede bloquear al código llamador.

Un archivo de fuente es una entrada binaria no confiable. El analizador rechaza las rutas con envoltorios de flujo y bytes nulos. El subproceso de creación de subconjuntos CJK se ejecuta sin conexiones de base de datos, manejadores de archivos ni estado del framework heredados, y recurre de forma segura a un mecanismo de respaldo en caso de fallo o tiempo de espera agotado (ADR-008). Validar la procedencia de las fuentes aceptadas de usuarios finales.

DeclaraciónEspecificaciónCláusulareference_id
El BaseFont/FontName de un subconjunto de fuente lleva un prefijo de subconjunto de seis letras mayúsculas unido con un signo más.ISO 32000-2iso32000_2_sec9#x1.x66.p2
Un programa de fuente TrueType incrustado se almacena como FontFile2 en el descriptor de fuente.ISO 32000-2iso32000_2_sec9#x1.x65.p15

Esta receta muestra cómo NextPDF incrusta y crea subconjuntos de una tipografía TrueType y emite un prefijo de subconjunto conforme. No afirma el cumplimiento de la licencia de la fuente, y los derechos de incrustación son responsabilidad del integrador.

No aplicable.