fix linespacing in table cells, add max height to table rendering
This commit is contained in:
parent
b3167d16ce
commit
26d8f2756a
11 changed files with 211 additions and 13 deletions
|
@ -1,5 +1,5 @@
|
||||||
group = org.xbib.graphics
|
group = org.xbib.graphics
|
||||||
name = graphics
|
name = graphics
|
||||||
version = 4.5.3
|
version = 4.5.4
|
||||||
|
|
||||||
org.gradle.warning.mode = ALL
|
org.gradle.warning.mode = ALL
|
||||||
|
|
|
@ -21,6 +21,8 @@ public class TableElement implements Element, Drawable, Dividable {
|
||||||
|
|
||||||
private Position absolutePosition;
|
private Position absolutePosition;
|
||||||
|
|
||||||
|
private Position endPosition;
|
||||||
|
|
||||||
public TableElement() {
|
public TableElement() {
|
||||||
this.table = Table.builder();
|
this.table = Table.builder();
|
||||||
}
|
}
|
||||||
|
@ -75,6 +77,10 @@ public class TableElement implements Element, Drawable, Dividable {
|
||||||
table.verticalAlignment(verticalAlignment);
|
table.verticalAlignment(verticalAlignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void maximumHeight(float maximumHeight) {
|
||||||
|
table.maximumHeight(maximumHeight);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getWidth() {
|
public float getWidth() {
|
||||||
return table.build().getWidth();
|
return table.build().getWidth();
|
||||||
|
@ -102,17 +108,27 @@ public class TableElement implements Element, Drawable, Dividable {
|
||||||
this.absolutePosition = absolutePosition;
|
this.absolutePosition = absolutePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEndPosition(Position endPosition) {
|
||||||
|
this.endPosition = endPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position getEndPosition() {
|
||||||
|
return endPosition;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(PDDocument pdDocument, PDPageContentStream contentStream,
|
public void draw(PDDocument pdDocument, PDPageContentStream contentStream,
|
||||||
Position upperLeft, Transform transform, DrawListener drawListener) throws IOException {
|
Position upperLeft, Transform transform, DrawListener drawListener) throws IOException {
|
||||||
TableRenderer tableRenderer = TableRenderer.builder()
|
TableRenderer.Builder tableRenderer = TableRenderer.builder()
|
||||||
.table(table.build())
|
.table(table.build())
|
||||||
.document(pdDocument)
|
.document(pdDocument)
|
||||||
.contentStream(contentStream)
|
.contentStream(contentStream)
|
||||||
.startX(upperLeft.getX())
|
.startX(upperLeft.getX())
|
||||||
.startY(upperLeft.getY())
|
.startY(upperLeft.getY());
|
||||||
.build();
|
if (endPosition != null) {
|
||||||
tableRenderer.draw();
|
tableRenderer.endY(endPosition.getY());
|
||||||
|
}
|
||||||
|
tableRenderer.build().draw();
|
||||||
if (drawListener != null) {
|
if (drawListener != null) {
|
||||||
drawListener.drawn(this, upperLeft, getWidth(), getHeight());
|
drawListener.drawn(this, upperLeft, getWidth(), getHeight());
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,8 +64,10 @@ public class CellCommand implements Command {
|
||||||
}
|
}
|
||||||
cell.colSpan(settings.getAsInt("colspan", 1));
|
cell.colSpan(settings.getAsInt("colspan", 1));
|
||||||
cell.rowSpan(settings.getAsInt("rowspan", 1));
|
cell.rowSpan(settings.getAsInt("rowspan", 1));
|
||||||
|
cell.lineSpacing(settings.getAsFloat("linespacing", 1.0f));
|
||||||
state.getElements().peek().add(cell.build());
|
state.getElements().peek().add(cell.build());
|
||||||
} else if (settings.containsSetting("markup")) {
|
} else if (settings.containsSetting("markup")) {
|
||||||
|
float linespacing = settings.getAsFloat("linespacing", 1.0f);
|
||||||
ParagraphCell.Builder cell = ParagraphCell.builder();
|
ParagraphCell.Builder cell = ParagraphCell.builder();
|
||||||
cell.colSpan(settings.getAsInt("colspan", 1));
|
cell.colSpan(settings.getAsInt("colspan", 1));
|
||||||
cell.rowSpan(settings.getAsInt("rowspan", 1));
|
cell.rowSpan(settings.getAsInt("rowspan", 1));
|
||||||
|
@ -79,16 +81,14 @@ public class CellCommand implements Command {
|
||||||
if (settings.containsSetting("alignment")) {
|
if (settings.containsSetting("alignment")) {
|
||||||
paragraph.setAlignment(Alignment.valueOf(settings.get("alignment", "left").toUpperCase(Locale.ROOT)));
|
paragraph.setAlignment(Alignment.valueOf(settings.get("alignment", "left").toUpperCase(Locale.ROOT)));
|
||||||
}
|
}
|
||||||
|
paragraph.setLineSpacing(linespacing);
|
||||||
cell.paragraph(paragraph);
|
cell.paragraph(paragraph);
|
||||||
String value = settings.get("markup");
|
String value = settings.get("markup");
|
||||||
float size = settings.getAsFloat("fontsize", 11.0f);
|
float size = settings.getAsFloat("fontsize", 11.0f);
|
||||||
float lineSpacing = settings.getAsFloat("linespacing", -1f);
|
|
||||||
Document document = state.getDocument();
|
Document document = state.getDocument();
|
||||||
Font font = document.getFont(settings.get("font", "helvetica"));
|
Font font = document.getFont(settings.get("font", "helvetica"));
|
||||||
cell.add(new Markup().setValue(value).setFont(font).setFontSize(size));
|
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());
|
state.getElements().peek().add(cell.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,12 @@ public class TableCommand implements Command {
|
||||||
if (settings.containsSetting("x") && settings.containsSetting("y")) {
|
if (settings.containsSetting("x") && settings.containsSetting("y")) {
|
||||||
tableElement.setAbsolutePosition(new Position(mmToPt(settings.getAsFloat("x", 0f)), mmToPt(settings.getAsFloat("y", 0f))));
|
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")) {
|
if (settings.containsSetting("padding")) {
|
||||||
String padding = settings.get("padding", "0 0 0 0");
|
String padding = settings.get("padding", "0 0 0 0");
|
||||||
String[] paddings = padding.split("\\s+");
|
String[] paddings = padding.split("\\s+");
|
||||||
|
|
|
@ -42,6 +42,8 @@ public class Table {
|
||||||
|
|
||||||
private float width;
|
private float width;
|
||||||
|
|
||||||
|
private float maximumHeight;
|
||||||
|
|
||||||
public Table(List<Row> rows, List<Column> columns, Set<Point> rowSpanCells) {
|
public Table(List<Row> rows, List<Column> columns, Set<Point> rowSpanCells) {
|
||||||
this.rows = rows;
|
this.rows = rows;
|
||||||
this.columns = columns;
|
this.columns = columns;
|
||||||
|
@ -64,6 +66,14 @@ public class Table {
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMaximumHeight(float maximumHeight) {
|
||||||
|
this.maximumHeight = maximumHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getMaximumHeight() {
|
||||||
|
return maximumHeight;
|
||||||
|
}
|
||||||
|
|
||||||
public static BorderStyleInterface getDefaultBorderStyle() {
|
public static BorderStyleInterface getDefaultBorderStyle() {
|
||||||
return DEFAULT_BORDER_STYLE;
|
return DEFAULT_BORDER_STYLE;
|
||||||
}
|
}
|
||||||
|
@ -142,6 +152,8 @@ public class Table {
|
||||||
|
|
||||||
private float width;
|
private float width;
|
||||||
|
|
||||||
|
private float maximumHeight;
|
||||||
|
|
||||||
private Builder() {
|
private Builder() {
|
||||||
parameters.setFont(DEFAULT_FONT);
|
parameters.setFont(DEFAULT_FONT);
|
||||||
parameters.setFontSize(DEFAULT_FONT_SIZE);
|
parameters.setFontSize(DEFAULT_FONT_SIZE);
|
||||||
|
@ -280,6 +292,11 @@ public class Table {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder maximumHeight(float maximumHeight) {
|
||||||
|
this.maximumHeight = maximumHeight;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Table build() {
|
public Table build() {
|
||||||
if (getNumberOfRegularCells() != getNumberOfSpannedCells()) {
|
if (getNumberOfRegularCells() != getNumberOfSpannedCells()) {
|
||||||
throw new TableSetupException("Number of table cells does not match with table setup. " +
|
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 table = new Table(rows, columns, rowSpanCells);
|
||||||
table.setSettings(parameters);
|
table.setSettings(parameters);
|
||||||
table.setWidth(width);
|
table.setWidth(width);
|
||||||
|
table.setMaximumHeight(maximumHeight);
|
||||||
table.setNumberOfColumns(numberOfColumns);
|
table.setNumberOfColumns(numberOfColumns);
|
||||||
setupConnectionsBetweenElementsFor(table);
|
setupConnectionsBetweenElementsFor(table);
|
||||||
correctHeightOfCellsDueToRowSpanningIfNecessaryFor(table);
|
correctHeightOfCellsDueToRowSpanningIfNecessaryFor(table);
|
||||||
|
|
|
@ -14,6 +14,8 @@ import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode.APPEND;
|
import static org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode.APPEND;
|
||||||
|
|
||||||
|
@ -152,12 +154,15 @@ public class TableRenderer {
|
||||||
|
|
||||||
protected void drawWithFunction(PageData pageData, Point2D.Float startingPoint, BiConsumer<Renderer, TableRenderContext> consumer) {
|
protected void drawWithFunction(PageData pageData, Point2D.Float startingPoint, BiConsumer<Renderer, TableRenderContext> consumer) {
|
||||||
float y = startingPoint.y;
|
float y = startingPoint.y;
|
||||||
|
float miny = startingPoint.y - table.getMaximumHeight();
|
||||||
for (int rowIndex = pageData.firstRowOnPage; rowIndex < pageData.firstRowOnNextPage; rowIndex++) {
|
for (int rowIndex = pageData.firstRowOnPage; rowIndex < pageData.firstRowOnNextPage; rowIndex++) {
|
||||||
final Row row = table.getRows().get(rowIndex);
|
final Row row = table.getRows().get(rowIndex);
|
||||||
y -= row.getHeight();
|
y -= row.getHeight();
|
||||||
|
if (table.getMaximumHeight() == 0.0f || y > miny) {
|
||||||
drawRow(new Point2D.Float(startingPoint.x, y), row, rowIndex, consumer);
|
drawRow(new Point2D.Float(startingPoint.x, y), row, rowIndex, consumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void drawRow(Point2D.Float start, Row row, int rowIndex, BiConsumer<Renderer, TableRenderContext> consumer) {
|
protected void drawRow(Point2D.Float start, Row row, int rowIndex, BiConsumer<Renderer, TableRenderContext> consumer) {
|
||||||
float x = start.x;
|
float x = start.x;
|
||||||
|
|
|
@ -37,6 +37,8 @@ public class TextCell extends AbstractTextCell {
|
||||||
|
|
||||||
private int rowSpan;
|
private int rowSpan;
|
||||||
|
|
||||||
|
private float lineSpacing = 1.0f;
|
||||||
|
|
||||||
private Builder() {
|
private Builder() {
|
||||||
parameters = new Parameters();
|
parameters = new Parameters();
|
||||||
}
|
}
|
||||||
|
@ -178,10 +180,16 @@ public class TextCell extends AbstractTextCell {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder lineSpacing(float lineSpacing) {
|
||||||
|
this.lineSpacing = lineSpacing;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public TextCell build() {
|
public TextCell build() {
|
||||||
TextCell cell = new TextCell();
|
TextCell cell = new TextCell();
|
||||||
cell.setParameters(parameters);
|
cell.setParameters(parameters);
|
||||||
cell.setText(text);
|
cell.setText(text);
|
||||||
|
cell.setLineSpacing(lineSpacing);
|
||||||
if (colSpan > 0) {
|
if (colSpan > 0) {
|
||||||
cell.setColSpan(colSpan);
|
cell.setColSpan(colSpan);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,7 @@ public class DrawableCellRenderer extends AbstractCellRenderer<DrawableCell> {
|
||||||
@Override
|
@Override
|
||||||
public void renderContent(TableRenderContext tableRenderContext) {
|
public void renderContent(TableRenderContext tableRenderContext) {
|
||||||
Drawable drawable = cell.getDrawable();
|
Drawable drawable = cell.getDrawable();
|
||||||
if (drawable instanceof WidthRespecting) {
|
if (drawable instanceof WidthRespecting widthRespecting) {
|
||||||
WidthRespecting widthRespecting = (WidthRespecting) drawable;
|
|
||||||
widthRespecting.setMaxWidth(cell.getWidth());
|
widthRespecting.setMaxWidth(cell.getWidth());
|
||||||
}
|
}
|
||||||
float x = tableRenderContext.getStartingPoint().x + cell.getPaddingLeft();
|
float x = tableRenderContext.getStartingPoint().x + cell.getPaddingLeft();
|
||||||
|
|
|
@ -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.
|
* @return a copy of this text flow where all leading {@link NewLine}s are removed.
|
||||||
*/
|
*/
|
||||||
public TextFlow removeLeadingEmptyLines() {
|
public TextFlow removeLeadingEmptyLines() {
|
||||||
if (text.size() == 0 || !(text.get(0) instanceof NewLine)) {
|
if (text.isEmpty() || !(text.get(0) instanceof NewLine)) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
TextFlow result = createInstance();
|
TextFlow result = createInstance();
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in a new issue