Sticky Headers on JavaFX Gridpane - javafx-8

I was wondering if there is a possibility to implement Sticky Headers on a GridPane in JavaFX, meaning a row of the GridPane that stays in place while you can scroll through the remaining rows of the pane.

It's possible, but you need to ensure the header nodes are the last children of the GridPane.children list. This allows you to set the translateY property of the header nodes to remain at the top of the ScrollPane wrapping the GridPane:
GridPane grid = new GridPane();
// fill grid
Color[] colors = new Color[]{Color.RED, Color.ORANGE, Color.LIGHTGREEN, Color.LIGHTBLUE};
for (int i = 1; i <= 100; i++) {
grid.add(new Rectangle(50, 50, colors[(2 * i - 2) % colors.length]), 0, i);
grid.add(new Label(Integer.toString(2 * i - 1)), 0, i);
grid.add(new Rectangle(50, 50, colors[(2 * i - 1) % colors.length]), 1, i);
grid.add(new Label(Integer.toString(2 * i)), 1, i);
}
// create & add header
Node[] headers = new Node[] {
createHeaderNode("H1"),
createHeaderNode("H2")
};
grid.addRow(0, headers);
ScrollPane scrollPane = new ScrollPane(grid);
scrollPane.setPrefHeight(400);
// keep header in position on top of the viewport
InvalidationListener headerUpdater = o -> {
final double ty = (grid.getHeight() - scrollPane.getViewportBounds().getHeight()) * scrollPane.getVvalue();
for (Node header : headers) {
header.setTranslateY(ty);
}
};
grid.heightProperty().addListener(headerUpdater);
scrollPane.viewportBoundsProperty().addListener(headerUpdater);
scrollPane.vvalueProperty().addListener(headerUpdater);
private static Label createHeaderNode(String text) {
Label label = new Label(text);
label.setMaxWidth(Double.MAX_VALUE);
label.setStyle("-fx-border-style: solid; -fx-background-color: white;");
return label;
}

Related

iTextSharp Footer Background Color

I am trying to set the background color of my footer. I can't seem to find any code that can assist in doing this. Please see my code for the OnEndPage event.
I have tried cb.SetColorFill(BaseColor.LIGHT_GRAY);, but that does not work :/
Which property or method is used to add a background color to the footer?
public override void OnEndPage(iTextSharp.text.pdf.PdfWriter writer, iTextSharp.text.Document document)
{
base.OnEndPage(writer, document);
iTextSharp.text.Font baseFontNormal = new iTextSharp.text.Font(iTextSharp.text.Font.FontFamily.HELVETICA, 12f, iTextSharp.text.Font.NORMAL, iTextSharp.text.BaseColor.BLACK);
iTextSharp.text.Font baseFontBig = new iTextSharp.text.Font(iTextSharp.text.Font.FontFamily.HELVETICA, 12f, iTextSharp.text.Font.BOLD, iTextSharp.text.BaseColor.BLACK);
var headerImagePath = System.Web.HttpContext.Current.Server.MapPath("~/Content/misc/proactive-reg-form-header.jpg");
var headerImage = Image.GetInstance(headerImagePath);
if (headerImage.Height > headerImage.Width)
{
//Maximum height is 800 pixels.
float percentage = 0.0f;
percentage = 700 / headerImage.Height;
headerImage.ScalePercent(percentage * 100);
}
else
{
//Maximum width is 600 pixels.
float percentage = 0.0f;
percentage = 572 / headerImage.Width;
headerImage.ScalePercent(percentage * 100);
}
//Create PdfTable object
PdfPTable pdfTab = new PdfPTable(1);
//We will have to create separate cells to include image logo and 2 separate strings
//Row 1
//PdfPCell pdfCell1 = new PdfPCell();
PdfPCell pdfCell2 = new PdfPCell(headerImage);
//PdfPCell pdfCell3 = new PdfPCell();
String text = "Page " + writer.PageNumber + " of ";
//Add paging to footer
{
cb.BeginText();
cb.SetFontAndSize(bf, 10);
cb.SetTextMatrix(document.PageSize.GetRight(100), document.PageSize.GetBottom(30));
cb.ShowText(text);
cb.EndText();
float len = bf.GetWidthPoint(text, 10);
cb.AddTemplate(footerTemplate, document.PageSize.GetRight(100) + len, document.PageSize.GetBottom(30));
//add image to footer
writer.DirectContent.AddImage(footerImage);
}
pdfCell2.HorizontalAlignment = Element.ALIGN_CENTER;
pdfCell2.VerticalAlignment = Element.ALIGN_BOTTOM;
pdfCell2.Border = 0;
pdfTab.AddCell(pdfCell2);
pdfTab.TotalWidth = 100f;
pdfTab.WidthPercentage = 100f;
//call WriteSelectedRows of PdfTable. This writes rows from PdfWriter in PdfTable
//first param is start row. -1 indicates there is no end row and all the rows to be included to write
//Third and fourth param is x and y position to start writing
pdfTab.WriteSelectedRows(0, -1, 10, document.PageSize.Height - 10, writer.DirectContent);
//set pdfContent value
if (document.PageNumber != 1)
{
//Move the pointer and draw line to separate header section from rest of page
cb.MoveTo(10, document.PageSize.Height - 60);
cb.LineTo((document.PageSize.Width - 10), document.PageSize.Height - 60);
cb.Stroke();
}
}
Got it!
So I amended the following code:
//Add paging to footer
{
cb.BeginText();
cb.SetFontAndSize(bf, 10);
cb.SetTextMatrix(document.PageSize.GetRight(100), document.PageSize.GetBottom(30));
cb.ShowText(text);
cb.EndText();
float len = bf.GetWidthPoint(text, 10);
cb.AddTemplate(footerTemplate, document.PageSize.GetRight(100) + len, document.PageSize.GetBottom(30));
//add image to footer
writer.DirectContent.AddImage(footerImage);
}
with
//Add paging to footer
{
cb.BeginText();
cb.SetFontAndSize(bf, 10);
cb.SetTextMatrix(document.PageSize.GetRight(100), document.PageSize.GetBottom(30));
cb.ShowText(text);
cb.EndText();
float len = bf.GetWidthPoint(text, 10);
cb.AddTemplate(footerTemplate, document.PageSize.GetRight(100) + len, document.PageSize.GetBottom(30));
//this part adds the background color
cb.SetColorFill(BaseColor.LIGHT_GRAY);
cb.Rectangle(0, 0, document.PageSize.Width, 50);
cb.FillStroke();
//add image to footer
writer.DirectContent.AddImage(footerImage);
}

How to specify a row background in table with iText5

When creating a PDF table with iText5, it is possible to create a table background by implementing a PdfPTableEvent and a cell background by implementing a PdfPCellEvent.
But what about a row background? How can I create that?
The reason is because I want to create a calendar table like in the image below:
Actually, you have included the answer in your question: you have to use a table event. Take a look at the RowBackground example. It contains a table event RowBackgroundEvent that allows you to create a table event to draw the background of a single row.
public class RowBackgroundEvent implements PdfPTableEvent {
// the row number of the row that needs a background
protected int row;
// creates a background event for a specific row
public RowBackgroundEvent(int row) {
this.row = row;
}
/**
* Draws the background of a row.
*/
#Override
public void tableLayout(PdfPTable table, float[][] widths, float[] heights,
int headerRows, int rowStart, PdfContentByte[] canvases) {
float llx = widths[row][0];
float lly = heights[row];
float urx = widths[row][widths[row].length - 1];
float ury = heights[row - 1];
float h = ury - lly;
PdfContentByte canvas = canvases[PdfPTable.BASECANVAS];
canvas.saveState();
canvas.arc(llx - h / 2, lly, llx + h / 2, ury, 90, 180);
canvas.lineTo(urx, lly);
canvas.arc(urx - h / 2, lly, urx + h / 2, ury, 270, 180);
canvas.lineTo(llx, ury);
canvas.setColorFill(BaseColor.LIGHT_GRAY);
canvas.fill();
canvas.restoreState();
}
}
This is how this event is used:
public void createPdf(String filename) throws SQLException, DocumentException, IOException {
// step 1
Document document = new Document(PageSize.A4.rotate());
// step 2
PdfWriter.getInstance(document, new FileOutputStream(filename));
// step 3
document.open();
// step 4
PdfPTableEvent event = new RowBackgroundEvent(3);
PdfPTable table = new PdfPTable(7);
table.setTableEvent(event);
table.getDefaultCell().setBorder(Rectangle.NO_BORDER);
for (int i = 0; i < 10; i++) {
for (int j = 1; j < 8; j++) {
table.addCell(String.valueOf(j));
}
}
document.add(table);
// step 5
document.close();
}
As you can see, we want a background for the third row. The result looks like this:
You can tweak the llx, lly, urx, and ury value if you want to adapt the size of the background bar.
If you can draw the background of a single row, you can extend the code to draw the background of more than one row.

JavaFX resize rotated object to GridPane cell

I have a problem with the orientation of a node (GridPane) after rotating it with setRotate() in JavaFX. When i rotate the node and put it in a cell of the GridPane, I would like the rotated node to fit inside the cell and also resize with the cell. I added some sample code to show you what I would like the result to be.
public class MonopolyApp extends Application {
#Override
public void start(Stage primaryStage) {
//Construction of the grid
GridPane square = new GridPane();
square.setGridLinesVisible(true);
final int heightPercent = 14;
final int widthPercent = 8;
RowConstraints rowsEdge = new RowConstraints();
rowsEdge.setPercentHeight(heightPercent);
RowConstraints rowsMid = new RowConstraints();
rowsMid.setPercentHeight(widthPercent);
ColumnConstraints colEdge = new ColumnConstraints();
colEdge.setPercentWidth(heightPercent);
ColumnConstraints colMid = new ColumnConstraints();
colMid.setPercentWidth(widthPercent);
square.getColumnConstraints().addAll(colEdge, colMid,
colMid, colMid, colMid, colMid, colMid, colMid, colMid, colMid, colEdge);
square.getRowConstraints().addAll(rowsEdge, rowsMid,
rowsMid,rowsMid,rowsMid,rowsMid,rowsMid,rowsMid, rowsMid,rowsMid, rowsEdge);
GridPane wrongSuare = makeMonopolySquare();
square.add(wrongSuare, 0, 4);
wrongSuare.setRotate(90);
GridPane rightSquare = makeMonopolySquare();
square.add(rightSquare, 1, 10);
Scene s = new Scene(square);
primaryStage.setHeight(500);
primaryStage.setWidth(500);
primaryStage.setScene(s);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
private GridPane makeMonopolySquare(){
GridPane monopolySquare = new GridPane();
RowConstraints top = new RowConstraints();
top.setPercentHeight(20);
RowConstraints bottom = new RowConstraints();
bottom.setPercentHeight(80);
ColumnConstraints c = new ColumnConstraints();
c.setPercentWidth(100);
monopolySquare.getRowConstraints().addAll(top,bottom);
monopolySquare.getColumnConstraints().addAll(c);
bottom.setValignment(VPos.CENTER);
monopolySquare.setGridLinesVisible(true);
Label name = new Label("name");
Pane colorPane = new Pane();
colorPane.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
colorPane.setBackground(new Background( new BackgroundFill(Color.BLUE, null, null)));
GridPane.setMargin(colorPane, new Insets(1));
monopolySquare.add(colorPane,0,0);
monopolySquare.add(name, 0, 1);
return monopolySquare;
}
}
If you run the code you will see that the GridPane at the bottom of the stage perfectly fits it's cell. But the rotated GridPane does not. I will also add a picture to show you what my problem is:
Does anyone know how to solve this? I know that I could put it in a group, but the problem with putting it in a group is that the group would not resize to the cell of the GridPane.
Well, that is a tough one. No optimal solution comes to mind, but if I was in your position, I would try to stay within the confinements of the layout (not use low-level operations like rotation).
For example, build a differently layout cell for each row/column:
private static Node makeMonopolySquare( final Layout layout )
{
final GridPane monopolySquare = new GridPane();
monopolySquare.setGridLinesVisible( true );
final Label label = new Label( "name" );
final StackPane name = new StackPane( label );
final Pane colorPane = new Pane();
GridPane.setMargin( colorPane, new Insets( 1 ) );
colorPane.setMaxSize( Double.MAX_VALUE, Double.MAX_VALUE );
colorPane.setBackground( new Background( new BackgroundFill( Color.BLUE, null, null ) ) );
final RowConstraints top = new RowConstraints();
final RowConstraints bottom = new RowConstraints();
// bottom.setValignment( VPos.CENTER );
// top.setValignment( VPos.CENTER );
final ColumnConstraints c = new ColumnConstraints();
c.setPercentWidth( 100 );
final ColumnConstraints right = new ColumnConstraints();
// right.setHalignment( HPos.CENTER );
final ColumnConstraints left = new ColumnConstraints();
// left.setHalignment( HPos.CENTER );
final RowConstraints r = new RowConstraints();
r.setPercentHeight( 100 );
switch ( layout )
{
case BOTTOM:
top.setPercentHeight( 20 );
bottom.setPercentHeight( 80 );
monopolySquare.getRowConstraints().addAll( top, bottom );
monopolySquare.getColumnConstraints().addAll( c );
monopolySquare.add( colorPane, 0, 0 );
monopolySquare.add( name, 0, 1 );
break;
case LEFT:
right.setPercentWidth( 20 );
left.setPercentWidth( 80 );
monopolySquare.getColumnConstraints().addAll( left, right );
monopolySquare.getRowConstraints().addAll( r );
monopolySquare.add( name, 0, 0 );
monopolySquare.add( colorPane, 1, 0 );
break;
case RIGHT:
right.setPercentWidth( 80 );
left.setPercentWidth( 20 );
monopolySquare.getColumnConstraints().addAll( left, right );
monopolySquare.getRowConstraints().addAll( r );
monopolySquare.add( colorPane, 0, 0 );
monopolySquare.add( name, 1, 0 );
break;
case TOP:
top.setPercentHeight( 80 );
bottom.setPercentHeight( 20 );
monopolySquare.getRowConstraints().addAll( top, bottom );
monopolySquare.getColumnConstraints().addAll( c );
bottom.setValignment( VPos.CENTER );
monopolySquare.add( colorPane, 0, 1 );
monopolySquare.add( name, 0, 0 );
break;
}
This however, will not rotate the label (which, I would argue is better anyways for a digital monopoly board ;) ). As soon as you rotate the label, you get the same problem you had with the cell, only smaller.
The full code example.

itextsharp change margins no document.SetPageSize

I added the function below to change the margins for the page
at every page change.
I found the forum a method that sets the size of the page:
document.SetPageSize (New Rectangle (36.0F, 36.0F, 52.0F, PageFooter.TotalHeight))
But I do not want to change the size of the page, but those margins.
thanks
public override void OnEndPage(PdfWriter writer, Document document)
{
try
{
DataSet dsReport = new DataSet();
foreach (DataSet obj in report.arrayDs)
{
dsReport = obj;
break;
}
Single topMargin = 0;
if (document.PageNumber != 1)
{
if (report.repeatHead) //ripete l'intestazione del report su tutte le pagine di stampa
{
repeatHead(writer, document);
topMargin = 60;
}
else
{
if (document.PageNumber == 2) //ripete l'intestazione del report solo sulla second pagina dopo la copertina
{
repeatHead(writer, document);
topMargin = 60;
}
else
{
topMargin = Convert.ToSingle(dsReport.Tables["REPORT_STYLE"].Rows[0]["topMargin"]) * 10;
}
}
document.SetMargins(Convert.ToSingle(dsReport.Tables["REPORT_STYLE"].Rows[0]["leftMargin"]) * 10,
Convert.ToSingle(dsReport.Tables["REPORT_STYLE"].Rows[0]["rightMargin"]) * 10,
topMargin,
Convert.ToSingle(dsReport.Tables["REPORT_STYLE"].Rows[0]["bottomMargin"]) * 10);
}
}
catch
{ throw; }
}
Based on your clarification in the comments, you want the top margin of the first page to be 60 and the top margin of the second page to be 0.
This is shown in the following screen shot:
The Java code to achieve this looks like this:
public void createPdf(String dest) throws IOException, DocumentException {
float left = 30;
float right = 30;
float top = 60;
float bottom = 0;
Document document = new Document(PageSize.A4, left, right, top, bottom);
PdfWriter.getInstance(document, new FileOutputStream(dest));
document.open();
document.setMargins(left, right, 0, bottom);
for (int i = 0; i < 60; i++) {
document.add(new Paragraph("This is a test"));
}
document.close();
}
If you want to port this to C#, you need to change some lower cases into upper cases. You already knew most of the methods, for instance: I see document.SetMargins(...) in your page event.

How can you eliminate white-space in multiple columns using iTextSharp?

I'd like to add a Paragraph of text to pages in 2 columns. I understand that MultiColumnText has been eliminated. I know I can create column 1, write to it, and if there is more text create column 2 and write to it. If there is still more text, go to the next page and repeat.
However I always end up with either:
a long chunk of text orphaned in the left column.
a full left column and partially used right column.
How can I format my content in 2 columns while reducing white space, such as compressing the columns so I end with 2 full columns of equal length?
Thanks!
You should add your column twice, once in simulation mode and once for real.
I have adapted the ColumnTextParagraphs example to show what is meant by simulation mode. Take a look at the ColumnTextParagraphs2 example:
We add the column in simulation mode to obtain the total height needed for the column. This is done in the following method:
public float getNecessaryHeight(ColumnText ct) throws DocumentException {
ct.setSimpleColumn(new Rectangle(0, 0, COLUMN_WIDTH, -500000));
ct.go(true);
return -ct.getYLine();
}
We use this height when we add the left and right column:
Rectangle left;
float top = COLUMNS[0].getTop();
float middle = (COLUMNS[0].getLeft() + COLUMNS[1].getRight()) / 2;
float columnheight;
int status = ColumnText.START_COLUMN;
while (ColumnText.hasMoreText(status)) {
if (checkHeight(height)) {
columnheight = COLUMNS[0].getHeight();
left = COLUMNS[0];
}
else {
columnheight = (height / 2) + ERROR_MARGIN;
left = new Rectangle(
COLUMNS[0].getLeft(),
COLUMNS[0].getTop() - columnheight,
COLUMNS[0].getRight(),
COLUMNS[0].getTop()
);
}
// left half
ct.setSimpleColumn(left);
ct.go();
height -= COLUMNS[0].getTop() - ct.getYLine();
// separator
canvas.moveTo(middle, top - columnheight);
canvas.lineTo(middle, top);
canvas.stroke();
// right half
ct.setSimpleColumn(COLUMNS[1]);
status = ct.go();
height -= COLUMNS[1].getTop() - ct.getYLine();
// new page
document.newPage();
}
This is how we check the height:
public boolean checkHeight(float height) {
height -= COLUMNS[0].getHeight() + COLUMNS[1].getHeight() + ERROR_MARGIN;
return height > 0;
}
As you can see, we add the full columns as long as the height of both columns is smaller than the remaining height. When columns are added, we adjust the remaining height. As soon as the height is lower than the height of to columns, we adapt the height of the first column.
Note that we work with an ERROR_MARGIN because dividing by two often leads to a situation where the second column has one line more than the first column. It is better when it's the other way around.
This is the full example at your request:
/**
* Example written by Bruno Lowagie in answer to:
* http://stackoverflow.com/questions/29378407/how-can-you-eliminate-white-space-in-multiple-columns-using-itextsharp
*/
package sandbox.objects;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfWriter;
public class ColumnTextParagraphs2 {
public static final String DEST = "results/objects/column_paragraphs2.pdf";
public static final String TEXT = "This is some long paragraph that will be added over and over again to prove a point.";
public static final float COLUMN_WIDTH = 254;
public static final float ERROR_MARGIN = 16;
public static final Rectangle[] COLUMNS = {
new Rectangle(36, 36, 36 + COLUMN_WIDTH, 806),
new Rectangle(305, 36, 305 + COLUMN_WIDTH, 806)
};
public static void main(String[] args) throws IOException, DocumentException {
File file = new File(DEST);
file.getParentFile().mkdirs();
new ColumnTextParagraphs2().createPdf(DEST);
}
public void createPdf(String dest) throws IOException, DocumentException {
// step 1
Document document = new Document();
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
// step 3
document.open();
// step 4
PdfContentByte canvas = writer.getDirectContent();
ColumnText ct = new ColumnText(canvas);
addContent(ct);
float height = getNecessaryHeight(ct);
addContent(ct);
Rectangle left;
float top = COLUMNS[0].getTop();
float middle = (COLUMNS[0].getLeft() + COLUMNS[1].getRight()) / 2;
float columnheight;
int status = ColumnText.START_COLUMN;
while (ColumnText.hasMoreText(status)) {
if (checkHeight(height)) {
columnheight = COLUMNS[0].getHeight();
left = COLUMNS[0];
}
else {
columnheight = (height / 2) + ERROR_MARGIN;
left = new Rectangle(
COLUMNS[0].getLeft(),
COLUMNS[0].getTop() - columnheight,
COLUMNS[0].getRight(),
COLUMNS[0].getTop()
);
}
// left half
ct.setSimpleColumn(left);
ct.go();
height -= COLUMNS[0].getTop() - ct.getYLine();
// separator
canvas.moveTo(middle, top - columnheight);
canvas.lineTo(middle, top);
canvas.stroke();
// right half
ct.setSimpleColumn(COLUMNS[1]);
status = ct.go();
height -= COLUMNS[1].getTop() - ct.getYLine();
// new page
document.newPage();
}
// step 5
document.close();
}
public void addContent(ColumnText ct) {
for (int i = 0; i < 35; i++) {
ct.addElement(new Paragraph(String.format("Paragraph %s: %s", i, TEXT)));
}
}
public float getNecessaryHeight(ColumnText ct) throws DocumentException {
ct.setSimpleColumn(new Rectangle(0, 0, COLUMN_WIDTH, -500000));
ct.go(true);
return -ct.getYLine();
}
public boolean checkHeight(float height) {
height -= COLUMNS[0].getHeight() + COLUMNS[1].getHeight() + ERROR_MARGIN;
return height > 0;
}
}