Skip to content
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
46 changes: 45 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,51 @@ For other build systems, check the [Maven Central Repository](http://search.mave

# Tutorial

A tutorial is being created and will be accessible at https://github.com/dhorions/boxable/wiki.
Comprehensive tutorials are now available in the test package! Each tutorial demonstrates specific features with well-documented, runnable examples:

## Running the Tutorials

You can run all tutorials at once using the Tutorial Runner:

```bash
mvn test -Dtest=TutorialRunner
```

Or run individual tutorials:

```bash
mvn test -Dtest=Tutorial01_BasicTable
```

All generated PDFs will be saved in the `target/tutorials/` directory.

## Available Tutorials

1. **Tutorial01_BasicTable** - Simple table creation, headers, and cell styling
2. **Tutorial02_HtmlFormatting** - All supported HTML tags including `<sup>` and `<sub>`
3. **Tutorial03_ColorsAndTransparency** - Cell colors, text colors, and alpha channel
4. **Tutorial04_Alignment** - Horizontal and vertical text alignment
5. **Tutorial05_Images** - Images in cells with scaling and padding control
6. **Tutorial06_BordersAndStyling** - Border styles, colors, widths, and selective borders
7. **Tutorial07_HeaderRows** - Single and multiple header rows with page repetition
8. **Tutorial08_DataImport** - Import data from CSV and Java Lists
9. **Tutorial09_MultiPageTables** - Large tables spanning multiple pages
10. **Tutorial10_NestedTables** - Tables within cells using HTML `<table>` tags
11. **Tutorial11_FixedHeightRows** - Fixed-height rows with auto-fit text
12. **Tutorial12_AdvancedFeatures** - Rotated text, line spacing, and colspan

## Tutorial Source Code

All tutorial source code is available in `src/test/java/be/quodlibet/boxable/tutorial/`.

Each tutorial is self-contained and includes:
- Detailed JavaDoc comments explaining the demonstrated features
- Well-structured, readable code
- Generated PDF output for visual reference

## Wiki Documentation

Detailed documentation is being created and will be accessible at https://github.com/dhorions/boxable/wiki.
If you want to help, please let us know [here](https://github.com/dhorions/boxable/issues/41).

# Usage examples
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/be/quodlibet/boxable/Row.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,23 @@ public Cell<T> createImageCell(float width, Image img, HorizontalAlignment align
return cell;
}

/**
* <p>
* Creates a table cell with provided width and table data, using the table's
* document, page, yStart, and margins.
* </p>
*
* @param width
* Table width

Copilot AI Feb 13, 2026

Copy link

Choose a reason for hiding this comment

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

The JavaDoc parameter description is incorrect. The parameter is named 'width' and represents the cell's width, not the table's width. This should be changed to 'Cell width' or 'Cell's width' to match the documentation pattern used in the existing createTableCell method at line 128.

Suggested change
* Table width
* Cell width

Copilot uses AI. Check for mistakes.
* @param tableData
* Table's data (HTML table tags)
* @return {@link TableCell} with provided width and table data
*/
public TableCell<T> createTableCell(float width, String tableData) {
return createTableCell(width, tableData, table.getDocument(), table.getCurrentPage(), table.yStart,
table.pageTopMargin, table.pageBottomMargin);
}

/**
* <p>
* Creates a table cell with provided width and table data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

import java.io.File;
import java.io.IOException;
import be.quodlibet.boxable.page.PageProvider;
import java.util.ArrayList;
import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
Expand Down
137 changes: 137 additions & 0 deletions src/test/java/be/quodlibet/boxable/tutorial/Tutorial01_BasicTable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package be.quodlibet.boxable.tutorial;

import be.quodlibet.boxable.BaseTable;
import be.quodlibet.boxable.Cell;
import be.quodlibet.boxable.HorizontalAlignment;
import be.quodlibet.boxable.Row;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
import org.junit.Test;

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

/**
* Tutorial 01: Basic Table Creation
*
* This tutorial demonstrates:
* - Creating a simple PDF table
* - Adding header rows with styling
* - Creating data rows
* - Basic cell styling (colors, fonts, alignment)
* - Saving the PDF document
*/
public class Tutorial01_BasicTable {

@Test
public void createBasicTable() throws IOException {
// Step 1: Initialize PDF Document and Page
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);

// Step 2: Define table dimensions and margins
float margin = 50;
float yStart = page.getMediaBox().getHeight() - margin;
float tableWidth = page.getMediaBox().getWidth() - (2 * margin);
float yStartNewPage = page.getMediaBox().getHeight() - margin;
float bottomMargin = 70;

// Step 3: Create the table
BaseTable table = new BaseTable(yStart, yStartNewPage, bottomMargin,
tableWidth, margin, document, page,
true, true);

// Step 4: Create a title row
Row<PDPage> titleRow = table.createRow(30f);
Cell<PDPage> titleCell = titleRow.createCell(100, "Tutorial 01: Basic Table Creation");
titleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD));
titleCell.setFontSize(16f);
titleCell.setFillColor(new Color(41, 128, 185)); // Blue background
titleCell.setTextColor(Color.WHITE);
titleCell.setAlign(HorizontalAlignment.CENTER);

// Step 5: Create a description row
Row<PDPage> descRow = table.createRow(20f);
Cell<PDPage> descCell = descRow.createCell(100,
"This table demonstrates basic table creation, headers, and cell styling.");
descCell.setFillColor(new Color(236, 240, 241)); // Light gray background
descCell.setAlign(HorizontalAlignment.CENTER);

// Step 6: Create header row
Row<PDPage> headerRow = table.createRow(20f);
headerRow.createCell(25, "Product");
headerRow.createCell(25, "Category");
headerRow.createCell(25, "Price");
headerRow.createCell(25, "Stock");

// Style header cells
Color headerColor = new Color(52, 152, 219); // Blue
Color headerTextColor = Color.WHITE;
for (Cell<PDPage> cell : headerRow.getCells()) {
cell.setFillColor(headerColor);
cell.setTextColor(headerTextColor);
cell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD));
cell.setAlign(HorizontalAlignment.CENTER);
}

// Step 7: Add data rows with alternating colors
String[][] data = {
{"Laptop", "Electronics", "$999.99", "15"},
{"Mouse", "Accessories", "$19.99", "120"},
{"Keyboard", "Accessories", "$49.99", "85"},
{"Monitor", "Electronics", "$299.99", "42"},
{"USB Cable", "Accessories", "$9.99", "200"},
{"Headphones", "Audio", "$79.99", "67"},
{"Webcam", "Electronics", "$59.99", "33"},
{"Desk Lamp", "Furniture", "$34.99", "58"}
};

Color lightGray = new Color(236, 240, 241);
Color white = Color.WHITE;

for (int i = 0; i < data.length; i++) {
Row<PDPage> dataRow = table.createRow(15f);

// Alternate row colors for better readability
Color rowColor = (i % 2 == 0) ? white : lightGray;

dataRow.createCell(25, data[i][0]);
dataRow.createCell(25, data[i][1]);
Cell<PDPage> cell3 = dataRow.createCell(25, data[i][2]);
Cell<PDPage> cell4 = dataRow.createCell(25, data[i][3]);

// Apply styling
for (Cell<PDPage> cell : dataRow.getCells()) {
cell.setFillColor(rowColor);
}

// Right-align numeric columns
cell3.setAlign(HorizontalAlignment.RIGHT);
cell4.setAlign(HorizontalAlignment.RIGHT);
}

// Step 8: Add a summary row
Row<PDPage> summaryRow = table.createRow(20f);
Cell<PDPage> summaryCell = summaryRow.createCell(100,
"Total Items: " + data.length);
summaryCell.setFillColor(new Color(46, 204, 113)); // Green background
summaryCell.setTextColor(Color.WHITE);
summaryCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD));
summaryCell.setAlign(HorizontalAlignment.CENTER);

// Step 9: Draw the table
table.draw();

// Step 10: Save the document
File file = new File("target/tutorials/Tutorial01_BasicTable.pdf");
System.out.println("Tutorial 01 PDF saved at: " + file.getAbsolutePath());
file.getParentFile().mkdirs();
document.save(file);
document.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package be.quodlibet.boxable.tutorial;

import be.quodlibet.boxable.BaseTable;
import be.quodlibet.boxable.Cell;
import be.quodlibet.boxable.HorizontalAlignment;
import be.quodlibet.boxable.Row;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
import org.junit.Test;

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

/**
* Tutorial 02: HTML Formatting
*
* This tutorial demonstrates all supported HTML tags in Boxable:
* - Text formatting: <b>, <i>, <u>, <s>
* - Headings: <h1> through <h6>
* - Superscript and subscript: <sup>, <sub>
* - Line breaks: <br>
* - Paragraphs: <p>
* - Lists: <ul>, <ol>, <li>
* - Nested tags
*/
public class Tutorial02_HtmlFormatting {

@Test
public void demonstrateHtmlFormatting() throws IOException {
// Initialize PDF Document
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);

// Define table dimensions
float margin = 50;
float yStart = page.getMediaBox().getHeight() - margin;
float tableWidth = page.getMediaBox().getWidth() - (2 * margin);
float yStartNewPage = page.getMediaBox().getHeight() - margin;
float bottomMargin = 70;

// Create the table
BaseTable table = new BaseTable(yStart, yStartNewPage, bottomMargin,
tableWidth, margin, document, page,
true, true);

// Title Row
Row<PDPage> titleRow = table.createRow(30f);
Cell<PDPage> titleCell = titleRow.createCell(100, "Tutorial 02: HTML Formatting");
titleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD));
titleCell.setFontSize(16f);
titleCell.setFillColor(new Color(231, 76, 60)); // Red background
titleCell.setTextColor(Color.WHITE);
titleCell.setAlign(HorizontalAlignment.CENTER);

// Description
Row<PDPage> descRow = table.createRow(20f);
Cell<PDPage> descCell = descRow.createCell(100,
"Boxable supports various HTML tags for rich text formatting.");
descCell.setFillColor(new Color(236, 240, 241));
descCell.setAlign(HorizontalAlignment.CENTER);

// Header Row
Row<PDPage> headerRow = table.createRow(15f);
Cell<PDPage> htmlHeader = headerRow.createCell(40, "HTML Code");
Cell<PDPage> descHeader = headerRow.createCell(60, "Description");
htmlHeader.setFillColor(new Color(52, 73, 94));
descHeader.setFillColor(new Color(52, 73, 94));
htmlHeader.setTextColor(Color.WHITE);
descHeader.setTextColor(Color.WHITE);

// Text Formatting Examples
addExampleRow(table, "<b>Bold text</b>", "Makes text bold");
addExampleRow(table, "<i>Italic text</i>", "Makes text italic");
addExampleRow(table, "<u>Underlined text</u>", "Underlines text");
addExampleRow(table, "<s>Strikethrough text</s>", "Strikes through text");

// Nested formatting
addExampleRow(table, "<b><i>Bold and Italic</i></b>", "Combines multiple styles");
addExampleRow(table, "<u><b>Bold Underlined</b></u>", "Underlined bold text");

// Headings
addSectionHeader(table, "Heading Tags");
addExampleRow(table, "<h1>Heading 1</h1>", "Largest heading");
addExampleRow(table, "<h2>Heading 2</h2>", "Second-level heading");
addExampleRow(table, "<h3>Heading 3</h3>", "Third-level heading");
addExampleRow(table, "<h6>Heading 6</h6>", "Smallest heading");

// Superscript and Subscript (NEW FEATURES)
addSectionHeader(table, "Superscript and Subscript (New!)");
addExampleRow(table, "H<sub>2</sub>O", "Water formula with subscript");
addExampleRow(table, "x<sup>2</sup> + y<sup>2</sup>", "Mathematical exponents");
addExampleRow(table, "CO<sub>2</sub>", "Carbon dioxide formula");
addExampleRow(table, "E=mc<sup>2</sup>", "Einstein's equation");
addExampleRow(table, "A<sup>n</sup><sub>i</sub>", "Combined super and subscript");

// Line Breaks and Paragraphs
addSectionHeader(table, "Line Breaks and Paragraphs");
addExampleRow(table, "First line<br/>Second line<br/>Third line",
"Line breaks separate lines");
addExampleRow(table, "<p>First paragraph</p><p>Second paragraph</p>",
"Paragraphs create vertical spacing");

// Lists
addSectionHeader(table, "Lists");
addExampleRow(table, "<ul><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>",
"Unordered (bullet) list");
addExampleRow(table, "<ol><li>First</li><li>Second</li><li>Third</li></ol>",
"Ordered (numbered) list");

// Complex Nested Example
addSectionHeader(table, "Complex Nested Tags");
addExampleRow(table,
"<b>Bold with <i>italic</i> and <u>underline</u></b>",
"Multiple nested tags work together");
addExampleRow(table,
"<u>Underlined with <sup>superscript</sup> and <sub>subscript</sub></u>",
"Combines underline with super/subscripts");

// Draw the table
table.draw();

// Save the document
File file = new File("target/tutorials/Tutorial02_HtmlFormatting.pdf");
System.out.println("Tutorial 02 PDF saved at: " + file.getAbsolutePath());
file.getParentFile().mkdirs();
document.save(file);
document.close();
}

/**
* Helper method to add an example row with alternating colors
*/
private void addExampleRow(BaseTable table, String htmlCode, String description) throws IOException {
Row<PDPage> row = table.createRow(15f);
Cell<PDPage> codeCell = row.createCell(40, htmlCode);
Cell<PDPage> descCell = row.createCell(60, description);

// Alternate colors for readability
Color rowColor = (table.getRows().size() % 2 == 0) ?
Color.WHITE : new Color(236, 240, 241);
codeCell.setFillColor(rowColor);
descCell.setFillColor(rowColor);
}

/**
* Helper method to add a section header
*/
private void addSectionHeader(BaseTable table, String title) throws IOException {
Row<PDPage> sectionRow = table.createRow(15f);
Cell<PDPage> sectionCell = sectionRow.createCell(100, title);
sectionCell.setFillColor(new Color(149, 165, 166));
sectionCell.setTextColor(Color.WHITE);
sectionCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD));
}
}
Loading
Loading