From 26d8f2756a619d3b9de20e13567970620ca660eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Fri, 25 Aug 2023 15:25:33 +0200 Subject: [PATCH] fix linespacing in table cells, add max height to table rendering --- gradle.properties | 2 +- .../pdfbox/layout/element/TableElement.java | 24 +++- .../scripting/command/CellCommand.java | 8 +- .../scripting/command/TableCommand.java | 6 + .../graphics/pdfbox/layout/table/Table.java | 18 +++ .../pdfbox/layout/table/TableRenderer.java | 7 +- .../pdfbox/layout/table/TextCell.java | 8 ++ .../table/render/DrawableCellRenderer.java | 3 +- .../graphics/pdfbox/layout/text/TextFlow.java | 2 +- .../layout/test/ScriptingTableTest.java | 23 ++++ .../pdfbox/layout/test/scriptingtable.json | 123 ++++++++++++++++++ 11 files changed, 211 insertions(+), 13 deletions(-) create mode 100644 graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/ScriptingTableTest.java create mode 100644 graphics-pdfbox-layout/src/test/resources/org/xbib/graphics/pdfbox/layout/test/scriptingtable.json diff --git a/gradle.properties b/gradle.properties index c0d25f9..cb6e5c5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = org.xbib.graphics name = graphics -version = 4.5.3 +version = 4.5.4 org.gradle.warning.mode = ALL diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/TableElement.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/TableElement.java index a5caa92..b96fdf0 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/TableElement.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/TableElement.java @@ -21,6 +21,8 @@ public class TableElement implements Element, Drawable, Dividable { private Position absolutePosition; + private Position endPosition; + public TableElement() { this.table = Table.builder(); } @@ -75,6 +77,10 @@ public class TableElement implements Element, Drawable, Dividable { table.verticalAlignment(verticalAlignment); } + public void maximumHeight(float maximumHeight) { + table.maximumHeight(maximumHeight); + } + @Override public float getWidth() { return table.build().getWidth(); @@ -102,17 +108,27 @@ public class TableElement implements Element, Drawable, Dividable { this.absolutePosition = absolutePosition; } + public void setEndPosition(Position endPosition) { + this.endPosition = endPosition; + } + + public Position getEndPosition() { + return endPosition; + } + @Override public void draw(PDDocument pdDocument, PDPageContentStream contentStream, Position upperLeft, Transform transform, DrawListener drawListener) throws IOException { - TableRenderer tableRenderer = TableRenderer.builder() + TableRenderer.Builder tableRenderer = TableRenderer.builder() .table(table.build()) .document(pdDocument) .contentStream(contentStream) .startX(upperLeft.getX()) - .startY(upperLeft.getY()) - .build(); - tableRenderer.draw(); + .startY(upperLeft.getY()); + if (endPosition != null) { + tableRenderer.endY(endPosition.getY()); + } + tableRenderer.build().draw(); if (drawListener != null) { drawListener.drawn(this, upperLeft, getWidth(), getHeight()); } diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/command/CellCommand.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/command/CellCommand.java index c7ba0e8..49735c7 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/command/CellCommand.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/command/CellCommand.java @@ -64,8 +64,10 @@ public class CellCommand implements Command { } cell.colSpan(settings.getAsInt("colspan", 1)); cell.rowSpan(settings.getAsInt("rowspan", 1)); + cell.lineSpacing(settings.getAsFloat("linespacing", 1.0f)); state.getElements().peek().add(cell.build()); } else if (settings.containsSetting("markup")) { + float linespacing = settings.getAsFloat("linespacing", 1.0f); ParagraphCell.Builder cell = ParagraphCell.builder(); cell.colSpan(settings.getAsInt("colspan", 1)); cell.rowSpan(settings.getAsInt("rowspan", 1)); @@ -79,16 +81,14 @@ public class CellCommand implements Command { if (settings.containsSetting("alignment")) { paragraph.setAlignment(Alignment.valueOf(settings.get("alignment", "left").toUpperCase(Locale.ROOT))); } + paragraph.setLineSpacing(linespacing); cell.paragraph(paragraph); String value = settings.get("markup"); float size = settings.getAsFloat("fontsize", 11.0f); - float lineSpacing = settings.getAsFloat("linespacing", -1f); Document document = state.getDocument(); Font font = document.getFont(settings.get("font", "helvetica")); cell.add(new Markup().setValue(value).setFont(font).setFontSize(size)); - if (lineSpacing >= 0f) { - cell.lineSpacing(lineSpacing); - } + cell.lineSpacing(linespacing); state.getElements().peek().add(cell.build()); } } diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/command/TableCommand.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/command/TableCommand.java index 4779ff8..efaa93e 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/command/TableCommand.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/command/TableCommand.java @@ -25,6 +25,12 @@ public class TableCommand implements Command { if (settings.containsSetting("x") && settings.containsSetting("y")) { tableElement.setAbsolutePosition(new Position(mmToPt(settings.getAsFloat("x", 0f)), mmToPt(settings.getAsFloat("y", 0f)))); } + if (settings.containsSetting("endx") && settings.containsSetting("endy")) { + tableElement.setEndPosition(new Position(mmToPt(settings.getAsFloat("endx", 0f)), mmToPt(settings.getAsFloat("endy", 0f)))); + } + if (settings.containsSetting("maxheight")) { + tableElement.maximumHeight(mmToPt(settings.getAsFloat("maxheight", 0f))); + } if (settings.containsSetting("padding")) { String padding = settings.get("padding", "0 0 0 0"); String[] paddings = padding.split("\\s+"); diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/Table.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/Table.java index f75e8b2..c7e2cff 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/Table.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/Table.java @@ -42,6 +42,8 @@ public class Table { private float width; + private float maximumHeight; + public Table(List rows, List columns, Set rowSpanCells) { this.rows = rows; this.columns = columns; @@ -64,6 +66,14 @@ public class Table { return width; } + public void setMaximumHeight(float maximumHeight) { + this.maximumHeight = maximumHeight; + } + + public float getMaximumHeight() { + return maximumHeight; + } + public static BorderStyleInterface getDefaultBorderStyle() { return DEFAULT_BORDER_STYLE; } @@ -142,6 +152,8 @@ public class Table { private float width; + private float maximumHeight; + private Builder() { parameters.setFont(DEFAULT_FONT); parameters.setFontSize(DEFAULT_FONT_SIZE); @@ -280,6 +292,11 @@ public class Table { return this; } + public Builder maximumHeight(float maximumHeight) { + this.maximumHeight = maximumHeight; + return this; + } + public Table build() { if (getNumberOfRegularCells() != getNumberOfSpannedCells()) { throw new TableSetupException("Number of table cells does not match with table setup. " + @@ -288,6 +305,7 @@ public class Table { Table table = new Table(rows, columns, rowSpanCells); table.setSettings(parameters); table.setWidth(width); + table.setMaximumHeight(maximumHeight); table.setNumberOfColumns(numberOfColumns); setupConnectionsBetweenElementsFor(table); correctHeightOfCellsDueToRowSpanningIfNecessaryFor(table); diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/TableRenderer.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/TableRenderer.java index aa6d140..d9207ae 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/TableRenderer.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/TableRenderer.java @@ -14,6 +14,8 @@ import java.util.List; import java.util.Queue; import java.util.function.BiConsumer; import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; import static org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode.APPEND; @@ -152,10 +154,13 @@ public class TableRenderer { protected void drawWithFunction(PageData pageData, Point2D.Float startingPoint, BiConsumer consumer) { float y = startingPoint.y; + float miny = startingPoint.y - table.getMaximumHeight(); for (int rowIndex = pageData.firstRowOnPage; rowIndex < pageData.firstRowOnNextPage; rowIndex++) { final Row row = table.getRows().get(rowIndex); y -= row.getHeight(); - drawRow(new Point2D.Float(startingPoint.x, y), row, rowIndex, consumer); + if (table.getMaximumHeight() == 0.0f || y > miny) { + drawRow(new Point2D.Float(startingPoint.x, y), row, rowIndex, consumer); + } } } diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/TextCell.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/TextCell.java index a308727..31ccfd9 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/TextCell.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/TextCell.java @@ -37,6 +37,8 @@ public class TextCell extends AbstractTextCell { private int rowSpan; + private float lineSpacing = 1.0f; + private Builder() { parameters = new Parameters(); } @@ -178,10 +180,16 @@ public class TextCell extends AbstractTextCell { return this; } + public Builder lineSpacing(float lineSpacing) { + this.lineSpacing = lineSpacing; + return this; + } + public TextCell build() { TextCell cell = new TextCell(); cell.setParameters(parameters); cell.setText(text); + cell.setLineSpacing(lineSpacing); if (colSpan > 0) { cell.setColSpan(colSpan); } diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/render/DrawableCellRenderer.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/render/DrawableCellRenderer.java index af55ca3..f17d5fe 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/render/DrawableCellRenderer.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/table/render/DrawableCellRenderer.java @@ -17,8 +17,7 @@ public class DrawableCellRenderer extends AbstractCellRenderer { @Override public void renderContent(TableRenderContext tableRenderContext) { Drawable drawable = cell.getDrawable(); - if (drawable instanceof WidthRespecting) { - WidthRespecting widthRespecting = (WidthRespecting) drawable; + if (drawable instanceof WidthRespecting widthRespecting) { widthRespecting.setMaxWidth(cell.getWidth()); } float x = tableRenderContext.getStartingPoint().x + cell.getPaddingLeft(); diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/text/TextFlow.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/text/TextFlow.java index 0e6c98a..7b46c27 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/text/TextFlow.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/text/TextFlow.java @@ -244,7 +244,7 @@ public class TextFlow implements TextSequence, WidthRespecting { * @return a copy of this text flow where all leading {@link NewLine}s are removed. */ public TextFlow removeLeadingEmptyLines() { - if (text.size() == 0 || !(text.get(0) instanceof NewLine)) { + if (text.isEmpty() || !(text.get(0) instanceof NewLine)) { return this; } TextFlow result = createInstance(); diff --git a/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/ScriptingTableTest.java b/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/ScriptingTableTest.java new file mode 100644 index 0000000..f4743af --- /dev/null +++ b/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/ScriptingTableTest.java @@ -0,0 +1,23 @@ +package org.xbib.graphics.pdfbox.layout.test; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.pdfbox.layout.element.Document; +import org.xbib.graphics.pdfbox.layout.element.scripting.Engine; +import org.xbib.settings.Settings; + +import java.io.FileOutputStream; + +public class ScriptingTableTest { + + @Test + public void testTable() throws Exception { + Settings settings = Settings.settingsBuilder() + .loadFromResource("json", getClass().getResourceAsStream("scriptingtable.json")) + .build(); + try (Engine engine = new Engine()) { + engine.execute(settings); + Document document = engine.getState().getDocument(); + document.render().save(new FileOutputStream("build/scriptingtable.pdf")).close(); + } + } +} diff --git a/graphics-pdfbox-layout/src/test/resources/org/xbib/graphics/pdfbox/layout/test/scriptingtable.json b/graphics-pdfbox-layout/src/test/resources/org/xbib/graphics/pdfbox/layout/test/scriptingtable.json new file mode 100644 index 0000000..5952478 --- /dev/null +++ b/graphics-pdfbox-layout/src/test/resources/org/xbib/graphics/pdfbox/layout/test/scriptingtable.json @@ -0,0 +1,123 @@ +{ + "type": "document", + "margin": "0 0 0 0", + "author": "Jörg Prante", + "font": [ + "helvetica", + "notosans" + ], + "elements": [ + { + "type": "table", + "padding": "10 10 10 10", + "columnwidths": "50 50 50", + "elements": [ + { + "type": "row", + "elements": [ + { + "type": "cell", + "text": "Cell A" + }, + { + "type": "cell", + "text": "Cell B" + }, + { + "type": "cell", + "text": "Cell C" + } + ] + }, + { + "type": "row", + "elements": [ + { + "type": "cell", + "text": "Cell D" + }, + { + "type": "cell", + "text": "Cell E" + }, + { + "type": "cell", + "text": "Cell F" + } + ] + } + ] + }, + { + "type": "table", + "columnwidths": "60 40 20", + "maxheight": 25, + "elements": [ + { + "type": "row", + "elements": [ + { + "type": "cell", + "text": "This is very long text for cell 1 This is very long text for cell 1 This is very long text for cell 1", + "linespacing": 0.5 + }, + { + "type": "cell", + "text": "Cell 2" + }, + { + "type": "cell", + "text": "Cell 3" + } + ] + }, + { + "type": "row", + "elements": [ + { + "type": "cell", + "text": "Cell 4", + "padding": "5 5 5 5", + "borderwidth": 0.5 + }, + { + "type": "cell", + "text": "Cell 5", + "padding": "5 5 5 5", + "borderwidth": 0.5 + }, + { + "type": "cell", + "text": "Cell 6", + "padding": "5 5 5 5", + "borderwidth": 0.5 + } + ] + }, + { + "type": "row", + "elements": [ + { + "type": "cell", + "text": "Cell 7", + "padding": "5 5 5 5", + "borderwidth": 0.5 + }, + { + "type": "cell", + "text": "Cell 8", + "padding": "5 5 5 5", + "borderwidth": 0.5 + }, + { + "type": "cell", + "text": "Cell 9", + "padding": "5 5 5 5", + "borderwidth": 0.5 + } + ] + } + ] + } + ] +}