Przejdź do głównej zawartości

ContentStream: emiter strumieni treści PDF

Moduł ContentStream emituje operatory treści oznaczonej w formacie Portable Document Format (PDF). Otwiera i zamyka znaczniki struktury oraz artefakty, śledzi głębokość zagnieżdżenia i zwraca bufor operatorów.

Okno terminala
composer require nextpdf/core:^3

ContentStreamBuilder to jedyna klasa tego modułu. Tworzy warstwę treści oznaczonej w strumieniu treści strony. Strumień treści koduje treść strony jako sekwencję operatorów — ISO 32000-2 §8. Builder następnie emituje wokół tej treści operatory treści oznaczonej.

append() dodaje surowe bajty operatorów dosłownie. Builder nie wykonuje dla tych danych wejściowych zmiany znaczenia. Za ich poprawność odpowiada kod wywołujący. Korzystaj z tej granicy, gdy potok HTML i moduł Graphics muszą przeplatać własne operatory.

beginTag() otwiera sekwencję oznaczoną znacznikiem struktury. Emituje operator BDC z listą właściwości MCID, zgodnie z ISO 32000-2 §14.6. endTag() emituje odpowiadający mu operator EMC. Builder zlicza głębokość zagnieżdżenia. Jeśli wywołasz endTag() bez otwartej sekwencji, zgłasza PageLayoutException zamiast zapisywać niezbilansowany EMC.

beginArtifact() otwiera sekwencję artefaktu. Używaj artefaktów do elementów dekoracyjnych paginacji — nagłówków, stopek, numerów stron i linii — które muszą pozostać poza drzewem struktury, zgodnie z ISO 32000-2 §14.8.2.2. Podtyp jest jedną z czterech wartości ISO: Pagination, Layout, Page lub Background. Preferuj typowany enum ArtifactSubtype. Przeciążenie przyjmujące ciąg znaków jest walidowane względem tego enuma, więc niestandardowa wartość od razu kończy się błędem.

relabelTag() przepisuje wcześniej wyemitowany znacznik w miejscu. finish() zwraca pełny bufor i zgłasza wyjątek, jeśli treść oznaczona jest niezbilansowana. drain() zwraca dotychczasowy bufor bez sprawdzania zbilansowania, na potrzeby strumieniowania przyrostowego. peek() zwraca bufor bez konsumowania go. reset() czyści stan.

MetodaSygnaturaRola
append()append(string $raw): voidDodaje dosłownie surowe bajty operatorów (bez zmiany znaczenia)
beginTag()beginTag(string $structType, int $mcid): voidOtwiera sekwencję struktury BDC
endTag()endTag(): voidZamyka najgłębiej zagnieżdżoną sekwencję operatorem EMC
beginArtifact()beginArtifact(ArtifactSubtype|string $type): voidOtwiera sekwencję artefaktu
endArtifact()endArtifact(): voidZamyka najgłębiej zagnieżdżoną sekwencję artefaktu
getMarkedContentDepth()getMarkedContentDepth(): intZwraca bieżącą głębokość zagnieżdżenia
relabelTag()relabelTag(string $old, string $new, int $mcid): voidPrzepisuje wyemitowany znacznik w miejscu
finish()finish(): stringZwraca pełny bufor; zgłasza wyjątek, jeśli jest niezbilansowany
drain()drain(): stringZwraca bufor bez sprawdzania zbilansowania
peek()peek(): stringZwraca bufor bez jego konsumowania
reset()reset(): voidCzyści cały stan

Uruchom composer docs:generate-api-php -- --module=ContentStream, aby wygenerować pełną tabelę PHPDoc.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\ContentStream\ContentStreamBuilder;
$builder = new ContentStreamBuilder();
$builder->beginTag('P', mcid: 0);
$builder->append("BT /F1 12 Tf 72 720 Td (Hello) Tj ET\n");
$builder->endTag();
$pageContent = $builder->finish();

Użyj tego wzorca, aby owinąć akapit znacznikiem struktury, a stopkę artefaktem. W tym wzorcu bufor jest strumieniowany przyrostowo za pomocą drain().

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Accessibility\ArtifactSubtype;
use NextPDF\ContentStream\ContentStreamBuilder;
$builder = new ContentStreamBuilder();
$builder->beginTag('H1', mcid: 0);
$builder->append($titleOperators);
$builder->endTag();
$builder->beginArtifact(ArtifactSubtype::Pagination);
$builder->append($footerOperators);
$builder->endArtifact();
if ($builder->getMarkedContentDepth() !== 0) {
throw new RuntimeException('Unbalanced marked content before flush.');
}
$chunk = $builder->drain();
  • append() nie wykonuje zmiany znaczenia danych wejściowych. Przekazuj wyłącznie prawidłowe bajty operatorów. Builder ufa kodowi wywołującemu.
  • endTag() i endArtifact() zgłaszają wyjątek, gdy zabraknie otwartej sekwencji. Nigdy nie zamykaj sekwencji, która nie jest otwarta.
  • finish() sprawdza zbilansowanie i zgłasza wyjątek, gdy głębokość nie jest równa zero. drain() nie sprawdza. drain() stosuj wyłącznie do strumieniowania przyrostowego.
  • Licznik głębokości nie rozróżnia znaczników od artefaktów. EMC zamyka najgłębiej zagnieżdżoną sekwencję dowolnego z tych rodzajów. Zagnieżdżaj sekwencje z zachowaniem ścisłej kolejności.
  • Przeciążenie metody beginArtifact() przyjmujące ciąg znaków jest walidowane względem enuma. Niestandardowy podtyp powoduje błąd w momencie wywołania, a nie w danych wyjściowych.
  • relabelTag() przepisuje wyemitowany znacznik. Użyj tego samego mcid, którego użyto do jego wyemitowania.

Każda operacja to dopisanie ciągu znaków o złożoności O(1), z wyjątkiem relabelTag(), która wykonuje przepisanie o złożoności O(buffer). Moduł przechowuje jeden bufor ciągu znaków i jeden całkowitoliczbowy licznik głębokości. Nie wykonuje żadnego parsowania i alokuje wyłącznie bufor. Budżet dla obciążenia referencyjnego wynosi 1500 ms czasu rzeczywistego i 64 MB w szczycie. Ten moduł działa znacznie poniżej tego budżetu.

append() stanowi granicę zaufania. Builder zapisuje bajty dosłownie, więc wcześniejszy kod w łańcuchu musi zmienić znaczenie każdego ciągu znaków, który trafia do operatora ciągu literalnego. Kanonicznym narzędziem do zmiany znaczenia jest PdfStringEscaper::escapeLiteral() (ADR-015). Nigdy nie przekazuj przez append() tekstu użytkownika bez zmiany znaczenia. Sprawdzanie zbilansowania w endTag(), endArtifact() oraz finish() zapobiega dotarciu niepoprawnego drzewa treści oznaczonej do modułu Writer. Model zagrożeń dokumentu opisano w /modules/core/security/.

Moduł emituje zgodne z ISO 32000-2 struktury operatorów treści oznaczonej: pary BDC/EMC z listą właściwości MCID zgodnie z §14.6 oraz sekwencje artefaktów zgodnie z §14.8.2.2. Są to fakty dotyczące implementacji. Dowodem są src/ContentStream/ContentStreamBuilder.php, enum src/Accessibility/ArtifactSubtype.php oraz tests/Unit/ContentStream/ContentStreamBuilderMarkedContentBalanceCoverageTest wraz z ContentStreamBuilderRelabelTagInvariantTest. Nie stanowią one deklaracji kompleksowej zgodności z PDF/UA-2 ani PDF 2.0. Zewnętrzny oracle waliduje strukturę otagowanego PDF, w której uczestniczą te operatory: tests/Integration/Accessibility/VeraPdfUa2GoldenTest sprawdza wygenerowaną próbkę względem veraPDF dla profilu PDF/UA-2. Ten test typu oracle jest pomijany, gdy brakuje pliku binarnego veraPDF, więc stanowi opcjonalnie dołączaną bramkę. Stwierdzaj, że ten moduł „wytwarza struktury treści oznaczonej; zgodność z PDF/UA-2 jest walidowana przez veraPDF”, zamiast deklarować zgodność bez zastrzeżeń.