Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added repeatable header for overflow tables #166

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package org.vandeseer.easytable;

import static org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode.APPEND;

import java.awt.geom.Point2D;
import java.io.IOException;
import java.util.Queue;
import java.util.function.Supplier;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.vandeseer.easytable.TableDrawer.PageData;
import org.vandeseer.easytable.structure.Row;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

@SuperBuilder(toBuilder = true)
public class OverflowOnSamePageRepeatableHeaderTableDrawer extends TableDrawer {

@Builder.Default
private final int lanesPerPage = 2;

@Builder.Default
private final float spaceInBetween = 10f;

@Builder.Default
private int numberOfRowsToRepeat = 1;

private Float headerHeight;

// This is really meant as a private field.
// Unfortunately it might be settable by the builder and we can't make it final :/
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
@Builder.Default
private int actualTableLane = 1;

@Override
protected void drawPage(PageData pageData) {
if (pageData.firstRowOnPage != 0) {
drawHeaderForCurrentLane();
}
drawerList.forEach(drawer ->
drawWithFunction(pageData, new Point2D.Float(this.startX + calculateXOffset(), this.startY ), drawer)
);
}

private float calculateXOffset() {
final float widthOfTableLanes = (actualTableLane - 1) * table.getWidth();
final float spacing =
actualTableLane > 1
? (actualTableLane - 1) * spaceInBetween
: 0;

return widthOfTableLanes + spacing;
}

private float calculateHeightForFirstRows() {
if (headerHeight != null) {
return headerHeight;
}

float height = 0;
for (int i = 0; i < numberOfRowsToRepeat; i++) {
height += table.getRows().get(i).getHeight();
}

// Cache and return
headerHeight = height;
return height;
}

@Override
protected void determinePageToStartTable(float yOffsetOnNewPage) {
float minimumRowsToFitHeight = 0;
int minimumRowsToFit = table.getRows().size() > numberOfRowsToRepeat
? numberOfRowsToRepeat + 1
: numberOfRowsToRepeat;

for (final Row row : table.getRows().subList(0, minimumRowsToFit))
minimumRowsToFitHeight += row.getHeight();

if (startY - minimumRowsToFitHeight < endY) {
startY = yOffsetOnNewPage + calculateHeightForFirstRows();
startTableInNewPage = true;
}
}

public void drawHeaderForCurrentLane() {
float adaption = 0;
for (int k = 0; k < numberOfRowsToRepeat; k++) {
adaption += table.getRows().get(k).getHeight();
Point2D.Float startPoint = new Point2D.Float(this.startX + calculateXOffset(),
this.startY + calculateHeightForFirstRows() - adaption);
drawRow(startPoint, table.getRows().get(k), k, (drawer, drawingContext) -> {
drawer.drawBackground(drawingContext);
drawer.drawContent(drawingContext);
drawer.drawBorders(drawingContext);
});
}
}

public void draw(Supplier<PDDocument> documentSupplier, Supplier<PDPage> pageSupplier, float yOffset) throws IOException {
final PDDocument document = documentSupplier.get();

// We create one throwaway page to be able to calculate the page data upfront
float startOnNewPage = pageSupplier.get().getMediaBox().getHeight() - yOffset;
determinePageToStartTable(startOnNewPage);
final Queue<PageData> pageDataQueue = computeRowsOnPagesWithNewPageStartOf(startOnNewPage);

for (int i = 0; !pageDataQueue.isEmpty(); i++) {
final PDPage pageToDrawOn = determinePageToDraw(i, document, pageSupplier);

if ((i == 0 && startTableInNewPage) || i > 0 || document.getNumberOfPages() == 0 || actualTableLane != lanesPerPage) {
startTableInNewPage = false;
}

if (i == 0) {
tableStartPage = pageToDrawOn;
}

try (final PDPageContentStream newPageContentStream = new PDPageContentStream(document, pageToDrawOn, APPEND, compress)) {
for (int j = 1; j <= lanesPerPage && !pageDataQueue.isEmpty(); j++) {
actualTableLane = j;
if (actualTableLane > 1) {
this.startY = startOnNewPage;
}

this.contentStream(newPageContentStream)
.page(pageToDrawOn)
.drawPage(pageDataQueue.poll());
}
}
startY(pageToDrawOn.getMediaBox().getHeight() - yOffset);
}
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package org.vandeseer.integrationtest;

import static junit.framework.TestCase.assertTrue;
import static org.vandeseer.TestUtils.getActualPdfFor;
import static org.vandeseer.TestUtils.getExpectedPdfFor;

import java.awt.Color;
import java.io.IOException;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.junit.Test;
import org.vandeseer.TestUtils;
import org.vandeseer.easytable.OverflowOnSamePageRepeatableHeaderTableDrawer;
import org.vandeseer.easytable.structure.Row;
import org.vandeseer.easytable.structure.Table;
import org.vandeseer.easytable.structure.cell.TextCell;

import de.redsix.pdfcompare.CompareResult;
import de.redsix.pdfcompare.PdfComparator;

public class OverflowOnSamePageRepeatableHeaderTableDrawerTest {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class doesn't test anything yet.
Interestingly I also can't open the new PDF file.

Essentially it's missing the comparing part, like this:

final CompareResult compareResult = new PdfComparator<>(
                getExpectedPdfFor(OVERFLOW_ON_SAME_PAGE_PDF),
                getActualPdfFor(OVERFLOW_ON_SAME_PAGE_PDF)
        ).compare();

        assertTrue(compareResult.isEqual());

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if you mean something else, but I'm comparing the pdfs at line 45 :)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed! My bad, I simply overlooked it. Sorry!

private static final String OVERFLOW_ON_SAME_PAGE_REPEATABLE_HEADER_PDF = "overflowOnSamePageRepeatableHeader.pdf";

@Test
public void createTableWithOverflowOnSamePageAndRepeatableHeader() throws IOException {

try (final PDDocument document = new PDDocument()) {

OverflowOnSamePageRepeatableHeaderTableDrawer.builder()
.table(createTable())
.startX(50)
.lanesPerPage(3)
.numberOfRowsToRepeat(2)
.spaceInBetween(25)
.endY(50F) // note: if not set, table is drawn over the end of the page
.build()
.draw(() -> document, () -> new PDPage(PDRectangle.A4), 100f);

document.save(TestUtils.TARGET_FOLDER + "/" + OVERFLOW_ON_SAME_PAGE_REPEATABLE_HEADER_PDF);
}

final CompareResult compareResult = new PdfComparator<>(
getExpectedPdfFor(OVERFLOW_ON_SAME_PAGE_REPEATABLE_HEADER_PDF),
getActualPdfFor(OVERFLOW_ON_SAME_PAGE_REPEATABLE_HEADER_PDF)
).compare();
assertTrue(compareResult.isEqual());
}

private Table createTable() {
final Table.TableBuilder tableBuilder = Table.builder()
.addColumnOfWidth(60)
.addColumnOfWidth(60);

TextCell dummyHeaderCell = TextCell.builder()
.text("HD")
.backgroundColor(Color.BLUE)
.textColor(Color.WHITE)
.borderWidth(1F)
.build();

TextCell dummyHeaderCell2 = TextCell.builder()
.text("HD2")
.backgroundColor(Color.BLACK)
.textColor(Color.RED)
.borderWidth(1F)
.build();

tableBuilder.addRow(
Row.builder()
.add(dummyHeaderCell)
.add(dummyHeaderCell)
.build());

tableBuilder.addRow(
Row.builder()
.add(dummyHeaderCell2)
.add(dummyHeaderCell2)
.build());

for (int i = 0; i < 200; i++) {
tableBuilder.addRow(
Row.builder()
.add(TextCell.builder()
.text("a " + i)
.borderWidth(1F)
.build())
.add(TextCell.builder()
.text("b " + i)
.borderWidth(1F)
.build())
.build());
}

return tableBuilder.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class OverflowOnSamePageTableDrawerTest {
private static final String OVERFLOW_ON_SAME_PAGE_PDF = "overflowOnSamePage.pdf";

@Test
public void createTwoPageTableWithRepeatedHeader() throws IOException {
public void createTableWithOverflowOnSamePage() throws IOException {

try (final PDDocument document = new PDDocument()) {

Expand Down
Binary file not shown.