maxPageHeight = $maxPageHeight; $this->currentPageHeight = 0; $this->pages = []; $this->currentPageContent = ''; } public function splitIntoPages($htmlContent) { if (empty($htmlContent)) { return [['content' => '', 'pageNumber' => 1]]; } $dom = new DOMDocument(); @$dom->loadHTML('
' . $htmlContent . '
'); $this->pages = []; $this->currentPageHeight = 0; $this->currentPageContent = ''; $body = $dom->getElementById('content'); if ($body) { $this->processNode($body); } // Добавляем последнюю страницу if (!empty($this->currentPageContent)) { $this->addPage(); } return $this->pages; } private function processNode($node) { $nodeName = strtolower($node->nodeName); // Элементы, которые нельзя разрывать $unbreakableElements = ['table', 'tr', 'img', 'pre', 'code']; if (in_array($nodeName, $unbreakableElements)) { $this->processUnbreakableElement($node); } else { $this->processBreakableElement($node); } } private function processUnbreakableElement($node) { $elementHtml = $this->getOuterHTML($node); $estimatedHeight = $this->estimateElementHeight($elementHtml); // Если элемент не помещается на текущую страницу if ($this->currentPageHeight + $estimatedHeight > $this->maxPageHeight && $this->currentPageHeight > 0) { $this->addPage(); } $this->currentPageContent .= $elementHtml; $this->currentPageHeight += $estimatedHeight; } private function processBreakableElement($node) { if ($node->nodeType === XML_TEXT_NODE) { $this->processTextNode($node); return; } // Для breakable элементов обрабатываем детей по отдельности foreach ($node->childNodes as $child) { $this->processNode($child); } // Добавляем закрывающий тег после обработки всех детей if ($node->nodeType === XML_ELEMENT_NODE) { $this->currentPageContent .= 'nodeName . '>'; } } private function processTextNode($node) { $text = $node->textContent; $words = preg_split('/(\s+)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE); foreach ($words as $word) { if (trim($word) === '') { $this->addContent($word, 4); // Примерная высота пробела continue; } $wordHeight = $this->estimateTextHeight($word); // Если слово не помещается на текущую страницу if ($this->currentPageHeight + $wordHeight > $this->maxPageHeight && $this->currentPageHeight > 0) { $this->addPage(); } $this->addContent($word, $wordHeight); } } private function addContent($content, $height) { $this->currentPageContent .= $content; $this->currentPageHeight += $height; } private function addPage() { $this->pages[] = [ 'content' => $this->currentPageContent, 'pageNumber' => count($this->pages) + 1, 'height' => $this->currentPageHeight ]; $this->currentPageContent = ''; $this->currentPageHeight = 0; } private function estimateElementHeight($html) { // Упрощенная оценка высоты элемента $lineHeight = 20; // Примерная высота строки в px $lines = substr_count($html, 'appendChild($doc->importNode($node, true)); return $doc->saveHTML(); } // Альтернативный метод: разделение по количеству символов public function splitByCharacterCount($htmlContent, $charsPerPage = 3000) { $pages = []; $currentPage = ''; $charCount = 0; // Разделяем HTML на теги и текст $tokens = preg_split('/(<[^>]+>)/', $htmlContent, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); $openTags = []; foreach ($tokens as $token) { // Если это открывающий тег if (preg_match('/^<([^\/][^>]*)>$/', $token, $matches)) { $tagName = strtolower(explode(' ', $matches[1])[0]); $openTags[] = $tagName; $currentPage .= $token; } // Если это закрывающий тег elseif (preg_match('/^<\/([^>]+)>$/', $token, $matches)) { array_pop($openTags); $currentPage .= $token; } // Если это текст else { $text = $token; $textLength = mb_strlen($text); if ($charCount + $textLength > $charsPerPage && $charCount > 0) { // Закрываем открытые теги $currentPage .= $this->closeOpenTags($openTags); $pages[] = [ 'content' => $currentPage, 'pageNumber' => count($pages) + 1 ]; $currentPage = $this->reopenTags($openTags) . $text; $charCount = $textLength; } else { $currentPage .= $text; $charCount += $textLength; } } } // Добавляем последнюю страницу if (!empty($currentPage)) { $pages[] = [ 'content' => $currentPage, 'pageNumber' => count($pages) + 1 ]; } return $pages; } private function closeOpenTags($openTags) { $html = ''; for ($i = count($openTags) - 1; $i >= 0; $i--) { $html .= ''; } return $html; } private function reopenTags($openTags) { $html = ''; foreach ($openTags as $tag) { $html .= '<' . $tag . '>'; } return $html; } }