fix linespacing in table cells, add max height to table rendering

This commit is contained in:
Jörg Prante 2023-08-25 15:25:33 +02:00
parent b3167d16ce
commit 26d8f2756a
11 changed files with 211 additions and 13 deletions

View file

@ -1,5 +1,5 @@
group = org.xbib.graphics
name = graphics
version = 4.5.3
version = 4.5.4
org.gradle.warning.mode = ALL

View file

@ -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());
}

View file

@ -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());
}
}

View file

@ -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+");

View file

@ -42,6 +42,8 @@ public class Table {
private float width;
private float maximumHeight;
public Table(List<Row> rows, List<Column> columns, Set<Point> 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);

View file

@ -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<Renderer, TableRenderContext> 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);
}
}
}

View file

@ -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);
}

View file

@ -17,8 +17,7 @@ public class DrawableCellRenderer extends AbstractCellRenderer<DrawableCell> {
@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();

View file

@ -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();

View file

@ -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();
}
}
}

View file

@ -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
}
]
}
]
}
]
}