Как я могу конвертировать HTML и CSS в PDF на Linux-сервере с помощью PHP? У меня есть HTML-документ, который корректно отображается в браузерах, но его нужно конвертировать в PDF. Я пробовал несколько решений:
- DOMPDF: Возникли проблемы с таблицами и изображениями, чрезмерное потребление памяти
- HTML2PDF/HTML2PS: Лучшее отображение таблиц, но возникали ошибки с неизвестным node_type()
- Htmldoc: Работает с базовым HTML, но имеет слабую поддержку CSS
Мне нужно решение, которое работает на Linux и, в идеале, работает по запросу через PHP на веб-сервере. Какие лучшие подходы или альтернативы существуют для такой конвертации?
Преобразование HTML и CSS в PDF на Linux-серверах с использованием PHP можно осуществить несколькими надежными подходами. Современные решения, такие как headless-браузеры (Chrome/Chromium), обеспечивают наиболее точное рендеринг, в то время как специализированные библиотеки, такие как Mpdf или Snappy, предоставляют нативные для PHP альтернативы, которые решают ограничения, с которыми вы столкнулись при использовании DOMPDF и других инструментов.
Содержание
- Обзор методов преобразования HTML в PDF
- Современные решения на основе headless-браузеров
- Нативные для PHP библиотеки и альтернативы
- Руководство по установке и настройке
- Техники оптимизации производительности
- Устранение распространенных проблем
- Расширенные функции и настройка
Обзор методов преобразования HTML в PDF
Преобразование HTML в PDF на Linux-серверах обычно относится к трем основным категориям:
- Нативные для PHP библиотеки, которые напрямую анализируют HTML и генерируют PDF-вывод
- Инструменты командной строки, которые преобразуют HTML в PDF через системные вызовы
- Решения на основе headless-браузеров, которые рендерят HTML в среде браузера и преобразуют в PDF
Каждый подход имеет свои преимущества и недостатки. Нативные для PHP библиотеки, такие как Mpdf, легковесны и легко интегрируются, но могут испытывать трудности со сложным CSS. Инструменты командной строки, такие как Htmldoc, обеспечивают хорошую базовую поддержку, но с ограниченными возможностями CSS. Headless-браузеры обеспечивают наиболее точное рендеринг, но требуют больше системных ресурсов.
Ключевой момент: Лучший подход зависит от ваших конкретных требований к поддержке CSS, производительности, системным ресурсам и качеству вывода.
Современные решения на основе headless-браузеров
Puppeteer с мостом Node.js
Puppeteer, библиотека для Node.js, обеспечивает превосходное преобразование HTML в PDF через headless Chrome/Chromium. Хотя это не нативное для PHP решение, вы можете создать мост между PHP и Node.js:
// Пример моста PHP к Node.js для Puppeteer
function generatePdfWithPuppeteer($html, $outputPath) {
$tempHtmlFile = tempnam(sys_get_temp_dir(), 'html_');
file_put_contents($tempHtmlFile, $html);
$command = "node puppeteer-pdf.js " . escapeshellarg($tempHtmlFile) . " " . escapeshellarg($outputPath);
exec($command, $output, $returnCode);
unlink($tempHtmlFile);
return ($returnCode === 0);
}
Преимущества:
- Отличная поддержка CSS, включая современные функции
- Точное рендеринг, соответствующее браузерам
- Поддержка сложных макетов, таблиц и изображений
Недостатки:
- Требует Node.js на сервере
- Более высокое потребление памяти
- Более сложная настройка
WeasyPrint
WeasyPrint - это библиотека для Python, которая преобразует HTML/CSS в PDF с отличным соответствием стандартам:
// PHP-обертка для WeasyPrint
function generatePdfWithWeasyPrint($html, $outputPath) {
$tempHtmlFile = tempnam(sys_get_temp_dir(), 'html_');
file_put_contents($tempHtmlFile, $html);
$command = "weasyprint " . escapeshellarg($tempHtmlFile) . " " . escapeshellarg($outputPath);
exec($command, $output, $returnCode);
unlink($tempHtmlFile);
return ($returnCode === 0);
}
Преимущества:
- Отличная поддержка CSS3
- Высокое качество вывода
- Хорошая производительность
Недостатки:
- Требует зависимости Python
- Ограниченная поддержка JavaScript
- Более крутая кривая обучения
Нативные для PHP библиотеки и альтернативы
Mpdf (Улучшенная альтернатива DOMPDF)
Mpdf часто рекомендуется как преемник DOMPDF с лучшей производительностью и функциональностью:
require_once __DIR__ . '/vendor/autoload.php';
$mpdf = new \Mpdf\Mpdf([
'mode' => 'utf-8',
'format' => 'A4',
'margin_left' => 10,
'margin_right' => 10,
'margin_top' => 10,
'margin_bottom' => 10
]);
$mpdf->WriteHTML($htmlContent);
$mpdf->Output('document.pdf', 'D');
Ключевые улучшения по сравнению с DOMPDF:
- Лучшее рендеринг таблиц
- Улучшенная поддержка CSS
- Более низкое потребление памяти
- Лучшая обработка изображений
Установка:
composer require mpdf/mpdf
TCPDF
TCPDF - еще одна надежная PHP-библиотека с обширными функциями:
require_once('tcpdf/tcpdf.php');
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
$pdf->AddPage();
$pdf->writeHTML($htmlContent, true, false, true, false, '');
$pdf->Output('document.pdf', 'D');
Функции:
- Поддержка сложных макетов
- Хорошо подходит для форм и таблиц
- Обширная документация
Snappy (обертка для wkhtmltopdf)
Snappy предоставляет PHP-обертку для мощного инструмента командной строки wkhtmltopdf:
require_once __DIR__ . '/vendor/autoload.php';
use Knp\Snappy\Pdf;
$snappy = new Pdf('/usr/bin/wkhtmltopdf');
$snappy->generateFromHtml(
'<h1>Счет</h1><p>Уважаемый клиент</p>',
'/path/to/bill.pdf'
);
Преимущества:
- Использует реальный движок рендеринга WebKit
- Отличная поддержка CSS
- Хорошо обрабатывает сложные макеты
Руководство по установке и настройке
Системные требования
Перед установкой любого решения для преобразования HTML в PDF убедитесь, что ваш Linux-сервер соответствует этим требованиям:
# Базовые системные требования
sudo apt update
sudo apt install -y php php-cli php-gd php-xml php-mbstring
sudo apt install -y libpng-dev libjpeg-dev libfreetype6-dev
sudo apt install -y fontconfig
Установка Mpdf
# Установка Mpdf через Composer
composer require mpdf/mpdf
# Или сначала установите системные зависимости
sudo apt install -y php-pear php-dev libpng-dev libjpeg-dev libfreetype6-dev
sudo pecl install imagick
echo "extension=imagick.so" | sudo tee /etc/php/$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')/cli/conf.d/imagick.ini
Установка wkhtmltopdf для Snappy
# Загрузка и установка wkhtmltopdf
sudo apt install -y xvfb
wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.bionic_amd64.deb
sudo dpkg -i wkhtmltox_0.12.6-1.bionic_amd64.deb
sudo apt install -f
# Проверка установки
wkhtmltopdf --version
Настройка headless Chrome
# Установка Chrome/Chromium
sudo apt install -y chromium-browser
# Или установка Google Chrome
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google-chrome.list
sudo apt update
sudo apt install -y google-chrome-stable
# Установка Node.js и Puppeteer
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
npm install -g puppeteer
Техники оптимизации производительности
Управление памятью
Для нативных для PHP библиотек, таких как Mpdf:
// Настройте лимиты памяти в php.ini
memory_limit = 512M
# В PHP коде
ini_set('memory_limit', '512M');
# Для больших документов используйте потоковую передачу
$mpdf = new \Mpdf\Mpdf([
'mode' => 'utf-8',
'format' => 'A4',
'tempDir' => '/tmp/mpdf',
'setAutoTopMargin' => 'pad',
'setAutoBottomMargin' => 'pad'
]);
# При необходимости обрабатывайте HTML по частям
$mpdf->WriteHTML($htmlContent);
$mpdf->Output('document.pdf', 'F');
Кэширование и оптимизация
# Реализуйте кэширование для часто генерируемых PDF
function getCachedPdf($key, $html, $ttl = 3600) {
$cacheFile = "/tmp/pdf_cache/{$key}.pdf";
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $ttl) {
return $cacheFile;
}
# Генерация PDF
generatePdfWithLibrary($html, $cacheFile);
return $cacheFile;
}
# Оптимизация HTML для генерации PDF
function optimizeHtmlForPdf($html) {
# Удаление ненужных элементов
$html = preg_replace('/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/mi', '', $html);
$html = preg_replace('/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/mi', '', $html);
# Оптимизация изображений
$html = preg_replace('/<img([^>]+)>/i', '<img$1 loading="eager" />', $html);
return $html;
}
Фоновая обработка
Для ресурсоемких преобразований:
# Используйте очереди или фоновую обработку
function generatePdfInBackground($html, $outputPath, $callback = null) {
$tempHtmlFile = tempnam(sys_get_temp_dir(), 'html_');
file_put_contents($tempHtmlFile, $html);
$command = "nohup php generate_pdf.php " . escapeshellarg($tempHtmlFile) . " " . escapeshellarg($outputPath) . " > /dev/null 2>&1 &";
exec($command);
if ($callback) {
$callback($tempHtmlFile);
}
}
# Рабочий скрипт (generate_pdf.php)
$htmlFile = $argv[1];
$outputFile = $argv[2];
$html = file_get_contents($htmlFile);
generatePdfWithLibrary($html, $outputFile);
unlink($htmlFile);
Устранение распространенных проблем
Проблемы с памятью в DOMPDF/Mpdf
# Решение: увеличьте память и оптимизируйте обработку
$mpdf = new \Mpdf\Mpdf([
'mode' => 'utf-8',
'format' => 'A4',
'tempDir' => '/tmp/mpdf',
'memoryLimit' => '512M',
'debug' => true # включите режим отладки
]);
# Для очень больших документов разбейте на страницы
$htmlParts = explode('<page-break>', $htmlContent);
foreach ($htmlParts as $part) {
$mpdf->AddPage();
$mpdf->WriteHTML($part);
}
Проблемы совместимости CSS
# Решение: используйте CSS-сбросы и стили, специфичные для PDF
$pdfStyles = '
@page {
size: A4;
margin: 1cm;
}
body {
font-family: Arial, sans-serif;
font-size: 12pt;
line-height: 1.4;
}
table {
border-collapse: collapse;
width: 100%;
}
table td, table th {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
img {
max-width: 100%;
height: auto;
}
';
# Примените стили, специфичные для PDF
$html = '<style>' . $pdfStyles . '</style>' . $html;
Проблемы загрузки шрифтов
# Решение: встраивайте пользовательские шрифты
$mpdf = new \Mpdf\Mpdf([
'mode' => 'utf-8',
'format' => 'A4',
'default_font_size' => 0,
'default_font' => 'helvetica',
'fontDir' => [
__DIR__ . '/fonts/',
__DIR__ . '/tcpdf/fonts/',
'/usr/share/fonts/truetype/dejavu/'
],
'fontdata' => [
'customfont' => [
'R' => 'Custom-Regular.ttf',
'B' => 'Custom-Bold.ttf',
'I' => 'Custom-Italic.ttf',
'BI' => 'Custom-BoldItalic.ttf'
]
]
]);
Проблемы рендеринга таблиц
# Решение: используйте HTML-структуру, дружественную к таблицам
$html = '
<table>
<thead>
<tr>
<th>Заголовок 1</th>
<th>Заголовок 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Данные 1</td>
<td>Данные 2</td>
</tr>
</tbody>
</table>
';
# CSS для таблиц
$css = '
table {
border-collapse: collapse;
width: 100%;
page-break-inside: avoid;
}
table td, table th {
border: 1px solid #000;
padding: 6px;
text-align: left;
}
thead {
background-color: #f2f2f2;
}
';
Расширенные функции и настройка
Добавление верхних и нижних колонтитулов
# Пример с Mpdf для верхних и нижних колонтитулов
$mpdf = new \Mpdf\Mpdf();
# Настройка верхних и нижних колонтитулов
$mpdf->SetHTMLHeader('
<div style="text-align: right; font-size: 10pt;">
Страница {PAGENO} из {nbpg}
</div>
');
$mpdf->SetHTMLFooter('
<div style="text-align: center; font-size: 8pt;">
Сгенерировано ' . date('Y-m-d H:i:s') . '
</div>
');
$mpdf->WriteHTML($html);
$mpdf->Output('document.pdf', 'D');
Водяные знаки и безопасность
# Добавление водяного знака в PDF
$mpdf = new \Mpdf\Mpdf();
# Добавление водяного знака
$mpdf->SetWatermarkText('ЧЕРНОВИК');
$mpdf->showWatermarkText = true;
# Настройка безопасности PDF
$mpdf->SetProtection(
['copy', 'print'], // Разрешить копирование и печать
'user_password', // Пароль пользователя
'owner_password' // Пароль владельца
);
$mpdf->WriteHTML($html);
$mpdf->Output('document.pdf', 'D');
Поддержка нескольких языков
# Поддержка разных языков и шрифтов
$mpdf = new \Mpdf\Mpdf([
'mode' => 'utf-8',
'format' => 'A4',
'fontDir' => [
__DIR__ . '/fonts/',
'/usr/share/fonts/truetype/'
],
'fontdata' => [
'cjk' => [
'R' => 'NotoSansCJK-Regular.ttc',
'useOTL' => 0x100
]
]
]);
# Настройки, специфичные для языка
$mpdf->autoScriptToLang = true;
$mpdf->autoLangToFont = true;
$mpdf->WriteHTML('<p>Привет мир!</p>');
$mpdf->WriteHTML('<p>Bonjour le monde!</p>');
$mpdf->Output('multilingual.pdf', 'D');
Генерация динамического контента
# Генерация PDF с динамическими данными из базы данных
function generateInvoicePdf($invoiceId) {
# Получение данных счета
$invoice = getInvoiceData($invoiceId);
$items = getInvoiceItems($invoiceId);
# Генерация HTML-шаблона
$html = '
<h1>Счет #' . $invoice['id'] . '</h1>
<p>Дата: ' . $invoice['date'] . '</p>
<table>
<thead>
<tr>
<th>Товар</th>
<th>Количество</th>
<th>Цена</th>
<th>Итого</th>
</tr>
</thead>
<tbody>';
foreach ($items as $item) {
$html .= '
<tr>
<td>' . $item['description'] . '</td>
<td>' . $item['quantity'] . '</td>
<td>$' . number_format($item['price'], 2) . '</td>
<td>$' . number_format($item['quantity'] * $item['price'], 2) . '</td>
</tr>';
}
$html .= '
</tbody>
</table>
<p><strong>Итого: $' . number_format($invoice['total'], 2) . '</strong></p>
';
# Генерация PDF
$mpdf = new \Mpdf\Mpdf();
$mpdf->WriteHTML($html);
$mpdf->Output('invoice_' . $invoiceId . '.pdf', 'D');
}
Заключение
Преобразование HTML в PDF на Linux-серверах с использованием PHP можно успешно реализовать несколькими подходами в зависимости от ваших конкретных требований:
-
Для высококачественной поддержки CSS: используйте решения на основе headless-браузеров, такие как Puppeteer или WeasyPrint, которые обеспечивают наиболее точное рендеринг, но требуют дополнительных системных зависимостей.
-
Для нативных решений PHP: Mpdf предлагает лучший баланс функций и производительности, решая многие ограничения, с которыми вы столкнулись при использовании DOMPDF, особенно для таблиц и использования памяти.
-
Для сложных макетов: рассмотрите wkhtmltopdf через Snappy, который использует движок рендеринга WebKit для отличной поддержки CSS, оставаясь доступным через PHP.
-
Для оптимизации производительности: реализуйте кэширование, фоновую обработку и техники управления памятью для эффективной работы с большими документами.
Рекомендуемые следующие шаги: начните с Mpdf из-за его легкой интеграции и хорошей производительности, затем исследуйте решения на основе headless-браузеров, если вам нужна поддержка продвинутого CSS. Всегда тестируйте с вашим конкретным HTML-контентом, чтобы убедиться, что совместимость и производительность соответствуют вашим требованиям.
Источники
- Документация Mpdf - Официальная PHP библиотека для генерации PDF
- Документация Puppeteer - API headless Chrome для Node.js
- wkhtmltopdf - Инструмент командной строки для преобразования HTML в PDF
- Документация WeasyPrint - Преобразователь HTML/CSS в PDF
- Документация TCPDF - PHP библиотека для PDF
- PHP-обертка Snappy - PHP интерфейс для wkhtmltopdf
- Лучшие практики производительности PHP - Управление памятью