ContentStream: emiter strumieni treści PDF
W skrócie
Dział zatytułowany „W skrócie”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.
Instalacja
Dział zatytułowany „Instalacja”composer require nextpdf/core:^3Przegląd koncepcyjny
Dział zatytułowany „Przegląd koncepcyjny”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.
Powierzchnia API
Dział zatytułowany „Powierzchnia API”| Metoda | Sygnatura | Rola |
|---|---|---|
append() | append(string $raw): void | Dodaje dosłownie surowe bajty operatorów (bez zmiany znaczenia) |
beginTag() | beginTag(string $structType, int $mcid): void | Otwiera sekwencję struktury BDC |
endTag() | endTag(): void | Zamyka najgłębiej zagnieżdżoną sekwencję operatorem EMC |
beginArtifact() | beginArtifact(ArtifactSubtype|string $type): void | Otwiera sekwencję artefaktu |
endArtifact() | endArtifact(): void | Zamyka najgłębiej zagnieżdżoną sekwencję artefaktu |
getMarkedContentDepth() | getMarkedContentDepth(): int | Zwraca bieżącą głębokość zagnieżdżenia |
relabelTag() | relabelTag(string $old, string $new, int $mcid): void | Przepisuje wyemitowany znacznik w miejscu |
finish() | finish(): string | Zwraca pełny bufor; zgłasza wyjątek, jeśli jest niezbilansowany |
drain() | drain(): string | Zwraca bufor bez sprawdzania zbilansowania |
peek() | peek(): string | Zwraca bufor bez jego konsumowania |
reset() | reset(): void | Czyści cały stan |
Uruchom composer docs:generate-api-php -- --module=ContentStream, aby wygenerować pełną tabelę PHPDoc.
Przykład kodu — szybki start
Dział zatytułowany „Przykład kodu — szybki start”<?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();Przykład kodu — produkcja
Dział zatytułowany „Przykład kodu — produkcja”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();Przypadki brzegowe i pułapki
Dział zatytułowany „Przypadki brzegowe i pułapki”append()nie wykonuje zmiany znaczenia danych wejściowych. Przekazuj wyłącznie prawidłowe bajty operatorów. Builder ufa kodowi wywołującemu.endTag()iendArtifact()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.
EMCzamyka 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 samegomcid, którego użyto do jego wyemitowania.
Wydajność
Dział zatytułowany „Wydajność”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.
Uwagi dotyczące bezpieczeństwa
Dział zatytułowany „Uwagi dotyczące bezpieczeństwa”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/.
Zgodność
Dział zatytułowany „Zgodność”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ń.