list styles set via class attribute (decimalDot, decimalCircle, lowerAlpha, upperAlpha), * e.g. rendering "(1)" instead of "1." for decimalCircle. * - Explicitly set values on
  • tags, including non-standard values like "1b". * Numbers and single letters (a/A=1, b/B=2, ...) set the list counter, so counting * continues from there; other values leave the counter unaffected. * - The markers are still rendered separately, left of the actual text block. * * Additionally, some vertical spacing behaviors of TCPDF 6 that IPdfWriter::printMotionSection() * and the PDF layouts rely on are restored. * * @phpstan-import-type THTMLRenderContext from \Com\Tecnick\Pdf\HTML * @phpstan-import-type THTMLDOMNode from \Com\Tecnick\Pdf\HTML */ class IPdfWriterEngine extends \TCPDF_ENGINE { /** Block tags that should share the line with the list marker when they are the first child of a
  • */ private const LI_INLINE_BLOCK_TAGS = ['p', 'div', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'pre']; /** @var string|null The value attribute of the list item currently being opened, to be rendered verbatim */ private ?string $currentLiValue = null; /** * TCPDF 6 swallowed a
    at the very beginning of a written cell (more precisely: a
    * that was only preceded by opening block tags). The markup created for line-numbered motion * sections relies on this: IPdfWriter::printMotionSection() inserts an empty slot into the * line number column for every line starting a list, pairing with the
    -joined text lines. * * @param THTMLRenderContext $hrc */ protected function shouldSkipHTMLBrAdvance(array &$hrc, int $key, float $tpx): bool { if ($this->isBrOnlyPrecededByOpeningTags($hrc, $key)) { return true; } return parent::shouldSkipHTMLBrAdvance($hrc, $key, $tpx); } /** * @param THTMLRenderContext $hrc */ private function isBrOnlyPrecededByOpeningTags(array &$hrc, int $key): bool { // Read into a plain local variable first: PHPStan cannot keep the precise THTMLDOMNode // shape for a chained access like $hrc['dom'][$k] inside a loop, since $hrc is by-ref // and could in theory be mutated through another alias between iterations. $dom = $hrc['dom']; $ancestors = []; $parent = $dom[$key]['parent'] ?? -1; $guard = 0; while ($parent >= 0 && $guard < 256) { $ancestors[$parent] = true; $next = $dom[$parent]['parent'] ?? -1; if ($next === $parent) { break; } $parent = $next; $guard++; } for ($k = $key - 1; $k >= 0; $k--) { $node = $dom[$k] ?? null; if ($node === null) { return true; } if (!$node['tag']) { if (trim($node['value']) === '') { continue; // whitespace between tags } return false; } if (!$node['opening'] || !isset($ancestors[$k])) { return false; } } return true; } /** * The parent method applies a default 1em top/bottom margin to p/ol/ul/dl/blockquote/pre. * Legacy TCPDF semantics, which the PDF layouts (and the line numbering next to motion texts) * rely on: for tags explicitly configured through setHtmlVSpace(), that configuration defines * the full vertical spacing - no default margins are added on top. * * @param array $dom */ protected function parseHTMLAttributesBlockMargins(array &$dom, int $key): void { $node = $dom[$key] ?? null; if ($node !== null && isset($this->tagvspaces[$node['value']])) { return; } parent::parseHTMLAttributesBlockMargins($dom, $key); } /** * The parent method starts a new line for every block element unless it is at the very top * of the rendered cell. For a block element (like

    ) that is the first child of a

  • , * this would place the text one line below the list marker. Suppress the vertical advance * in this case, so the text starts on the same line as the marker. * Nested
      /