Перейти к содержимому

Печатная форма и PDF - шаблон печати, генерация файла, отдача

Делаем печать заказа и счёта: отдельная страница с пустым шаблоном, стили печати, сборка PDF библиотекой и проверка прав на документ.

Что нужно знать заранее

Печать из браузера и файл PDF - это две разные задачи с общим шаблоном. Первая дешевле и часто достаточна, вторая нужна там, где документ отправляют письмом или прикладывают к заказу.

Готовой печатной формы под произвольный документ в поставке нет. Собирают её сами: страница с данными, стили печати и, если нужен файл, библиотека сборки PDF из состава проекта.

Печатная форма показывает данные заказа, поэтому доступ к ней закрывают. Адрес вида «страница печати плюс номер заказа» перебирается за минуту, и чужой документ не должен открываться никому.

Шаги

  1. Завести отдельную страницу печати и подключить к ней пустой шаблон сайта.
  2. Проверить на этой странице права: документ видит владелец и сотрудник магазина.
  3. Собрать разметку документа и описать для неё стили печати.
  4. Добавить сборку файла PDF, если документ уходит письмом или в архив.
  5. Проверить кириллицу, разрывы страниц и картинки в готовом файле.

Решение

Делаем страницу печати с пустым шаблоном:

/personal/order/print.php
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/header.php';
$APPLICATION->SetPageProperty('title', 'Заказ №' . $orderId);
$APPLICATION->SetTemplate('print'); // пустой шаблон: без меню, шапки и подвала

Отдельный шаблон печати избавляет от борьбы со стилями сайта. В нём остаётся только разметка документа, а меню, баннеры и подвал не попадают на бумагу вообще.

Проверяем права на документ:

$order = \Bitrix\Sale\Order::load($orderId);
if (!$order || ($order->getUserId() !== $USER->GetID() && !$USER->IsAdmin())) {
\Bitrix\Main\Application::getInstance()->getContext()
->getResponse()->setStatus('403 Forbidden');
return; // чужой заказ по подобранному номеру открываться не должен
}

Проверка прав на печатной форме обязательна и делается до вывода разметки. Ссылку на печать посетитель получает из своего кабинета, но открыть адрес можно и напрямую, подставив чужой номер.

Описываем стили для печати:

@media print {
.no-print { display: none; } /* кнопки и подсказки на бумагу не идут */
table { page-break-inside: avoid; } /* таблица не рвётся посередине строки */
body { font-size: 12pt; color: #000; }
}

Стили печати решают половину задач без всякого PDF. Посетитель нажимает печать в браузере и получает аккуратный документ, а разработчик экономит на библиотеке и её шрифтах.

Собираем файл PDF из той же разметки:

require $_SERVER['DOCUMENT_ROOT'] . '/local/vendor/autoload.php';
$pdf = new \Dompdf\Dompdf(['defaultFont' => 'dejavu sans']); // шрифт с кириллицей
$pdf->loadHtml($html);
$pdf->render();
file_put_contents($path, $pdf->output()); // файл сохраняем, а не отдаём сразу

Библиотека сборки PDF приходит в проект менеджером зависимостей и живёт в своём каталоге. Шрифт с поддержкой кириллицы указывают явно: со шрифтом по умолчанию русский текст превращается в квадраты.

Отдаём готовый файл посетителю:

header('Content-Type: application/pdf');
header("Content-Disposition: attachment; filename*=UTF-8''"
. rawurlencode('Заказ-' . $orderId . '.pdf'));
readfile($path); // имя с кириллицей отдают кодированным заголовком

Имя файла с русскими буквами передают кодированным вариантом заголовка. Иначе браузер сохраняет документ под именем из случайных символов, и покупатель не находит его в загрузках.

Типичные проблемы

В готовом файле вместо русского текста квадраты.

Взят шрифт по умолчанию без поддержки кириллицы. Шрифт с русскими буквами подключают явно и кладут рядом с проектом.

На бумагу выводятся меню, баннеры и подвал сайта.

Печать идёт с обычной страницей сайта без стилей печати. Лишние блоки скрывают правилами для печати или выносят документ на отдельный шаблон.

Чужой заказ открывается подстановкой номера в адрес.

Печатная форма отдаётся без проверки владельца документа. Права проверяют до вывода разметки, а не в шаблоне.

Сборка файла заметно тормозит сайт.

Документ собирается заново на каждый запрос страницы печати. Готовый файл сохраняют и отдают повторно, а тяжёлые документы собирают фоновым заданием.

Картинки не попали в готовый файл.

В разметке указаны адреса, недоступные библиотеке сборки. Картинки подключают локальными путями к файлам на диске сервера.

Частые вопросы

Нужен ли PDF, если есть печать из браузера?

Не всегда: для печати на месте хватает стилей печати. Файл нужен там, где документ отправляют письмом, прикладывают к заказу или хранят.

Какую библиотеку выбрать для PDF?

Любую поддерживаемую с нормальной работой с кириллицей и таблицами. Ставят её менеджером зависимостей в каталог проекта, а не копируют файлами.

Где хранить готовые файлы документов?

В закрытом каталоге вне корня сайта, с отдачей через свой скрипт. Так документ не откроется по прямой ссылке случайному посетителю.

Как сделать печать нескольких документов сразу?

Собрать один файл из нескольких документов подряд с разрывами страниц. Пакетную сборку выносят в фоновое задание: она долгая и тяжёлая.

Можно ли печатать из административного раздела?

Да, ссылку на печать добавляют в карточку заказа своим кодом. Права там проверяет платформа, а разметка используется та же.

Смежное

Первоисточник