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

Cells spanning multiple pages do not render properly #107

Open
Thejipppp opened this issue Nov 30, 2020 · 3 comments
Open

Cells spanning multiple pages do not render properly #107

Thejipppp opened this issue Nov 30, 2020 · 3 comments

Comments

@Thejipppp
Copy link

Hi

This is basically part two of the problem described in #106, and I think it's double with #96, but I wanted to be sure by adding a little image for a minimal example. (Another time, thanks for fixing #106 VERY quickly, you're amazing)

It is immediately clear that the first gray cell should span over the second page and the borders shouldn't disappear in the image below.

What I wanted to do is split those cells by checking if the height is higher than a single page.
The problem is that you can't set the rowspan of a cell that is builded (which is reasonable).
Basically I have a whole machinery to make List< Row >s but to be able to adapt the rowspan, I would need to convert the whole machinery to work with List<List< AbstractCellBuilder >>s which is a bit clumsy I think. But I guess there is no different option? 😋

Kind regards
Jesper

image

Table table = Table.builder().addColumnsOfWidth(200, 200).borderColor(BLACK).fontSize(8)
				.font(HELVETICA_BOLD_OBLIQUE)
				.addRow(Row.builder().backgroundColor(GRAY).textColor(BLACK).horizontalAlignment(CENTER)
						.add(TextCell.builder().borderWidth(1).text("Markup").build())
						.add(TextCell.builder().borderWidth(1).text("No Markup").build()).build())
				.addRow(Row.builder().backgroundColor(LIGHT_GRAY).add(ParagraphCell.builder().borderWidth(1).padding(8)
						.lineSpacing(1.2f).rowSpan(34)
						.paragraph(Paragraph.builder().append(Markup.builder()
								.markup("Beneath is an example of __underscored__ and __{0.25:}striked through__ \n"
										+ " __ __{0.25:}text, both at the same time__ __")
								.font(Markup.MarkupSupportedFont.TIMES).build()).build())
						.build())
						.add(ParagraphCell.builder().borderWidth(1).padding(8).lineSpacing(1.2f).paragraph(Paragraph
								.builder()
								.append(StyledText
										.builder().text("This is some text in one font.").font(HELVETICA).build())
								.appendNewLine()
								.append(StyledText.builder().text(
										"But this text that introduces a link that follows is different. Here comes the link: ")
										.font(COURIER_BOLD).fontSize(6f).build())
								.append(Hyperlink.builder().text("github!").url("http://www.github.com")
										.font(COURIER_BOLD).fontSize(6f).color(WHITE).build())
								.appendNewLine(6f)
								.append(StyledText.builder().text(
										"There was the link. And now we are using the default font from the cell.")
										.build())
								.build()).build())
						.build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
				.addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build()).build();
		return table;
@vandeseer
Copy link
Owner

Hi Jesper,

using row span and extremely large cells together doesn't really work properly. Reason being that easytable was never designed for this use case: Its main goal was to allow for simple table creation (like the ones in the examples) where one could use some two or three cells bundled together by column and/or row spanning.

To be honest introducing row span was way harder than I thought (think of the edge cases like page wrapping, combination with column spanning or even both). When trying to use easytable as a "layout engine" – it gets nasty 😄
It was simply not made for the purpose.

Also it's not entirely clear how it should behave in the case where you have row spanning and page wrapping: Should it split the text automatically and continue on the next page? But if so, where and how should it split (you need to mind hyphens, word breaking etc.)? This is pretty tricky stuff and a bit too much for a voluntarily maintained open source project I don't even use myself that much anymore 😉

Well, still I would like to help you solving your issue. My approach would be to create some "helper cells" like so:

private Table createParagraphTable() {

    Table.TableBuilder tableBuilder = Table.builder().addColumnsOfWidth(200, 200).borderColor(BLACK).fontSize(8)
            .font(HELVETICA_BOLD_OBLIQUE)
            .addRow(Row.builder().backgroundColor(GRAY).textColor(BLACK).horizontalAlignment(CENTER)
                    .add(TextCell.builder().borderWidth(1).text("Markup").build())
                    .add(TextCell.builder().borderWidth(1).text("No Markup").build()).build())
            .addRow(Row.builder().backgroundColor(LIGHT_GRAY).add(ParagraphCell.builder().borderWidth(1).padding(8)
                    .lineSpacing(1.2f).rowSpan(4)
                    .paragraph(Paragraph.builder().append(Markup.builder()
                            .markup("Beneath is an example of __underscored__ and __{0.25:}striked through__ \n"
                                    + " __ __{0.25:}text, both at the same time__ __")
                            .font(Markup.MarkupSupportedFont.TIMES).build()).build())
                    .build())
                    .add(ParagraphCell.builder().borderWidth(1).padding(8).lineSpacing(1.2f).paragraph(Paragraph
                            .builder()
                            .append(StyledText
                                    .builder().text("This is some text in one font.").font(HELVETICA).build())
                            .appendNewLine()
                            .append(StyledText.builder().text(
                                    "But this text that introduces a link that follows is different. Here comes the link: ")
                                    .font(COURIER_BOLD).fontSize(6f).build())
                            .append(Hyperlink.builder().text("github!").url("http://www.github.com")
                                    .font(COURIER_BOLD).fontSize(6f).color(WHITE).build())
                            .appendNewLine(6f)
                            .append(StyledText.builder().text(
                                    "There was the link. And now we are using the default font from the cell.")
                                    .build())
                            .build()).build())
                    .build())
            .addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
            .addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
            .addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build());

    for (int i = 0; i <10; i++) {
        tableBuilder
                .addRow(
                        Row.builder()
                                .add(TextCell.builder()
                                        .backgroundColor(LIGHT_GRAY)
                                        .borderWidthLeft(1)
                                        .borderWidthRight(1)
                                        .lineSpacing(1.2f)
                                        .rowSpan(4)
                                        .text("Helper cell")
                                        .build()
                                )
                                .add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build())
                        .build()
                )
                .addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
                .addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build())
                .addRow(Row.builder().add(TextCell.builder().text("AAA\nAAAAAAA\nAAAAAA").build()).build());
        i++;
    }

    return tableBuilder.build();
}

This way you could feed some small pieces into the right side while still having the visual appearance of a cell that spans over multiple rows (even if it does not in practice). That'd be my approach but I don't know your exact requirements neither the context around it (like how much influence you have on the data that is fed in e.g.).

Let me know if this helps,
Stefan

PS: Of course I am always open for pull requests, donations or beer in order to improve easytable 😉

@Thejipppp
Copy link
Author

Hi Stefan

I completely understand your point. You have a nice library and I am asking too much of it. (I think that happens for every open source project after a while) 😋

In my case, I use an image at the top of the first row, so I'll do something like "If it can be drawn, draw it, else put it on the next page". It will be a struggle to be sure, but anyway, without easytable, it would be completely impossible to do it in a clean way 😄

Using helpercells won't probably do what I want, but thanks for the suggestion, I also considered it😇

When I find some time, I'll see which one of your three suggestions would be best 😋

Thanks for the quick replies and keep up the amazing work!
Jesper

@Thejipppp
Copy link
Author

Hi

I looked through the code and I can see why it's a problem 😄

I think the solution would be to

  1. copy the Table when setting it in the TableDrawer
  2. check every cell that spans multiple rows
  3. try to split it into parts by making an empty copy of it.
  4. checking the height of all pages for every action

But the longer I look at it, the more complicated it gets.

  • Deciding how to split would almost become an NP problem, however with the size of most tables, shouldn't be too bad in time, but damn it would be difficult to make that working in a bulletproof way 😢
  • The word breaking could be done with an extra setting I guess, but would also be an extra problem to solve
  • The problem I struggle with A LOT for my table is that you can't reset the rowspan on a cell once it's built, and I don't think there is a way to get the cell builders from the table at this moment.

So I agree, it would be too much work to fix that, and I'm really happy for your quick fix on the paragraph cells, so I can make a big workaround 😋

If someone else would have this problem, what I did was rewrote my hole machinery

  • Every Cell is converted to a CellBuilder (really not fun to specify a recursive type in Scala 😉 )
  • Every Row is converted to a List< CellBuilder >
  • Every logical row (with one cell spanning the whole height) is a List < List < CellBuilder > >
  • At the end, I calculate everything by building the tables multiple time, checking if the height is okay and splitting into multiple cells (at this point I have a List < List < List < CellBuilder > > > (page, logical row, real row)
  • And only then, the real table is drawn 😄

Maybe if I am really bored sometime, I'll try to implement it here, generally, but it's a bit too much at the moment...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants