В iText7 есть ли способ, подобный методу calculateHeights в iText5?

#java #itext7 #pdfdocument

#itext7

Вопрос:

В iText5 мы можем получить высоту PdfPTable, когда нам нужно «вычислить общедоступные значения с плавающей запятой (логическое значение в первый раз)».

Но в iText7, как мы можем получить текущее значение высоты таблицы (особенно перед добавлением таблицы в ее родительский элемент)?

Я уже тестировал метод «table.getHeight ()», но он возвращает null. И я также обнаружил, что в объекте визуализации таблицы я могу получить это значение, но ограничение заключается в том, что рендеринг должен запускаться при добавлении таблицы в ее родительский элемент, поэтому время мне не нужно.

Потому что иногда нам нужно это значение для вычисления, чтобы определить значение «оси y».

Комментарии:

1. Вы знаете, что высота не может быть вычислена, если ширина неизвестна, не так ли? Узкая таблица потребует большей высоты, чем широкая таблица. Можете ли вы показать нам код, который доказывает, что вы установили ширину?

2. Привет, Брайан, очень рад видеть твой ответ.

3. Я понимаю, что вы имеете в виду, но в iText5 нам не нужно беспокоиться об этом, просто вызовите метод. Ниже приведен простой пример, хотя вы устанавливаете ширину таблицы и добавляете только одну строку, но все равно не можете получить значение высоты. Таблица table = новая таблица(1); таблица.setWidth(300); таблица.addCell(«1»); // Результат: высота = null System.out.println(«height = » table.getHeight());

Ответ №1:

В iText5 элементы и информация об их положении / размере были немного перемешаны, что позволило вам вызвать calculateWidths PdfPTable элемент.

В iText7 эта функциональность разделена, что обеспечивает разную гибкость для рендеринга / компоновки элементов.

Таким образом, элементы модели, примером которых является Table экземпляр, ничего не знают об их положении или размере. И вызов table.getHeight приводит к null тому table , что для него ранее не было HEIGHT установлено свойство.

Чтобы вычислить высоту таблицы, нужно было бы использовать функциональность рендеринга.

Для элемента модели вы можете получить поддерево средств визуализации, представляющих этот элемент модели и все его дочерние элементы, и layout это в любой заданной области. Чтобы действительно узнать высоту таблицы, вы хотели бы создать область, которой заведомо будет достаточно для размещения всего содержимого элемента.

 PdfDocument pdfDoc = ...
Document doc = ...

Table table = new Table(2)
            .addCell(new Cell().add(new Paragraph("cell 1, 1")))
            .addCell(new Cell().add(new Paragraph("cell 1, 2")));
LayoutResult result = table.createRendererSubTree().setParent(doc.getRenderer()).layout(
            new LayoutContext(new LayoutArea(1, new Rectangle(0, 0, 400, 1e4f))));

System.out.println(result.getOccupiedArea().getBBox().getHeight());
 

Приведенный выше код печатается 22.982422O для меня, но результаты могут отличаться в зависимости от конфигурации и свойств элементов.

Я хотел бы указать на две важные части кода:

  1. Мы передаем 1e4f как высоту LayoutArea , считая, что этого будет достаточно для размещения всей таблицы. Обратите внимание, что если таблица не может быть помещена на эту высоту, результат никогда не превысит эту заданную высоту и, следовательно, он не будет корректным для вашего варианта использования (знайте общую высоту таблицы). Поэтому обязательно укажите высоту, которой будет достаточно для размещения всей таблицы.
  2. .setParent(doc.getRenderer()) здесь важна часть, которая используется для извлечения наследуемых свойств. Обратите внимание, что мы не задавали table элементу много свойств, даже шрифт, но эта информация необходима для определения области, которую будет занимать этот элемент. Таким образом, эта информация будет унаследована от родительской цепочки во layout время. Вы можете проверить это, изменив шрифт документа : document.setFont(newFont); , или размер шрифта: document.setFontSize(24); и наблюдая за результирующим изменением высоты

Ответ №2:

Ну, из-за того, как в iText7 написана среда визуализации, нет способа (пока) вычислить высоту объекта макета до его добавления в родительский документ, поскольку фактическое вычисление высоты для объектов макета происходит, когда они добавляются в Document объект a.

Однако вы можете ретранслировать a Document , позволяя изменять содержимое ранее добавленных элементов. Используя это, вы можете имитировать рендеринг таблиц и получать данные об их высотах при добавлении новых элементов. Все table.getHeight() равно не будет работать, поскольку он извлекает свойство height, и это свойство в настоящее время нигде не задано в процессе рендеринга таблицы.

В приведенном ниже примере я написал удобный метод, который выполняет итерацию по дереву визуализации и выводит область, которую каждая таблица занимает в документе, чтобы показать вам, как вы можете получить вычисленные высоты.

Сам пример добавляет несколько таблиц в документ, отображает занятые области, добавляет несколько ячеек в каждую таблицу, отображает занятые области (они одинаковы, поскольку добавление к элементу, который был добавлен ранее, не запускает макет) и, наконец, вручную запускает ретрансляцию и отображает окончательное занятоеобласти.

 public class DelayedLayout {
    public static String DEST = "target/output/StackOverflow/DelayedLayout/delayed.pdf";

    public static void main(String[] args)throws IOException, FileNotFoundException{
        File file = new File(DEST);
        file.getParentFile().mkdirs();
        new DelayedLayout().createPdf(DEST);
    }

    public void createPdf(String dest) throws IOException, FileNotFoundException{
        PdfWriter writer = new PdfWriter(dest);
        PdfDocument pdfDoc = new PdfDocument(writer);
        boolean immediateFlush = false;
        boolean relayout = true;
        //Set immediate layout to false, so the document doesn't immediatly write render-results to its outputstream
        Document doc = new Document(pdfDoc, PageSize.A4,immediateFlush);
        Table tOne = createSimpleTable();
        for(int i= 0; i< 5; i  ) {
            //Add a table and some whitespace
            doc.add(tOne);

            doc.add(new Paragraph(""));

        }
        System.out.println("nInitial layout results");
        printOccupiedAreasOfTableRenderers(doc.getRenderer());
        System.out.println("nAdding extra cells to the table");
        addToTable(tOne);
        printOccupiedAreasOfTableRenderers(doc.getRenderer());
        System.out.println("nForcing the document to redo the layout");
        if(relayout)doc.relayout();
        printOccupiedAreasOfTableRenderers(doc.getRenderer());
        doc.close();
    }

    /**
     * Create a very simple table
     * @return simple table
     */
    private Table createSimpleTable(){
        int nrOfCols = 3;
        int nrOfRows = 5;

        Table res = new Table(nrOfCols);
        for(int i= 0; i<nrOfRows;i  ){
            for(int j = 0; j<nrOfCols;j  ){
                Cell c = new Cell();
                c.add(new Paragraph("[" i ", " j "]"));
                res.addCell(c);
            }
        }

        return res;
    }

    /**
     * Add some extra cells to an exisiting table
     * @param tab table to add cells to
     */
    private void addToTable(Table tab){
        int nrOfRows = 5;
        int nrOfCols = tab.getNumberOfColumns();
        for(int i=0; i<nrOfRows*nrOfCols;i  ){
            Cell c = new Cell();
            c.add(new Paragraph("Extra cell"  i));
            tab.addCell(c);
        }

    }

    /**
     * Recursively iterate over the renderer tree, writing the occupied area to the console 
     * @param currentNode current renderer-node to check
     */
    private void printOccupiedAreasOfTableRenderers(IRenderer currentNode){
        if(currentNode.getClass().equals(TableRenderer.class)){
            System.out.println("Table renderer with occupied area: "   currentNode.getOccupiedArea());
        }
        for (IRenderer child:currentNode.getChildRenderers()) {
            printOccupiedAreasOfTableRenderers(child);
        }
    }