Комментарии 17
Прошу прощения, но зачем подключать дополнительные языки, JVM, писать DSLы, если задача версии для печати давно уже легко решается средствами CSS?
Все, что вы описали, легко можно сделать на CSS (а в связке с JS так вообще что угодно), есть Print Media Queries, которые с точностью до миллиметра спозиционируют любой элемент на странице. Вы ведь фронтэндер, CSS один из ваших основных инструментов, нужно лишь документацию поглубже почитать. Начните с лекции от сотрудника Яндекса, например: www.youtube.com/watch?v=xVPCZFBpjsI
Поиграть со стилями печати, скрыть всё что не нужно и надеяться, что всё будет хорошо. Только кнопки, фильтры и подобное распечатаются как есть. А ещё, если таблиц много, надо чтобы каждая была на отдельной странице. И лично меня бесят добавленные ссылки, даты и т.д., которые вылезают при печати с сайта
Все, что вы описали, легко можно сделать на CSS (а в связке с JS так вообще что угодно), есть Print Media Queries, которые с точностью до миллиметра спозиционируют любой элемент на странице. Вы ведь фронтэндер, CSS один из ваших основных инструментов, нужно лишь документацию поглубже почитать. Начните с лекции от сотрудника Яндекса, например: www.youtube.com/watch?v=xVPCZFBpjsI
+2
Я, если честно, не знал, что есть page-break-before и page-break-inside.
К сожалению, это не решит всех проблем в моем случае. У меня упертый заказчик и ему нужна кнопка по которой скачивается PDF, а не открывается окно печати. Кроме того, не во всех браузерах получится конролировать ориентацию страницы.
PS Я не фронтендер на самом деле. Просто в этом проекте сложная админка и я его делаю один.
К сожалению, это не решит всех проблем в моем случае. У меня упертый заказчик и ему нужна кнопка по которой скачивается PDF, а не открывается окно печати. Кроме того, не во всех браузерах получится конролировать ориентацию страницы.
PS Я не фронтендер на самом деле. Просто в этом проекте сложная админка и я его делаю один.
+1
jsPDF — сохранит pdf без необходимости показывать окно печати
codepen.io/AshikNesin/pen/KzgeYX
codepen.io/AshikNesin/pen/KzgeYX
0
А это уже не сочетается с page-break-before и page-break-inside. Скорее всего, эта библиотека переводит HTML в canvas и превращает это в pdf
0
1) ага- jsPDF игнорирует css форматирование для печати.
Но вы можете использовать волшебный коммент
2) pdf генерируется без участия canvas на основе HTML разметки страницы. Т е текст в pdf файле это действительно текст
3) Эта библиотека поддерживает печать только спецсимволов и латиницы. Для всего остального нужно юзать html2canvas
см пост jsPDF + canvas: экспорт в PDF многостраничной таблицы на русском языке
Но вы можете использовать волшебный коммент
<!--ADD_PAGE-->
для разбиения страниц 2) pdf генерируется без участия canvas на основе HTML разметки страницы. Т е текст в pdf файле это действительно текст
3) Эта библиотека поддерживает печать только спецсимволов и латиницы. Для всего остального нужно юзать html2canvas
см пост jsPDF + canvas: экспорт в PDF многостраничной таблицы на русском языке
0
Ну и на худой конец есть wkhtmltopdf, которому на вход можно подать страницу в stdin, а из stdout забрать байты pdf’а.
0
Из всех вариантов получить страницу в пдф, самым адекватным по тому что получается, по гибкости настроек мне показалось взять puppeteer. Из-за того что он тянет полный хром с собой (подводный камень), пдф получаются как если бы прямо нажать на печать страницы в хроме. wkhtmltopdf, например, делает из страницы какое-то месиво с кривым выделением. Также есть возможность настроить как угодно страницу, подсунуть любой скрипт и стиль.
Но, опять же из минусов, это жуткая неповоротливость. То есть имеет смысл держать демона с запущенным puppeter нежели вызывать его каждый раз.
Но, опять же из минусов, это жуткая неповоротливость. То есть имеет смысл держать демона с запущенным puppeter нежели вызывать его каждый раз.
пример на node.js
const puppeteer = require("puppeteer");
const path = require("path");
const base = __dirname;
(async () => {
const browser = await puppeteer.launch({});
const page = await browser.newPage();
await page.goto("file://" + path.resolve(base, "index.html"));
const height = await page.evaluate(
() => document.documentElement.clientHeight
);
await page.pdf({
path: path.resolve(base, "./output.pdf"),
margin: {
top: "2cm",
bottom: "2cm",
left: "1.5cm",
right: "2cm",
},
height: `${height}px`,
});
await browser.close();
})().catch(e => {
console.error(e);
});
+2
т.е. мне надо будет отпавить всю текущую страницу на бек с настроеными стилями для печати? А как пробросить стили?
0
Я бы отправлял на бэк (микросервис на node со ждущим puppeteer) только ту часть страницы, которую составляет теоретический отчет. Прямо брал бы document.querySelector(".report").outerHTML и отправлял бы POST'om на сервер. А на сервере уже вставлял данный отчет в какой-то шаблон, с настроенными стилями и тд. В принципе стили можно на любом этапе присоединять.
Как добавить стили: github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageaddstyletagoptions
Как добавить стили: github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageaddstyletagoptions
Опять же пример
const puppeteer = require("puppeteer");
const path = require("path");
const base = __dirname;
(async () => {
const browser = await puppeteer.launch({});
const page = await browser.newPage();
await page.goto("file://" + path.resolve(base, "index.html"));
const height = await page.evaluate(
() => document.documentElement.clientHeight
);
await page.addStyleTag({
content: `
button {
display: none;
}
`
});
await page.pdf({
path: path.resolve(base, "./output.pdf"),
margin: {
top: "2cm",
bottom: "2cm",
left: "1.5cm",
right: "2cm",
},
height: `${height}px`,
});
await browser.close();
})().catch(e => {
console.error(e);
});
0
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Вы хотите сказать, что HTML — это не DSL? Или конфиг gradle — не DSL?
Вас, наверное, смущает слово language. Думаю, отличие DSL от набора функций в наличии своего синтакисиса/семантики. Так, возможность вызова тех или иных функций возможна только в опрделенном контексте. Код, написанный в это статье не позволяет вставить текст в tr или tr в td. К этому прибавляется минимизация чего-либо не связанного с доменной моделью.
Как яркий пример, можно написать DSL для тестирования в стиле BDD, который будет выглядеть вот так:
Если это не новый маленький язык, то тогда DSL не существует.
Вас, наверное, смущает слово language. Думаю, отличие DSL от набора функций в наличии своего синтакисиса/семантики. Так, возможность вызова тех или иных функций возможна только в опрделенном контексте. Код, написанный в это статье не позволяет вставить текст в tr или tr в td. К этому прибавляется минимизация чего-либо не связанного с доменной моделью.
Как яркий пример, можно написать DSL для тестирования в стиле BDD, который будет выглядеть вот так:
val button = ...
on click button check { ... }
Если это не новый маленький язык, то тогда DSL не существует.
0
НЛО прилетело и опубликовало эту надпись здесь
Если что, в конфиг gradle можно вставлять любой код, который тебе заблогорассудится, потому что это просто groove код.
Это действительно так. Но это же его минус. Сколько шаблонизаторов HTML? Сколько попыток сделать BDD фреймфорк? Все они имеют проблемы из-за того, что рано или поздно им требуется поддержка регулярного языка. От этого возникают специфичные конструкции для for, if и подобного (я боюсь считать сколько РАЗНЫХ вариантов for я видел на этом фоне, иногда по два в одном DSL).
С другой стороны оптимально спроектированный DSL будет иметь серьезные ограничения по использованию. На DSL по HTML от JetBrains, как я понял, нельзя нарисовать HTML, который не будет являться XHTML. В DSL по React не меньше ограничений, что и при использовании typeScript + React. И использование этих DSL в перемешку с регулярноым кодом будет просто дурным тоном, как дурной тон не отделять stateless компоненты в React и переносить логику в шаблонизаторы.
Именно жесткое сужение функционала делает ДСЛ полезным
Это действительно так. Но это же его минус. Сколько шаблонизаторов HTML? Сколько попыток сделать BDD фреймфорк? Все они имеют проблемы из-за того, что рано или поздно им требуется поддержка регулярного языка. От этого возникают специфичные конструкции для for, if и подобного (я боюсь считать сколько РАЗНЫХ вариантов for я видел на этом фоне, иногда по два в одном DSL).
С другой стороны оптимально спроектированный DSL будет иметь серьезные ограничения по использованию. На DSL по HTML от JetBrains, как я понял, нельзя нарисовать HTML, который не будет являться XHTML. В DSL по React не меньше ограничений, что и при использовании typeScript + React. И использование этих DSL в перемешку с регулярноым кодом будет просто дурным тоном, как дурной тон не отделять stateless компоненты в React и переносить логику в шаблонизаторы.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Ещё один dsl на Kotlin или как я печатал PDF из react