Как масштабировать контент, чтобы он соответствовал размеру страницы в itextpdf?
Мне нужно взять несколько отчетов в формате html в pdf, используя itextpdf. В клиенте Mi есть принтеры размером 4x11 дюймов, поэтому отчеты должны иметь такой размер.
Проблема в том, что он хочет, чтобы отчеты, которые обычно занимают полторы страницы формата 4x11, умещались на одной странице формата 4x11. Как будто мы масштабируем изображение.
Следуя руководству itext7 о том, как использовать pdfHtml для создания одностраничного контента, я создал отчеты с размером страницы 4xcontent height.
Сам вопрос в том, есть ли способ масштабировать контент так, чтобы он умещался в 4x11 дюймов независимо от высоты контента.
Вот пример html-кода для преобразования в pdf
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="generator"
content="HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net"/>
<title>#Title</title>
<style type="text/css">
html, body {
padding: 0;
margin: 0;
}
@media print {
@page {
margin: 2px;
}
}
body {
font-family: Verdana, sans-serif;
font-size: 10px;
}
</style>
</head>
<body>
<div class="divTicketWrapper">
<div class="headerLogo"></div>
<div class="headerInfo">
<div style="font-size:18px; font-weight:NUMBERd;">COMPANY, LLC</div>
<div>COMPANY NAME</div>
<div>US, 123344</div>
<div>EMERGENCY CONTACT: 12345667</div>
</div>
<!-- LOAD INFORMATION -->
<div class="sectionHeader" width="100%">
<h3 style="font-size:10px;">INFORMATION</h3>
</div>
<table width="100%">
<tr width="100%">
<td class="divLabel">Product Type:</td>
<td class="divField" colspan="3">product 1</td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">NUMBER #:</td>
<td class="divLeftColumn divField">1234556</td>
<td class="divRightColumn divLabel">Trucked By:</td>
<td class="divRightColumn divField">COMPANY, LLC</td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">Ticket #:</td>
<td class="divLeftColumn divField">123243355</td>
<td class="divRightColumn divLabel">Accepted Date/Time:</td>
<td class="divRightColumn divField">04/28/2000 22:07</td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">Split Ticket # w/ #:</td>
<td class="divLeftColumn divField"></td>
<td class="divRightColumn divLabel">Confirmation #:</td>
<td class="divRightColumn divField">1233223423</td>
</tr>
<!-- DROP OFF TICKET NUMBER -->
<tr width="100%">
<td class="divLeftColumn divLabel">Receipt #:</td>
<td class="divLeftColumn divField"></td>
<td class="divRightColumn divLabel">DO Receipt #:</td>
<td class="divRightColumn divField"></td>
</tr>
</table>
<div class="sectionHeader" width="100%">
<h3 style="font-size:10px;">INFORMATION</h3>
</div>
<table width="100%">
<tr width="100%">
<td class="divLabel">INFORMATION:</td>
<td class="divField" colspan="3">COMPANY LLC</td>
</tr>
<tr width="100%">
<td class="divLabel">Name:</td>
<td class="divField" colspan="3">COMPANY A 2,3,4</td>
</tr>
<tr width="100%">
<td class="divLabel">Other name:</td>
<td class="divField" colspan="3">COMPANY</td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">SOMETHING #:</td>
<td class="divLeftColumn divField">COMPANY</td>
<td class="divRightColumn divLabel">Arrival Date & Time::</td>
<td class="divRightColumn divField">04/28/2000 22:39</td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">SOMETHING #:</td>
<td class="divLeftColumn divField"></td>
<td class="divRightColumn divLabel">Load Time:</td>
<td class="divRightColumn divField">00:08</td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">Legal Description:</td>
<td class="divLeftColumn divField"></td>
<td class="divRightColumn divLabel">Wait Time:</td>
<td class="divRightColumn divField">00:00</td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">Latitude:</td>
<td class="divLeftColumn divField">1212324343</td>
<td class="divRightColumn divLabel">SOMETHING Date & Time:</td>
<td class="divRightColumn divField">04/28/2000 22:47</td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">Longitude:</td>
<td class="divLeftColumn divField">123322344</td>
<td class="divRightColumn divLabel">Loaded Miles:</td>
<td class="divRightColumn divField">100</td>
</tr>
<tr width="100%">
<td class="divLabel">County, State::</td>
<td class="divField" colspan="3">COMPANY</td>
</tr>
<tr width="100%">
<td class="divLabel">Wait Time Notes:</td>
<td class="divField" colspan="3"></td>
</tr>
<tr width="100%">
<td class="divLabel">Reject Notes:</td>
<td class="divField" colspan="3"></td>
</tr>
<tr width="100%">
<td class="divLabel">Other Notes:</td>
<td class="divField" colspan="3">SOMETHING In Production;</td>
</tr>
</table>
<div class="sectionHeader" width="100%">
<h3 style="font-size:10px;">PICK UP</h3>
</div>
<table width="100%">
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">ACCEPT</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap"></td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY Type</td>
<td class="divLeftColumn divField divFieldWrap">COMPANY</td>
<td class="divRightColumn divLabel">BS&W(%)</td>
<td class="divRightColumn divField divFieldWrap">0.1</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">COMPANY</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">90.0</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">0.0</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">90.0</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">0.01</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">90.0</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">0 ft 0 in 0
in<br/>
<br/>
(0.0 in)
</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">53.0</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">0 ft 0 in 0
in<br/>
<br/>
(0.0 in)
</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">49.9</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">194.0</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">COMPANY</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">190.53</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">04/28/2000
22:39
</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">190.72</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">121313</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">0 ft 0 in 0
in<br/>
<br/>
(0.0 in)
</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">04/28/2000
22:47
</td>
</tr>
</table>
<!-- / SOMETHING INFORMATION -->
<div class="sectionHeader" width="100%">
<h3 style="font-size:10px;">PICK UP</h3>
</div>
<table width="100%">
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">ACCEPT</td>
<td class="divRightColumn divLabel">Reject Reason</td>
<td class="divRightColumn divField divFieldWrap"></td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">COMPANY</td>
<td class="divRightColumn divLabel">BS&W(%)</td>
<td class="divRightColumn divField divFieldWrap">0.1</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">COMPANY</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">90.0</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">0.0</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">90.0</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">0.01</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">90.0</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">0 ft 0 in 0
in<br/>
<br/>
(0.0 in)
</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">53.0</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">0 ft 0 in 0
in<br/>
<br/>
(0.0 in)
</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">49.9</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">194.0</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">COMPANY</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">190.53</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">04/28/2000
22:39
</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">190.72</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">108712121215</td>
</tr>
<tr>
<td class="divLeftColumn divLabel">COMPANY</td>
<td class="divLeftColumn divField divFieldWrap">0 ft 0 in 0
in<br/>
<br/>
(0.0 in)
</td>
<td class="divRightColumn divLabel">COMPANY</td>
<td class="divRightColumn divField divFieldWrap">04/28/2000
22:47
</td>
</tr>
</table>
<!-- SOMETHING INFORMATION -->
<div class="sectionHeader" width="100%">
<h3 style="font-size:10px;">COMPANY</h3>
</div>
<table width="100%">
<tr width="100%">
<td class="divLabel">COMPANY:</td>
<td class="divField" colspan="3"></td>
</tr>
<tr width="100%">
<td class="divLabel">COMPANY:</td>
<td class="divField" colspan="3"></td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">COMPANY:</td>
<td class="divLeftColumn divField"></td>
<td class="divRightColumn divLabel">Arrival Date & Time:</td>
<td class="divRightColumn divField"></td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">COMPANY #:</td>
<td class="divLeftColumn divField"></td>
<td class="divRightColumn divLabel">COMPANY:</td>
<td class="divRightColumn divField"></td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">Latitude:</td>
<td class="divLeftColumn divField"></td>
<td class="divRightColumn divLabel">Wait Time:</td>
<td class="divRightColumn divField"></td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">Longitude:</td>
<td class="divLeftColumn divField"></td>
<td class="divRightColumn divLabel">SOMETHING Date & Time:</td>
<td class="divRightColumn divField"></td>
</tr>
<tr width="100%">
<td class="divLabel">COMPANY</td>
<td class="divField" colspan="3">COMPANY</td>
</tr>
<tr width="100%">
<td class="divLabel">Wait Time Notes:</td>
<td class="divField" colspan="3"></td>
</tr>
<tr width="100%">
<td class="divLabel">Other Notes:</td>
<td class="divField" colspan="3"></td>
</tr>
</table>
<!-- / SOMETHING INFORMATION -->
<!-- SOMETHING SOMETHING & SOMETHING -->
<div class="">
<table width="100%" style="border-collapse: collapse;">
<tr width="100%">
<td class="divLeftColumn divLabel divLabelBackGround">
COMPANY
</td>
<td class="divRightColumn divLabel divLabelBackGround">
COMPANY
</td>
</tr>
</table>
<table width="100%">
<tr width="100%">
<td class="divLeftColumn divLabel">COMPANY #:</td>
<td class="divLeftColumn divField">100</td>
<td class="divRightColumn divLabel">COMPANY #:</td>
<td class="divRightColumn divField"></td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">COMPANY #:</td>
<td class="divLeftColumn divField">1015</td>
<td class="divRightColumn divLabel">COMPANY #:</td>
<td class="divRightColumn divField"></td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">COMPANY:</td>
<td class="divLeftColumn divField">COMPANY</td>
<td class="divRightColumn divLabel">COMPANY:</td>
<td class="divRightColumn divField"></td>
</tr>
<tr width="100%">
<td class="divLeftColumn divLabel">COMPANY:</td>
<td class="divLeftColumn divField">102</td>
<td class="divRightColumn divLabel">COMPANY:</td>
<td class="divRightColumn divField"></td>
</tr>
</table>
</div>
<p>hola</p>
<table width="100%" style="border-collapse: collapse;">
<tr width="100%">
<td style="text-align:center;"></td>
</tr>
</table>
</div>
</body>
</html>
Теперь, используя этот java-код, я получаю отчет в формате PDF на двух страницах размером 4x11 дюймов.
String path = "src/main/resources/html/";
String pdFName = String.valueOf(UUID.randomUUID());
PdfWriter pdfWriter = new PdfWriter(new FileOutputStream(path + pdFName + ".pdf"));
PdfDocument pdf = new PdfDocument(pdfWriter);
pdf.setDefaultPageSize(new PageSize(new Rectangle(288, 792)));//4x11 inches
Document document = HtmlConverter.convertToDocument(new FileInputStream(path + "out.html"), pdf, null);
document.close();
И с помощью этого java-кода я получаю отчет об отверстиях на одной странице Pdf 4x15 дюймов.
ConverterProperties properties = new ConverterProperties();
PdfWriter pdfWriter = new PdfWriter(new FileOutputStream(path + pdFName + ".pdf"));
PdfDocument pdf = new PdfDocument(pdfWriter);
pdf.setDefaultPageSize(new PageSize(new Rectangle(288, 14400)));//4x full PDF height
Document document = HtmlConverter.convertToDocument(new FileInputStream(path + "out.html"), pdf, properties);
EndPosition endPosition = new EndPosition();
LineSeparator separator = new LineSeparator(endPosition);
document.add(separator);
document.getRenderer().close();
PdfPage page = pdf.getPage(1);
float y = endPosition.getY() - 36;
page.setMediaBox(new Rectangle(0, y, 288, 14400 - y));
document.close();
private static class EndPosition implements ILineDrawer {
protected float y;
public float getY() {
return y;
}
@Override
public void draw(PdfCanvas pdfCanvas, Rectangle rectangle) {
this.y = rectangle.getY();
}
@Override
public float getLineWidth() {
return 0;
}
@Override
public void setLineWidth(float v) {
}
@Override
public Color getColor() {
return null;
}
@Override
public void setColor(Color color) {
}
}
Но мне все еще нужно, чтобы страница с отверстиями умещалась в 4x11 дюймов независимо от высоты содержимого. Когда я распечатываю страницу 4x15 дюймов, я могу указать принтер, чтобы он поместил содержимое в 4x11 дюймов, и принтер сжал его... но мне нужно это, используя код, а не принтер.
1 ответ
Конечно, просто изменить размер страницы 4x15 на страницу 4x11 можно с помощью матрицы преобразования в потоке контента страницы, но это исказит весь контент, что, вероятно, нежелательно.
Вместо этого мы можем сначала преобразовать HTML в элементы вместо прямого преобразования в документ, а затем масштабировать размер шрифта с различными коэффициентами масштабирования и эмулировать элементы макета на странице, чтобы увидеть, с каким коэффициентом масштабирования все элементы поместятся в один страницу полностью.
Обратите внимание, что подход и код, которые я описываю, основаны на некоторых предположениях, сделанных о входных данных, например, что HTML не использует расширенные макеты CSS, такие как float или абсолютное позиционирование, а также что он не содержит изображений. Для более сложных входных данных могут потребоваться некоторые дополнительные изменения или полное переписывание кода, поэтому, пожалуйста, внимательно проверьте его и используйте на свой страх и риск.
Бинарный поиск - наш лучший друг для тестирования шрифтов разных размеров, который позволяет значительно ускорить работу.
Во-первых, вспомогательная функция для рекурсивного масштабирования размера шрифта элемента:
private void scaleFontSizeRecursively(IElement element, float scale) {
if (element.hasOwnProperty(Property.FONT_SIZE)) {
UnitValue fontSize = element.getOwnProperty(Property.FONT_SIZE);
fontSize.setValue(fontSize.getValue() * scale);
}
if (element instanceof com.itextpdf.layout.element.AbstractElement) {
for (Object child : ((AbstractElement) element).getChildren()) {
scaleFontSizeRecursively((IElement) child, scale);
}
}
}
Теперь самая содержательная часть кода. В теле двоичного поиска мы масштабируем все размеры шрифтов элементов и эмулируем полный макет документа, чтобы увидеть, достаточно ли одной страницы. Затем мы делаем некоторые уборки и пробуем снова, пока не сойдемся. Вы можете настроить количество итераций (в коде установлено 20) по своему вкусу для достижения компромисса между точностью и скоростью. Вы также можете настроить левую и правую границы двоичного поиска, если можете сделать такое суждение в зависимости от ожидаемого ввода. Наконец, мы устанавливаем масштаб размера шрифта, к которому сошлись, и делаем окончательный макет.
PdfDocument pdf = new PdfDocument(pdfWriter);
pdf.setDefaultPageSize(new PageSize(new Rectangle(72 * 4, 72 * 11)));
// Important to set last boolean parameter to false so that added elements are not drawn immediately
Document document = new Document(pdf, pdf.getDefaultPageSize(), false);
List<IElement> elements = HtmlConverter.convertToElements(html);
float lFontSizeScale = 0.001f;
float rFontSize = 1;
// Initially adding all the elements into the document. They will be document's children from now on
for (IElement element : elements) {
if (element instanceof IBlockElement) {
document.add((IBlockElement) element);
} else {
throw new IllegalStateException("Unexpected situation that needs to be additionally handled in code");
}
}
for (int i = 0; i < 20; i++) {
float midFontSize = (lFontSizeScale + rFontSize) / 2;
for (IElement element : elements) {
scaleFontSizeRecursively(element, midFontSize);
}
// Relayouting all the elements with the new font size
document.relayout();
// See if one page was enough
if (document.getPdfDocument().getNumberOfPages() == 1) {
lFontSizeScale = midFontSize;
} else {
rFontSize = midFontSize;
}
// Roll back our font size scaling
for (IElement element : elements) {
scaleFontSizeRecursively(element, 1 / midFontSize);
}
// Remove extra created pages
while (pdf.getNumberOfPages() > 1) {
pdf.removePage(2);
}
}
// Finally setting the font scale that was the match for us and layouting again
for (IElement element : elements) {
scaleFontSizeRecursively(element, lFontSizeScale);
}
document.relayout();
System.out.println("Resultant scale: " + lFontSizeScale);
document.close();
Запуск кода создает действительный документ с одной страницей 4x11 дюймов, и все содержимое присутствует там. Вывод в консоль:
Resultant scale: 0.6297823
Как видите, нам нужно было уменьшить масштаб на ~63%.