Введение
Для проекта системы управления информацией дома престарелых Сторона А требует, чтобы различные отчеты финансового модуля экспортировались в виде документов PDF для удобной печати.
Предыдущее решение было генерировать специальную HTML-страницу для печати отчета, а затем нажмите Ctrl + P, чтобы позвонить собственной функции печати браузера для печати. Проблема с этим подходом в том, чтоОтображение с различными разрешениями имеют разные эффекты страниц, поэтому вам нужно установить размер печати специально, это недостаточно удобно для использования.После запуска функции на нее пожаловалась Сторона А...
Выбор колесного инструмента
Цель ясна — экспортировать HTML-контент в PDF. Время ограничено, сначала найдите колесо, а после поиска в Google я выбрал интерфейсный инструмент jspdf. Конкретное использование относительно простое, обратитесь к следующим двум ссылкам:
Анализ решения
Сначала код:
html2canvas(document.body, {
onrendered:function(canvas) {
// 要输出的 PDF 每页的宽高尺寸,单位是 pt
let pageWidth = 841.89
let pageHeight = 592.28
// 要打印内容,转换成 canvas 图片后的宽高尺寸
let contentWidth = canvas.width*3/4
let contentHeight = canvas.height*3/4
// 将要打印内容的图片,等比例缩放至宽度等于输出时 PDF 每页的宽度,此时的图片宽
let imgWidth = pageWidth
// 将要打印内容的图片,等比例缩放至宽度等于输出时 PDF 每页的宽度,此时的图片高
let imgHeight = pageWidth / contentWidth * contentHeight
// 起始内容截取位置
let position = 0
// 剩余未打印内容的高度
let leftHeight = imgHeight
// 获取打印内容 canvas 图片元素
let pageData = canvas.toDataURL('image/jpeg', 1.0)
// 初始化 pdf 容器,三个参数分别是:纸张方向(填'',则是横向)、打印单位、纸张尺寸
let PDF = new JsPDF('landscape', 'pt', 'a4')
// 循环截取打印内容并添加进容器
if (leftHeight < pageHeight) {
PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
} else {
while (leftHeight > 0) {
PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
leftHeight -= pageHeight
position -= pageHeight
if (leftHeight > 0) {
PDF.addPage()
}
}
}
// 将容器中的内容输出为 PDF 文档
pdf.save('content.pdf');
}
})
Имею в виду функцию экспорта pdfэта ссылка на гитхаб, с некоторыми изменениями. Логика функции относительно проста и не нуждается в особом объяснении. Два основных момента:
- Исправлена небольшая ошибка, исходная функция игнорировала проблему преобразования единиц измерения (px в pt), в конце экспортируемого PDF будет пустая страница.
- LeftHeight в исходной функции использует contentHeight, то есть высоту изображения холста перед масштабированием. Это приводит к необходимости повторного преобразования pageHeight для его получения, что увеличивает логическую сложность функции. На самом деле, leftHeight можно установить в imgHeight, то есть высоту после масштабирования, а pageHeight установить в высоту одной страницы PDF, чтобы логика кода была понятнее.
Основная логика этой функции состоит из трех шагов:
- Получите ширину и высоту области содержимого для печати и пропорционально масштабируйте ее, пока ее ширина не станет равной ширине выходной страницы PDF, чтобы получить ширину и высоту масштабированного изображения для печати (imgWidth, imageHeight)
- В соответствии с шириной и высотой одностраничного PDF-файла (pageWidth, pageHeight) циклически захватывайте масштабированные изображения содержимого печати и добавляйте каждое захваченное содержимое в контейнер объектов PDF. (Каждый перехват представляет собой страницу в формате pdf)
- Выведите содержимое контейнера объектов PDF в виде документа PDF.
Проблемы и способы их устранения
Проблема, возникающая на практике, заключается в том, что изображение случайным образом обрезается по вертикали, как показано ниже:
Для бизнес-сценария этого проекта я решил «установить высоту печатного содержимого». Конкретные идеи заключаются в следующем:
- Определить соотношение размеров выходного листа
Например: соотношение сторон бумаги формата A4 = 841,89/592,28 (альбомная ориентация);
- Сохраняйте масштаб неизменным и определяйте ширину и высоту одной печатной страницы с помощью простого преобразования
Например: ширина страницы 1920px, высота 1360px;
- С помощью CSS высота каждого элемента на печатной странице точно контролируется, поэтому содержимое, превышающее высоту одной страницы, является разумно чрезмерным.
Например: я хочу распечатать таблицу с не более чем 34 строками на странице, тогда высота одной строки должна быть установлена на 1360/34 = 40px;
Поскольку на первой странице есть такие элементы, как заголовок и заголовок, отображается только 30 строк, а общая высота заголовка и заголовка составляет 160 пикселей (это может быть основано на фактических потребностях, просто убедитесь, что высота заголовка и заголовка соответствует целое число, кратное высоте одной строки. )
Наконец, проблема неравномерного усечения в вертикальном направлении успешно решена.
Суммировать
Молодец по трем пунктам:
- Быстро найти колесо, правильная идея
- Понимание того, почему, побуждает к глубокому осмыслению принципа реализации, чтобы можно было оптимизировать решение и сделать конечный результат более качественным.
- Я не могу представить...
Не хватает двух моментов:
- На выполнение всей задачи уходил один рабочий день, а эффективность была слишком низкой. Много времени тратится на локальное развертывание проекта (в основном из-за того, что стек технологий самого проекта не очень хорошо подобран, и отсутствует соответствующая документация по развертыванию). Кроме того, я начал менять функцию, не разобравшись в функции в начале, что потратило много времени.
- Функции не инкапсулированы должным образом, и в конечном итоге копируют и вставляют их на все страницы вручную, что тратит много времени.
Дальнейшие исследования и размышления:
- Изучите исходный код jspdf и узнайте больше о принципе реализации экспорта PDF
- Изучите общую схему страницы печати, чтобы увидеть, можно ли функцию экспорта PDF-документов инкапсулировать как компонент vue (сначала для содержимого печати в виде таблицы).