Przejdź do głównej zawartości

Dzielenie długiego kodu HTML na strony

Użyj automatycznych podziałów stron, aby rozłożyć długą treść na wiele stron. Dodaj konspekt, żeby czytelnicy mogli przechodzić między sekcjami. Ten przepis opiera się na examples/12-bookmarks-and-toc.php.

Okno terminala
composer require nextpdf/core:^3

Użyj tego ograniczenia wersji dla pakietu nextpdf/core. Przykład działa w PHP 8.4.

setAutoPageBreak(true, $margin) nakazuje silnikowi rozpocząć nową stronę, zanim treść przekroczy próg dolnego marginesu. Silnik dzieli na tej granicy długi tekst zapisywany przez multiCell() lub writeHtml(). Moduł fragmentacji Cascading Style Sheets (CSS) (css_break_3) ma w macierzy wsparcia ocenę Verified. Stanowi on podstawę działania podziałów w potoku Hypertext Markup Language (HTML).

bookmark($title, $level) dodaje pozycję konspektu w bieżącym położeniu. Pozycja konspektu w formacie Portable Document Format (PDF) wiąże miejsce docelowe, dzięki czemu czytelnicy mogą przejść bezpośrednio do strony (ISO 32000-2). Silnik zapisuje to miejsce docelowe jako wpis Dest pozycji (ISO 32000-2). Użyj argumentu level, aby zagnieżdżać pozycje w hierarchicznym spisie treści na pasku bocznym czytnika.

Potok pozostaje jednoprzebiegowy (ADR-001). Silnik decyduje o paginacji podczas emisji strumienia, bez zachowywania drzewa układu.

  • setAutoPageBreak(bool $enabled, float $margin = 20): staticNextPDF\Core\Concerns\HasPages.
  • bookmark(string $title, int $level = 0, float $y = -1): staticNextPDF\Core\Concerns\HasNavigation.
  • multiCell(...) / writeHtml(string $html): staticNextPDF\Core\Concerns\HasTextOutput.

Pełna tabela PHPDoc jest generowana z kodu źródłowego.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setAutoPageBreak(true, margin: 25);
$doc->addPage();
$doc->bookmark('Section 1', level: 0);
$doc->setFont('helvetica', '', 11);
for ($i = 1; $i <= 80; $i++) {
$doc->multiCell(0, 7, "Paragraph {$i} of a long flowing document.");
}
$doc->save(__DIR__ . '/out.pdf');

Ten samodzielny przykład działa w środowisku testowym. Buduje dokument złożony z wielu rozdziałów, z zagnieżdżonym konspektem i automatycznymi podziałami stron, oraz odzwierciedla examples/12-bookmarks-and-toc.php.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Bookmarks and Navigation');
$doc->setPrintHeader(false);
$doc->setPrintFooter(false);
$doc->setAutoPageBreak(true, margin: 25);
$chapters = [
'Chapter 1: Introduction' => ['What is NextPDF?', 'Key Features'],
'Chapter 2: Getting Started' => ['Installation', 'Your First PDF'],
'Chapter 3: Advanced Topics' => ['Worker-safe Architecture', 'Streaming Output'],
];
$body = 'NextPDF is a modern PDF 2.0 library for PHP. This paragraph is '
. 'repeated so the content overflows the page and the engine inserts '
. 'an automatic page break at the bottom-margin threshold.';
foreach ($chapters as $chapter => $sections) {
$doc->addPage();
$doc->bookmark($chapter, level: 0);
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, $chapter, newLine: true);
$doc->ln(3);
foreach ($sections as $section) {
$doc->bookmark($section, level: 1);
$doc->setFont('helvetica', 'B', 14);
$doc->cell(0, 10, $section, newLine: true);
$doc->setFont('helvetica', '', 11);
for ($i = 0; $i < 12; $i++) {
$doc->multiCell(0, 7, $body);
}
$doc->ln(4);
}
}
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
$doc->save($out !== false ? $out : __DIR__ . '/paginate-long-html.pdf');
echo "Wrote paginate-long-html.pdf\n";

Oczekiwane standardowe wyjście (STDOUT):

Wrote paginate-long-html.pdf
  • Wyłączenie i zapomnienie. Gdy automatyczny podział strony jest wyłączony, silnik przycina treść wykraczającą poza dolny margines, zamiast rozkładać ją na kolejne strony. Włącz go ponownie przed długą treścią.
  • Treść niepodzielna. Pojedynczy blok wyższy niż użyteczna wysokość strony może zgłosić wyjątek UnsplittableContentException. Przyczyną może być bardzo wysoki wiersz tabeli albo duży obraz. Podziel treść źródłową.
  • Zakładka przed treścią. Wywołaj bookmark() w miejscu, na które ma wskazywać cel. Umieść ją bezpośrednio przed następnym nagłówkiem, na żądanej stronie.
  • Nagłówek i stopka rezerwują miejsce. Nagłówek lub stopka wydruku zmniejsza użyteczną wysokość treści, a próg podziału to uwzględnia. Wyłączenie obu elementów, tak jak w tym przykładzie, zapewnia pełną wysokość treści.
  • Zagnieżdżanie konspektu. level określa głębokość zagnieżdżenia. Element podrzędny na poziomie level: 1 musi następować po elemencie nadrzędnym na poziomie level: 0. W przeciwnym razie czytnik spłaszczy drzewo konspektu.

Silnik decyduje o paginacji w jednym przebiegu emisji. Koszt jest liniowy względem długości treści, O(n). Budżet wynosi wall_ms: 2000, peak_mb: 96. Czas rzeczywisty jest nieco wyższy niż w przepisach jednostronicowych, ponieważ wielostronicowe odsyłacze (xref) i składanie konspektu zwiększają nakład pracy. Model strumieniowy utrzymuje ograniczone zużycie pamięci, a konspekt pozostaje małą, płaską listą.

Fragment macierzy wsparcia CSS (tylko wiersze Verified)

Dział zatytułowany „Fragment macierzy wsparcia CSS (tylko wiersze Verified)”

Ta tabela odtwarza tylko wiersze Verified ze zweryfikowanej macierzy wsparcia CSS.

Moduł W3CPoziomStatusDowody
CSS Fragmentation (css_break_3)3Verifiedsrc/Html/Fragmentation/, tests/Unit/Html/PagedMedia/
CSS Table (css_tables_3)3Verifiedsrc/Html/Table/ + wzorcowe pliki PDF
CSS Cascading and Inheritance (css_cascade_3)3Verifiedsrc/Html/Cascade/

Selektory nazwanych stron @page należą do CSS Paged Media. Zanim na nich polegniesz, sprawdź bieżącą ocenę tego modułu w macierzy.

Ograniczenia strumieniowania jednoprzebiegowego (ADR-001)

Dział zatytułowany „Ograniczenia strumieniowania jednoprzebiegowego (ADR-001)”

Silnik emituje podziały stron w miarę przepływu strumienia. Ponieważ nie ma zachowanego drzewa, które można by ponownie rozłożyć, każda decyzja o podziale jest ostateczna. Niektóre treści, takie jak odsyłacz, potrzebują ostatecznego numeru strony po wykonaniu układu. Taka treść jest ograniczona, więc twórz ją z myślą o tym ograniczeniu.

Paginacja należy do kontrolera podziału stron, a nie do parsera. Parser nie emituje surowych operatorów przejścia między stronami; żąda podziału poprzez kontrakt kontrolera.

Model strumieniowy przechowuje bufor bieżącej strony oraz płaską listę konspektu, a nie wszystkie strony naraz. Bardzo długi dokument pozostaje w granicach pułapu ADR-020, ponieważ silnik opróżnia ukończone strony. Tabele i kontenery flex nadal podlegają ograniczeniu 5,000 węzłów na kontekst.

Wrogi dokument nie może wymusić nieograniczonego zużycia pamięci. Limity elementów i zagnieżdżania (ADR-001) oraz budżet węzłów na kontekst (ADR-020) ograniczają nakład pracy. Sprawdzaj długość i strukturę długiej treści dostarczanej przez użytkownika. Silnik renderuje kontrolowany przez atakującego tytuł konspektu jako tekst i nigdy go nie interpretuje.

StwierdzenieSpecyfikacjaKlauzulareference_id
Każda pozycja konspektu może być powiązana z miejscem docelowym, dzięki czemu użytkownik przechodzi bezpośrednio do niej.ISO 32000-2iso32000_2_sec12#x1.x5.p4
Wpis Dest pozycji konspektu nazywa miejsce docelowe wyświetlane po aktywowaniu pozycji.ISO 32000-2iso32000_2_sec12#x1.x11.p30

Ten przepis pokazuje, jak NextPDF rozkłada długą treść i buduje konspekt. Macierz wsparcia ocenia CSS Fragmentation jako Verified.

Nie dotyczy.