Как масштабировать контент, чтобы он соответствовал размеру страницы в 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 &amp; 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 &amp; 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&amp;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&amp;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 &amp; 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 &amp; 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 &amp; 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%.

Другие вопросы по тегам