Для генерации PDF существует множество JavaScript библиотек, одной из лучших можно считать pdfMake. Её можно использовать как на клиенте, так и на сервере. Но минифицированный бандл библиотеки весит больше мегабайта. Именно поэтому мы поговорим о том, как использовать эту библиотеку в cloud function.
Инициализация и импорт
Прежде всего внутри папки function устанавливаем библиотеку:
npm install pdfmake
В js файле функции заимпортим ее и бандл шрифтов Roboto, который идет с библиотекой.
const Printer = require('pdfmake'); const fonts = require('pdfmake/build/vfs_fonts.js');
Бандл с шрифтами необходим для инициализации класса принтера. Важно заметить, что в cloud functions нет возможности указать относительный путь к шрифтам, именно поэтому нам нужен бандл с шрифтами, который идет с библиотекой. Использовать его мы будем таким образом:
const fontDescriptors = { Roboto: { normal: new Buffer(fonts.pdfMake.vfs['Roboto-Regular.ttf'], 'base64'), bold: new Buffer(fonts.pdfMake.vfs['Roboto-Medium.ttf'], 'base64'), italics: new Buffer(fonts.pdfMake.vfs['Roboto-Italic.ttf'], 'base64'), bolditalics: new Buffer(fonts.pdfMake.vfs['Roboto-Italic.ttf'], 'base64'), } };
Следовательно, когда стартует функция, шрифты уже будут в ней.
Тело эндпоинта для генерации PDF
exports.generatePdf = functions.https.onRequest(async (req, res) => { if (request.method !== "GET") { response.send(405, 'HTTP Method ' + request.method + ' not allowed'); return null; } // Код генерации PDF будет тут и возвращать результат в response.send(); });
Дальнейший код мы будем писать в тело этой функции.
Инициализируем инстанс класса принтера с вышеупомянутыми шрифтами, а так же создадим переменную, которая будет собирать чанки pdf файла при его генерации.
const printer = new Printer(fontDescriptors); const chunks = [];
Теперь для генерации pdf, библиотеке нужно передать объект, описывающий структуру документа. Подробно с синтаксисом и функционалом можно ознакомиться в документации библиотеки.
Мы для примера сделаем простой статичный шаблон.
const docDefinition = { content: [ // if you don't need styles, you can use a simple string to define a paragraph 'This is a standard paragraph, using default style', // using a { text: '...' } object lets you set styling properties { text: 'This paragraph will have a bigger font', fontSize: 15 }, // if you set the value of text to an array instead of a string, you'll be able // to style any part individually { text: [ 'This paragraph is defined as an array of elements to make it possible to ', { text: 'restyle part of it and make it bigger ', fontSize: 15 }, 'than the rest.' ] } ] };
Обратите внимание, что генерировать этот шаблон можно динамически, подтянув необходимые данные например из базы firebase или взяв из параметров url запроса.
Также при желании можно добавить параметры документа (формат, ориентацию) и мета-описание документа ( авторство, описание, тайтл ).
Остается лишь передать шаблон в генератор.
const pdfDoc = printer.createPdfKitDocument(docDefinition);
Вызвав данный метод у принтера имеются колбэки, которые вызываются при генерации pdf файла. Их мы будем использовать для создания итогового blob файла, который отдадим клиенту в респонсе.
pdfDoc.on('data', (chunk) => { chunks.push(chunk); }); pdfDoc.on('end', () => { var result = Buffer.concat(chunks); response.setHeader('Content-Type', 'application/pdf'); response.setHeader('Content-disposition', 'attachment; filename=report.pdf'); response.send(result); }); pdfDoc.on('error', (err) => { response.status(501).send(err); }); pdfDoc.end();
При успехе мы задаем хедеры ответа: тип респонса application/pdf
и имя файла. Затем возвращаем его клиенту. При ошибке рендеринга можно вернуть 501 ошибку пользователю.
В результате у нас функция будет иметь следующий вид:
const Printer = require('pdfmake'); const fonts = require('pdfmake/build/vfs_fonts.js'); const fontDescriptors = { Roboto: { normal: new Buffer(fonts.pdfMake.vfs['Roboto-Regular.ttf'], 'base64'), bold: new Buffer(fonts.pdfMake.vfs['Roboto-Medium.ttf'], 'base64'), italics: new Buffer(fonts.pdfMake.vfs['Roboto-Italic.ttf'], 'base64'), bolditalics: new Buffer(fonts.pdfMake.vfs['Roboto-Italic.ttf'], 'base64'), } }; exports.generatePdf = functions.https.onRequest(async (req, res) => { if (request.method !== "GET") { response.send(405, 'HTTP Method ' + request.method + ' not allowed'); return null; } const printer = new Printer(fontDescriptors); const chunks = []; const docDefinition = { content: [ // if you don't need styles, you can use a simple string to define a paragraph 'This is a standard paragraph, using default style', // using a { text: '...' } object lets you set styling properties { text: 'This paragraph will have a bigger font', fontSize: 15 }, // if you set the value of text to an array instead of a string, you'll be able // to style any part individually { text: [ 'This paragraph is defined as an array of elements to make it possible to ', { text: 'restyle part of it and make it bigger ', fontSize: 15 }, 'than the rest.' ] } ] }; const pdfDoc = printer.createPdfKitDocument(docDefinition); pdfDoc.on('data', (chunk) => { chunks.push(chunk); }); pdfDoc.on('end', () => { var result = Buffer.concat(chunks); response.setHeader('Content-Type', 'application/pdf'); response.setHeader('Content-disposition', 'attachment; filename=report.pdf'); response.send(result); }); pdfDoc.on('error', (err) => { response.status(501).send(err); }); pdfDoc.end(); });
Таким образом вызвав cloud function generatePdf будет открываться сразу pdf файл. При желании можно сделать загрузку этого файла в сторедж и возвращать в респонсе ссылку на сохраненный файл.
Подписывайтесь
Для получения уведомлений о новых публикациях подписывайтесь на мой блог или страницы в соц. сетях: Twitter, Facebook.