Przejdź do głównej zawartości

Osadzanie czcionki TrueType i tworzenie jej podzbioru

Zarejestruj czcionkę TrueType, renderuj nią tekst i pozwól zapisującemu osadzić tylko potrzebny podzbiór. Ten przepis korzysta z tego samego przepływu tworzenia treści co examples/04-text-and-fonts.php, uzupełnionego o zarejestrowaną czcionkę TrueType (.ttf).

Okno terminala
composer require nextpdf/core:^3

Ta deklaracja zależności instaluje pakiet nextpdf/core. Przykład działa na PHP 8.4, a dołączony testowy plik LiberationSans-Regular.ttf sprawia, że przepis jest samowystarczalny.

Zarejestruj krój za pomocą FontRegistry::register($path, $alias). Rejestr analizuje plik i zwraca FontInfo, używając TrueTypeParser dla plików .ttf i .otf. Aby aktywować krój, wybierz jego alias za pomocą setFont($alias, ...). To wywołanie rejestruje także użyte punkty kodowe.

Tworzenie podzbioru jest uruchamiane automatycznie podczas save(). Zapisujący czcionki w PDF zbiera użyte punkty kodowe i wywołuje FontSubsetter::subset() albo CffSubsetter w przypadku krojów Compact Font Format (CFF) lub OpenType. Gdy podzbiór jest mniejszy niż pełny program, zapisujący osadza podzbiór. Przepisuje także BaseFont i FontName, dodając sześcioliterowy znacznik podzbioru zapisany wielkimi literami i połączony znakiem plus. Powstaje forma ABCDEF+FontName, której ISO 32000-2 wymaga dla podzbioru czcionki. Zapisujący przechowuje osadzony program TrueType jako FontFile2 w deskryptorze czcionki (ISO 32000-2).

Prefiks podzbioru jest generowany deterministycznie na podstawie nazwy PostScript, więc deterministyczna kompilacja tworzy stabilny znacznik. Dlatego profil odtwarzalności tego przepisu to structural. Profil structural normalizuje prefiks podzbioru oraz /ID ze zwiastuna, zamiast weryfikować je bajt po bajcie.

  • 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; przekaż zarejestrowany alias jako $family.
  • Tworzenie podzbioru odbywa się wewnątrz zapisującego (NextPDF\Writer\PdfFontWriter -> NextPDF\Typography\FontSubsetter). Nie ma publicznego przełącznika do jego włączania ani wyłączania: zapisujący zawsze tworzy podzbiór, gdy punkty kodowe są znane, a podzbiór jest mniejszy.

Pełna tabela PHPDoc jest generowana ze źródła.

<?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() tworzy własny rejestr. Aby użyć samodzielnie wypełnionego rejestru, zbuduj dokument za pomocą DocumentFactory, jak pokazano w przykładzie produkcyjnym. Dzięki fabryce zapisujący odczytuje wcześniej zarejestrowany krój.

Ten przykład jest samowystarczalny i nadaje się do uruchomienia w środowisku testowym. Rejestruje dołączoną LiberationSans-Regular.ttf i renderuje dokument przez DocumentFactory, dzięki czemu używany jest wypełniony rejestr. Opiera się na automatycznym tworzeniu podzbioru przy zapisie.

<?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";

Oczekiwane STDOUT:

Wrote embed-and-subset-fonts.pdf

Aby potwierdzić podzbiór, otwórz wynik i sprawdź słownik czcionki. Wpis BaseFont ma wartość <TAG>+LiberationSans, a deskryptor zawiera FontFile2. Polecenie qpdf --check nie zgłasza błędów strukturalnych.

  • Własność rejestru. Document::createStandalone() tworzy własny rejestr. Czcionka zarejestrowana w osobnym FontRegistry nie będzie w nim widoczna. Użyj DocumentFactory, aby przekazać swój rejestr, jak w przykładzie produkcyjnym.
  • Prawa do osadzania. Tworzenie podzbioru nie zmienia licencjonowania. Osadzaj tylko czcionki, do których osadzania masz licencję. Niektóre czcionki ustawiają bity ograniczeń osadzania; analizator odczytuje te bity, ale odpowiedzialność za zgodność pozostaje po stronie integratora.
  • Ścieżka CFF/OpenType. Dla krojów .otf i CFF podzbiór tworzy CffSubsetter, a nie FontSubsetter. Zachowanie oraz przepisanie znacznika podzbioru są równoważne. Różni się tylko ścieżka kodu.
  • Brak zysku na rozmiarze. Czasami podzbiór nie jest mniejszy niż oryginał. Może się to zdarzyć w przypadku bardzo małych czcionek lub gdy użyto wszystkich glifów. W takim przypadku zapisujący osadza oryginalny program bez znacznika podzbioru. Jest to poprawne, a nie błąd.
  • Czcionki CJK. Duże kroje chińskie, japońskie i koreańskie (CJK) korzystają z warstwowej strategii tworzenia podzbioru zgodnie z ADR-008, z izolowanym podprocesem i natywnym dla PHP rozwiązaniem awaryjnym. Zobacz Składanie tekstu CJK z kodowaniem uwzględniającym cmap, aby poznać szczegóły dotyczące CJK i bieżący stan potoku.

Analiza wymaga jednego przejścia przez tabele czcionki, a koszt tworzenia podzbioru rośnie wraz z liczbą glifów. Dla łacińskich krojów spoza CJK podzbiór powstaje w bieżącym procesie w ramach budżetu wall_ms: 1500, peak_mb: 96. Duże kroje CJK są kierowane do izolowanego podprocesu z dwusekundowym limitem czasu rzeczywistego i natywnym dla PHP rozwiązaniem awaryjnym (ADR-008). Dzięki temu powolne lub kończące się awarią tworzenie podzbioru nie może zablokować wywołującego.

Plik czcionki to niezaufane dane binarne. Analizator odrzuca ścieżki ze stream wrapperami oraz bajty zerowe. Podproces tworzenia podzbioru CJK działa bez odziedziczonych połączeń z bazą danych, uchwytów plików ani stanu frameworka. W razie awarii lub przekroczenia limitu czasu mechanizm bezpiecznie przełącza się na rozwiązanie awaryjne (ADR-008). Weryfikuj pochodzenie czcionek przyjmowanych od użytkowników końcowych.

StwierdzenieSpecyfikacjaKlauzulareference_id
BaseFont/FontName podzbioru czcionki zawiera sześcioliterowy prefiks podzbioru zapisany wielkimi literami i połączony znakiem plus.ISO 32000-2iso32000_2_sec9#x1.x66.p2
Osadzony program czcionki TrueType jest przechowywany jako FontFile2 w deskryptorze czcionki.ISO 32000-2iso32000_2_sec9#x1.x65.p15

Ten przepis pokazuje, jak NextPDF osadza krój TrueType, tworzy jego podzbiór i emituje zgodny prefiks podzbioru. Nie potwierdza zgodności z licencją czcionki. Za prawa do osadzania odpowiada integrator.

Nie dotyczy.