Salta ai contenuti

Contratti / Tipografia

Il dominio tipografico include il contratto del registro dei font e i contratti di preprocessing del testo: FontRegistryInterface, TextPreprocessorInterface e gli oggetti valore immutabili TextPreprocessResult e TextSegment. Tutti hanno stabilità stable.

Terminal window
composer require nextpdf/core:^3

FontRegistryInterface è l’archivio dei font con ciclo di vita pari a quello del processo. Registra un font TrueType, OpenType, TTC o PFB e restituisce i metadati FontInfo analizzati. Il registro continua a vivere oltre i singoli documenti, quindi un worker analizza ciascun font una sola volta. Può precaricare un insieme di font all’avvio e poi bloccarsi, così il traffico di produzione non può modificarlo. Un registro bloccato genera LogicException quando si chiamano register(), addFontDirectory() o warmup(), mentre le ricerche restano disponibili. Il registro accetta inoltre un font da dati binari grezzi tramite registerFromBinary(). Il bridge @font-face usa questo metodo per registrare un font recuperato da un’origine remota o da un data URI. Il registro contiene solo dati PHP puri — nessun handle di risorsa — quindi è sicuro condividerlo tra un pool di worker.

Il motore incorpora ogni font utilizzato e ne crea il sottoinsieme. Un programma di font incorporato è contenuto nel PDF, così il documento viene reso allo stesso modo su qualsiasi viewer, indipendentemente dai font di sistema installati — ISO 32000-2 §9. Un sottoinsieme di font contiene solo i glifi effettivamente referenziati dal documento, aspetto decisivo per contenuti CJK o ricchi di Unicode — ISO 32000-2 §9. Il contratto del registro espone i metadati analizzati usati dalle fasi di subsetting e incorporamento.

TextPreprocessorInterface intercetta il testo prima che raggiunga il layout dei glifi, il subsetting dei font, la CMap ToUnicode e l’albero della struttura. Questa collocazione definisce la proprietà di sicurezza: un preprocessore che oscura un contenuto lo rimuove prima che possa raggiungere il flusso di contenuto, il sottoinsieme di font o i metadati. Il contratto comporta due invarianti. Un preprocessore non deve introdurre caratteri che influiscono sul layout e deve preservare l’ordine di lettura logico; la sua responsabilità è la sostituzione del contenuto, non il layout. Il risultato è un TextPreprocessResult immutabile che contiene un elenco ordinato di valori TextSegment. Un segmento può essere pass-through oppure oscurato. Per un segmento oscurato, il testo visualizzato dipende dalla modalità di mascheramento: vuoto per un rettangolo black-box, asterischi corrispondenti alla lunghezza originale oppure un’etichetta fissa. Il valore originalCharCount di un segmento è una misura indicativa non reversibile, usata solo per dimensionare un rettangolo di oscuramento. Non deve mai essere usato per ricostruire il contenuto originale.

TipoGenereMembri principaliStabilitàDa
FontRegistryInterfaceinterfaceregister(), get(), has(), all(), addFontDirectory(), warmup(), lock(), isLocked(), registerBase14(), registerFromBinary(), memoryUsage()stable1.7.0
TextPreprocessorInterfaceinterfaceprocess(string): TextPreprocessResultstable1.9.0
TextPreprocessResultfinal readonly class$segments, hasRedactions(), getDisplayText()stable1.9.0
TextSegmentfinal readonly class$displayText, $isRedacted, $originalCharCount, $fillColorstable1.9.0

TextPreprocessResult e TextSegment rendono stabili la firma del costruttore e le proprietà pubbliche; possono essere aggiunti nuovi metodi, ma le proprietà non possono cambiare.

examples/04-text-and-fonts.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, 'Bold heading', newLine: true);
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'Body text rendered with a registered font.');
$doc->save(__DIR__ . '/output/04-text-and-fonts.pdf');

setFont() risolve la famiglia di font tramite FontRegistryInterface. Il documento standalone usa un registro privato. Un worker condivide invece un registro (vedere la pagina del documento).

examples/contracts/typography-production.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\FontRegistryInterface;
use NextPDF\Contracts\TextPreprocessorInterface;
use NextPDF\Exception\NextPdfException;
use Psr\Log\LoggerInterface;
final readonly class FontWarmupService
{
public function __construct(
private FontRegistryInterface $fonts,
private TextPreprocessorInterface $preprocessor,
private LoggerInterface $logger,
) {}
/**
* Warm a font set at boot, then lock the registry.
*
* @param list<string> $fontFiles Absolute paths to font files.
*/
public function boot(array $fontFiles): void
{
try {
$this->fonts->warmup($fontFiles);
$this->fonts->lock();
} catch (NextPdfException $e) {
$this->logger->error('Font warmup failed', ['error' => $e->getMessage()]);
throw $e;
}
}
public function redact(string $text): string
{
$result = $this->preprocessor->process($text);
return $result->hasRedactions()
? $result->getDisplayText()
: $text;
}
}

warmup() seguito da lock() costituisce la sequenza di avvio del worker. Dopo lock(), qualsiasi mutazione genera un’eccezione. Le ricerche continuano a servire il traffico.

  • Un registro bloccato rifiuta qualsiasi metodo di mutazione. Eseguire il precaricamento e il blocco all’avvio; non chiamare mai register() durante la gestione delle richieste.
  • registerFromBinary() scrive i byte del font in un file temporaneo per analizzarli. I dati di font non attendibili costituiscono una superficie di attacco legata all’analisi — vincolarli tramite ExternalResourcePolicyInterface (vedere la pagina dei criteri di sicurezza).
  • Un TextPreprocessor non deve aggiungere interruzioni di riga, ritorni a capo o tabulazioni. Aggiungerli altera il layout e viola il primo invariante del contratto.
  • TextSegment::$originalCharCount è soltanto un’indicazione di larghezza. Usarlo per dedurre il contenuto originale vanifica l’oscuramento e viola il terzo invariante del contratto.
  • TextPreprocessResult::getDisplayText() per progettazione restituisce una stringa vuota per i segmenti black-box. Non considerare un segmento vuoto come un errore di preprocessing.

L’analisi dei font domina il primo utilizzo; il registro ne ammortizza il costo a una sola volta per processo. Dopo il precaricamento, get() e has() sono ricerche su mappa O(1). memoryUsage() restituisce un MemoryReport, così un worker può monitorare la cache dei font rispetto al proprio budget. Il preprocessing del testo è lineare rispetto alla lunghezza dell’input. L’elenco dei segmenti aggiunge un overhead contenuto, proporzionale al numero di corrispondenze di oscuramento. Il performance_budget di 1500 ms di tempo reale e 64 MB di picco copre il precaricamento di un tipico set di font più il rendering del documento. Il costo del subsetting cresce in funzione del numero di glifi effettivamente utilizzati, non dell’intera tabella dei glifi del font. Il subsetting riduce quindi la dimensione dell’output e il costo di rendering per i contenuti CJK.

Il dominio tipografico presenta due superfici rilevanti per la sicurezza. La prima è l’input dei font: registerFromBinary() analizza byte arbitrari. I dati di font non attendibili devono passare attraverso un ExternalResourcePolicyInterface che limiti la dimensione del file e il numero di glifi prima che raggiungano l’analizzatore. La seconda è l’oscuramento: TextPreprocessorInterface è collocato prima del layout dei glifi, del subsetting dei font, della CMap ToUnicode e dell’albero della struttura, proprio affinché il contenuto oscurato non entri mai nell’artefatto renderizzato. Un oscuramento implementato come sovrapposizione in fase di disegno lascia trapelare il testo originale nel flusso di contenuto e nel sottoinsieme di font. La collocazione del contratto previene questa classe di difetti. L’indicazione di misura presente in un segmento è deliberatamente non reversibile. Considerare non attendibile qualsiasi font o testo fornito dall’esterno.

AsserzioneStandardClausolaProva
Ogni font utilizzato dal documento viene incorporato, in modo che il documento venga reso senza dipendere dai font di sistema.ISO 32000-2§9
Il font incorporato viene ridotto a un sottoinsieme che contiene i glifi referenziati dal documento.ISO 32000-2§9

Entrambe le clausole sono parafrasate. NextPDF non riproduce il testo normativo. PDF/A-4 impone l’incorporamento per ogni font. Questa conformità è documentata nelle pagine relative all’estrazione e all’accessibilità.