diff --git a/README.md b/README.md index 3f4db38..ae3e166 100644 --- a/README.md +++ b/README.md @@ -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 `` and `` +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 `` 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 diff --git a/src/main/java/be/quodlibet/boxable/Row.java b/src/main/java/be/quodlibet/boxable/Row.java index 41085dc..3ff9069 100644 --- a/src/main/java/be/quodlibet/boxable/Row.java +++ b/src/main/java/be/quodlibet/boxable/Row.java @@ -102,6 +102,23 @@ public Cell createImageCell(float width, Image img, HorizontalAlignment align return cell; } + /** + *

+ * Creates a table cell with provided width and table data, using the table's + * document, page, yStart, and margins. + *

+ * + * @param width + * Table width + * @param tableData + * Table's data (HTML table tags) + * @return {@link TableCell} with provided width and table data + */ + public TableCell createTableCell(float width, String tableData) { + return createTableCell(width, tableData, table.getDocument(), table.getCurrentPage(), table.yStart, + table.pageTopMargin, table.pageBottomMargin); + } + /** *

* Creates a table cell with provided width and table data diff --git a/src/test/java/be/quodlibet/boxable/BottomTablePlacementTest.java b/src/test/java/be/quodlibet/boxable/BottomTablePlacementTest.java index 6258416..b967a4d 100644 --- a/src/test/java/be/quodlibet/boxable/BottomTablePlacementTest.java +++ b/src/test/java/be/quodlibet/boxable/BottomTablePlacementTest.java @@ -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; diff --git a/src/test/java/be/quodlibet/boxable/tutorial/Tutorial01_BasicTable.java b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial01_BasicTable.java new file mode 100644 index 0000000..32478c8 --- /dev/null +++ b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial01_BasicTable.java @@ -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 titleRow = table.createRow(30f); + Cell 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 descRow = table.createRow(20f); + Cell 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 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 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 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 cell3 = dataRow.createCell(25, data[i][2]); + Cell cell4 = dataRow.createCell(25, data[i][3]); + + // Apply styling + for (Cell 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 summaryRow = table.createRow(20f); + Cell 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(); + } +} diff --git a/src/test/java/be/quodlibet/boxable/tutorial/Tutorial02_HtmlFormatting.java b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial02_HtmlFormatting.java new file mode 100644 index 0000000..35566df --- /dev/null +++ b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial02_HtmlFormatting.java @@ -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: , , , + * - Headings:

through
+ * - Superscript and subscript: , + * - Line breaks:
+ * - Paragraphs:

+ * - Lists:

    ,
      ,
    1. + * - 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 titleRow = table.createRow(30f); + Cell 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 descRow = table.createRow(20f); + Cell 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 headerRow = table.createRow(15f); + Cell htmlHeader = headerRow.createCell(40, "HTML Code"); + Cell 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, "Bold text", "Makes text bold"); + addExampleRow(table, "Italic text", "Makes text italic"); + addExampleRow(table, "Underlined text", "Underlines text"); + addExampleRow(table, "Strikethrough text", "Strikes through text"); + + // Nested formatting + addExampleRow(table, "Bold and Italic", "Combines multiple styles"); + addExampleRow(table, "Bold Underlined", "Underlined bold text"); + + // Headings + addSectionHeader(table, "Heading Tags"); + addExampleRow(table, "

      Heading 1

      ", "Largest heading"); + addExampleRow(table, "

      Heading 2

      ", "Second-level heading"); + addExampleRow(table, "

      Heading 3

      ", "Third-level heading"); + addExampleRow(table, "
      Heading 6
      ", "Smallest heading"); + + // Superscript and Subscript (NEW FEATURES) + addSectionHeader(table, "Superscript and Subscript (New!)"); + addExampleRow(table, "H2O", "Water formula with subscript"); + addExampleRow(table, "x2 + y2", "Mathematical exponents"); + addExampleRow(table, "CO2", "Carbon dioxide formula"); + addExampleRow(table, "E=mc2", "Einstein's equation"); + addExampleRow(table, "Ani", "Combined super and subscript"); + + // Line Breaks and Paragraphs + addSectionHeader(table, "Line Breaks and Paragraphs"); + addExampleRow(table, "First line
      Second line
      Third line", + "Line breaks separate lines"); + addExampleRow(table, "

      First paragraph

      Second paragraph

      ", + "Paragraphs create vertical spacing"); + + // Lists + addSectionHeader(table, "Lists"); + addExampleRow(table, "
      • Item 1
      • Item 2
      • Item 3
      ", + "Unordered (bullet) list"); + addExampleRow(table, "
      1. First
      2. Second
      3. Third
      ", + "Ordered (numbered) list"); + + // Complex Nested Example + addSectionHeader(table, "Complex Nested Tags"); + addExampleRow(table, + "Bold with italic and underline", + "Multiple nested tags work together"); + addExampleRow(table, + "Underlined with superscript and subscript", + "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 row = table.createRow(15f); + Cell codeCell = row.createCell(40, htmlCode); + Cell 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 sectionRow = table.createRow(15f); + Cell sectionCell = sectionRow.createCell(100, title); + sectionCell.setFillColor(new Color(149, 165, 166)); + sectionCell.setTextColor(Color.WHITE); + sectionCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + } +} diff --git a/src/test/java/be/quodlibet/boxable/tutorial/Tutorial03_ColorsAndTransparency.java b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial03_ColorsAndTransparency.java new file mode 100644 index 0000000..5867fa9 --- /dev/null +++ b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial03_ColorsAndTransparency.java @@ -0,0 +1,243 @@ +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 03: Colors and Transparency + * + * This tutorial demonstrates: + * - Setting cell fill colors (background) + * - Setting text colors + * - Using the alpha channel for transparency + * - Creating visual effects with color combinations + * - RGB and RGBA color specifications + */ +public class Tutorial03_ColorsAndTransparency { + + @Test + public void demonstrateColorsAndTransparency() 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 titleRow = table.createRow(30f); + Cell titleCell = titleRow.createCell(100, "Tutorial 03: Colors and Transparency"); + titleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + titleCell.setFontSize(16f); + titleCell.setFillColor(new Color(155, 89, 182)); // Purple background + titleCell.setTextColor(Color.WHITE); + titleCell.setAlign(HorizontalAlignment.CENTER); + + // Description + Row descRow = table.createRow(20f); + Cell descCell = descRow.createCell(100, + "Explore RGB colors, text colors, and alpha channel transparency effects."); + descCell.setFillColor(new Color(236, 240, 241)); + descCell.setAlign(HorizontalAlignment.CENTER); + + // Section 1: Basic RGB Colors + addSectionHeader(table, "Basic RGB Colors"); + + Row rgbHeader = table.createRow(15f); + rgbHeader.createCell(25, "Color Name"); + rgbHeader.createCell(25, "RGB Values"); + rgbHeader.createCell(25, "Fill Color"); + rgbHeader.createCell(25, "Text Color"); + styleHeaderRow(rgbHeader); + + // Red + addColorRow(table, "Red", "255, 0, 0", + new Color(255, 0, 0), new Color(255, 0, 0)); + + // Green + addColorRow(table, "Green", "0, 255, 0", + new Color(0, 255, 0), new Color(0, 128, 0)); + + // Blue + addColorRow(table, "Blue", "0, 0, 255", + new Color(0, 0, 255), new Color(0, 0, 255)); + + // Yellow + addColorRow(table, "Yellow", "255, 255, 0", + new Color(255, 255, 0), new Color(184, 134, 11)); + + // Cyan + addColorRow(table, "Cyan", "0, 255, 255", + new Color(0, 255, 255), new Color(0, 139, 139)); + + // Magenta + addColorRow(table, "Magenta", "255, 0, 255", + new Color(255, 0, 255), new Color(139, 0, 139)); + + // Section 2: Transparency (Alpha Channel) + addSectionHeader(table, "Transparency with Alpha Channel"); + + Row alphaHeader = table.createRow(15f); + alphaHeader.createCell(25, "Description"); + alphaHeader.createCell(25, "Alpha Value"); + alphaHeader.createCell(50, "Visual Effect"); + styleHeaderRow(alphaHeader); + + // Demonstrate different alpha values + addAlphaRow(table, "Opaque Red", "255 (100%)", + new Color(255, 0, 0, 255)); + + addAlphaRow(table, "Semi-Transparent Red", "180 (70%)", + new Color(255, 0, 0, 180)); + + addAlphaRow(table, "Half-Transparent Red", "128 (50%)", + new Color(255, 0, 0, 128)); + + addAlphaRow(table, "Very Transparent Red", "50 (20%)", + new Color(255, 0, 0, 50)); + + // Section 3: Color Combinations + addSectionHeader(table, "Text and Background Color Combinations"); + + Row comboHeader = table.createRow(15f); + comboHeader.createCell(50, "Background Color"); + comboHeader.createCell(50, "Text Color Example"); + styleHeaderRow(comboHeader); + + addCombinationRow(table, new Color(41, 128, 185), Color.WHITE, "White on Blue"); + addCombinationRow(table, new Color(46, 204, 113), Color.BLACK, "Black on Green"); + addCombinationRow(table, new Color(241, 196, 15), Color.BLACK, "Black on Yellow"); + addCombinationRow(table, new Color(52, 73, 94), Color.WHITE, "White on Dark Gray"); + addCombinationRow(table, new Color(236, 240, 241), Color.BLACK, "Black on Light Gray"); + addCombinationRow(table, new Color(231, 76, 60), Color.WHITE, "White on Red"); + + // Section 4: Gradient-like effects + addSectionHeader(table, "Simulated Gradient Effect (Using Alpha)"); + + Row gradientDesc = table.createRow(15f); + Cell gradCell = gradientDesc.createCell(100, + "By varying alpha values, we can create gradient-like visual effects:"); + gradCell.setFillColor(Color.WHITE); + + // Create gradient effect with different alpha values + for (int i = 0; i <= 5; i++) { + int alpha = 255 - (i * 40); // Decreasing opacity + Row gradRow = table.createRow(12f); + Cell cell = gradRow.createCell(100, + String.format("Level %d - Alpha: %d", i + 1, alpha)); + cell.setFillColor(new Color(52, 152, 219, alpha)); + if (alpha < 150) { + cell.setTextColor(Color.BLACK); + } else { + cell.setTextColor(Color.WHITE); + } + } + + // Draw the table + table.draw(); + + // Save the document + File file = new File("target/tutorials/Tutorial03_ColorsAndTransparency.pdf"); + System.out.println("Tutorial 03 PDF saved at: " + file.getAbsolutePath()); + file.getParentFile().mkdirs(); + document.save(file); + document.close(); + } + + /** + * Helper method to add a section header + */ + private void addSectionHeader(BaseTable table, String title) throws IOException { + Row sectionRow = table.createRow(18f); + Cell sectionCell = sectionRow.createCell(100, title); + sectionCell.setFillColor(new Color(44, 62, 80)); + sectionCell.setTextColor(Color.WHITE); + sectionCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + sectionCell.setAlign(HorizontalAlignment.CENTER); + } + + /** + * Helper method to style header rows + */ + private void styleHeaderRow(Row row) { + Color headerColor = new Color(52, 73, 94); + for (Cell cell : row.getCells()) { + cell.setFillColor(headerColor); + cell.setTextColor(Color.WHITE); + cell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + } + } + + /** + * Helper method to add a color demonstration row + */ + private void addColorRow(BaseTable table, String name, String rgb, + Color fillColor, Color textColor) throws IOException { + Row row = table.createRow(15f); + row.createCell(25, name); + row.createCell(25, rgb); + + Cell fillCell = row.createCell(25, "Sample"); + fillCell.setFillColor(fillColor); + fillCell.setTextColor(Color.WHITE); + fillCell.setAlign(HorizontalAlignment.CENTER); + + Cell textCell = row.createCell(25, "Sample Text"); + textCell.setTextColor(textColor); + textCell.setAlign(HorizontalAlignment.CENTER); + } + + /** + * Helper method to add an alpha/transparency demonstration row + */ + private void addAlphaRow(BaseTable table, String description, String alphaValue, + Color color) throws IOException { + Row row = table.createRow(15f); + row.createCell(25, description); + row.createCell(25, alphaValue); + + Cell visualCell = row.createCell(50, "This cell shows the transparency effect"); + visualCell.setFillColor(color); + visualCell.setAlign(HorizontalAlignment.CENTER); + } + + /** + * Helper method to add a color combination row + */ + private void addCombinationRow(BaseTable table, Color bgColor, Color textColor, + String text) throws IOException { + Row row = table.createRow(15f); + + Cell bgCell = row.createCell(50, "Background Sample"); + bgCell.setFillColor(bgColor); + bgCell.setTextColor(Color.WHITE); + bgCell.setAlign(HorizontalAlignment.CENTER); + + Cell textCell = row.createCell(50, text); + textCell.setFillColor(bgColor); + textCell.setTextColor(textColor); + textCell.setAlign(HorizontalAlignment.CENTER); + } +} diff --git a/src/test/java/be/quodlibet/boxable/tutorial/Tutorial04_Alignment.java b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial04_Alignment.java new file mode 100644 index 0000000..522a397 --- /dev/null +++ b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial04_Alignment.java @@ -0,0 +1,264 @@ +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 be.quodlibet.boxable.VerticalAlignment; +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 04: Cell Alignment + * + * This tutorial demonstrates: + * - Horizontal alignment (LEFT, CENTER, RIGHT) + * - Vertical alignment (TOP, MIDDLE, BOTTOM) + * - Combined horizontal and vertical alignments + * - Practical alignment examples + */ +public class Tutorial04_Alignment { + + @Test + public void demonstrateAlignment() 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 titleRow = table.createRow(30f); + Cell titleCell = titleRow.createCell(100, "Tutorial 04: Cell Alignment"); + titleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + titleCell.setFontSize(16f); + titleCell.setFillColor(new Color(230, 126, 34)); // Orange background + titleCell.setTextColor(Color.WHITE); + titleCell.setAlign(HorizontalAlignment.CENTER); + + // Description + Row descRow = table.createRow(20f); + Cell descCell = descRow.createCell(100, + "Demonstrate horizontal and vertical text alignment options in cells."); + descCell.setFillColor(new Color(236, 240, 241)); + descCell.setAlign(HorizontalAlignment.CENTER); + + // Section 1: Horizontal Alignment + addSectionHeader(table, "Horizontal Alignment"); + + Row hAlignHeader = table.createRow(15f); + hAlignHeader.createCell(25, "Alignment"); + hAlignHeader.createCell(75, "Example"); + styleHeaderRow(hAlignHeader); + + // LEFT alignment + Row leftRow = table.createRow(15f); + leftRow.createCell(25, "LEFT"); + Cell leftCell = leftRow.createCell(75, "This text is aligned to the left"); + leftCell.setAlign(HorizontalAlignment.LEFT); + leftCell.setFillColor(new Color(174, 214, 241)); + + // CENTER alignment + Row centerRow = table.createRow(15f); + centerRow.createCell(25, "CENTER"); + Cell centerCell = centerRow.createCell(75, "This text is centered"); + centerCell.setAlign(HorizontalAlignment.CENTER); + centerCell.setFillColor(new Color(174, 214, 241)); + + // RIGHT alignment + Row rightRow = table.createRow(15f); + rightRow.createCell(25, "RIGHT"); + Cell rightCell = rightRow.createCell(75, "This text is aligned to the right"); + rightCell.setAlign(HorizontalAlignment.RIGHT); + rightCell.setFillColor(new Color(174, 214, 241)); + + // Section 2: Vertical Alignment + addSectionHeader(table, "Vertical Alignment (Tall Cells)"); + + Row vAlignHeader = table.createRow(15f); + vAlignHeader.createCell(25, "Alignment"); + vAlignHeader.createCell(75, "Example (40pt tall cells)"); + styleHeaderRow(vAlignHeader); + + // TOP alignment + Row topRow = table.createRow(40f); + topRow.createCell(25, "TOP"); + Cell topCell = topRow.createCell(75, "Text at top"); + topCell.setValign(VerticalAlignment.TOP); + topCell.setFillColor(new Color(212, 230, 241)); + + // MIDDLE alignment + Row middleRow = table.createRow(40f); + middleRow.createCell(25, "MIDDLE"); + Cell middleCell = middleRow.createCell(75, "Text in middle"); + middleCell.setValign(VerticalAlignment.MIDDLE); + middleCell.setFillColor(new Color(212, 230, 241)); + + // BOTTOM alignment + Row bottomRow = table.createRow(40f); + bottomRow.createCell(25, "BOTTOM"); + Cell bottomCell = bottomRow.createCell(75, "Text at bottom"); + bottomCell.setValign(VerticalAlignment.BOTTOM); + bottomCell.setFillColor(new Color(212, 230, 241)); + + // Section 3: Combined Alignment + addSectionHeader(table, "Combined Horizontal and Vertical Alignment"); + + Row comboHeader = table.createRow(15f); + comboHeader.createCell(33.33f, "Top Left"); + comboHeader.createCell(33.33f, "Top Center"); + comboHeader.createCell(33.34f, "Top Right"); + styleHeaderRow(comboHeader); + + // Top row combinations + Row topComboRow = table.createRow(35f); + Cell topLeft = topComboRow.createCell(33.33f, "TOP
      LEFT"); + topLeft.setAlign(HorizontalAlignment.LEFT); + topLeft.setValign(VerticalAlignment.TOP); + topLeft.setFillColor(new Color(230, 245, 255)); + + Cell topCenter = topComboRow.createCell(33.33f, "TOP
      CENTER"); + topCenter.setAlign(HorizontalAlignment.CENTER); + topCenter.setValign(VerticalAlignment.TOP); + topCenter.setFillColor(new Color(230, 245, 255)); + + Cell topRight = topComboRow.createCell(33.34f, "TOP
      RIGHT"); + topRight.setAlign(HorizontalAlignment.RIGHT); + topRight.setValign(VerticalAlignment.TOP); + topRight.setFillColor(new Color(230, 245, 255)); + + // Middle row combinations + Row middleComboRow = table.createRow(35f); + Cell middleLeft = middleComboRow.createCell(33.33f, "MIDDLE
      LEFT"); + middleLeft.setAlign(HorizontalAlignment.LEFT); + middleLeft.setValign(VerticalAlignment.MIDDLE); + middleLeft.setFillColor(new Color(255, 243, 224)); + + Cell middleCenter = middleComboRow.createCell(33.33f, "MIDDLE
      CENTER"); + middleCenter.setAlign(HorizontalAlignment.CENTER); + middleCenter.setValign(VerticalAlignment.MIDDLE); + middleCenter.setFillColor(new Color(255, 243, 224)); + + Cell middleRight = middleComboRow.createCell(33.34f, "MIDDLE
      RIGHT"); + middleRight.setAlign(HorizontalAlignment.RIGHT); + middleRight.setValign(VerticalAlignment.MIDDLE); + middleRight.setFillColor(new Color(255, 243, 224)); + + // Bottom row combinations + Row bottomComboRow = table.createRow(35f); + Cell bottomLeft = bottomComboRow.createCell(33.33f, "BOTTOM
      LEFT"); + bottomLeft.setAlign(HorizontalAlignment.LEFT); + bottomLeft.setValign(VerticalAlignment.BOTTOM); + bottomLeft.setFillColor(new Color(255, 235, 238)); + + Cell bottomCenter = bottomComboRow.createCell(33.33f, "BOTTOM
      CENTER"); + bottomCenter.setAlign(HorizontalAlignment.CENTER); + bottomCenter.setValign(VerticalAlignment.BOTTOM); + bottomCenter.setFillColor(new Color(255, 235, 238)); + + Cell bottomRight = bottomComboRow.createCell(33.34f, "BOTTOM
      RIGHT"); + bottomRight.setAlign(HorizontalAlignment.RIGHT); + bottomRight.setValign(VerticalAlignment.BOTTOM); + bottomRight.setFillColor(new Color(255, 235, 238)); + + // Section 4: Practical Example + addSectionHeader(table, "Practical Example: Invoice Table"); + + Row invoiceHeader = table.createRow(15f); + invoiceHeader.createCell(40, "Item"); + Cell qtyHeader = invoiceHeader.createCell(20, "Qty"); + Cell priceHeader = invoiceHeader.createCell(20, "Price"); + Cell totalHeader = invoiceHeader.createCell(20, "Total"); + styleHeaderRow(invoiceHeader); + + // Center align numeric headers + qtyHeader.setAlign(HorizontalAlignment.CENTER); + priceHeader.setAlign(HorizontalAlignment.RIGHT); + totalHeader.setAlign(HorizontalAlignment.RIGHT); + + // Invoice data rows + String[][] invoiceData = { + {"Premium Widget", "5", "$10.00", "$50.00"}, + {"Standard Widget", "10", "$5.00", "$50.00"}, + {"Economy Widget", "20", "$2.50", "$50.00"} + }; + + for (String[] row : invoiceData) { + Row dataRow = table.createRow(15f); + Cell itemCell = dataRow.createCell(40, row[0]); + Cell qtyCell = dataRow.createCell(20, row[1]); + Cell priceCell = dataRow.createCell(20, row[2]); + Cell totalCell = dataRow.createCell(20, row[3]); + + // Apply appropriate alignment + itemCell.setAlign(HorizontalAlignment.LEFT); + qtyCell.setAlign(HorizontalAlignment.CENTER); + priceCell.setAlign(HorizontalAlignment.RIGHT); + totalCell.setAlign(HorizontalAlignment.RIGHT); + } + + // Total row + Row totalRow = table.createRow(15f); + Cell totalLabel = totalRow.createCell(80, "TOTAL:"); + Cell totalAmount = totalRow.createCell(20, "$150.00"); + totalLabel.setAlign(HorizontalAlignment.RIGHT); + totalLabel.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + totalAmount.setAlign(HorizontalAlignment.RIGHT); + totalAmount.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + totalAmount.setFillColor(new Color(46, 204, 113)); + totalAmount.setTextColor(Color.WHITE); + + // Draw the table + table.draw(); + + // Save the document + File file = new File("target/tutorials/Tutorial04_Alignment.pdf"); + System.out.println("Tutorial 04 PDF saved at: " + file.getAbsolutePath()); + file.getParentFile().mkdirs(); + document.save(file); + document.close(); + } + + /** + * Helper method to add a section header + */ + private void addSectionHeader(BaseTable table, String title) throws IOException { + Row sectionRow = table.createRow(18f); + Cell sectionCell = sectionRow.createCell(100, title); + sectionCell.setFillColor(new Color(44, 62, 80)); + sectionCell.setTextColor(Color.WHITE); + sectionCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + sectionCell.setAlign(HorizontalAlignment.CENTER); + } + + /** + * Helper method to style header rows + */ + private void styleHeaderRow(Row row) { + Color headerColor = new Color(52, 73, 94); + for (Cell cell : row.getCells()) { + cell.setFillColor(headerColor); + cell.setTextColor(Color.WHITE); + cell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + } + } +} diff --git a/src/test/java/be/quodlibet/boxable/tutorial/Tutorial05_Images.java b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial05_Images.java new file mode 100644 index 0000000..ca8233e --- /dev/null +++ b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial05_Images.java @@ -0,0 +1,256 @@ +package be.quodlibet.boxable.tutorial; + +import be.quodlibet.boxable.BaseTable; +import be.quodlibet.boxable.Cell; +import be.quodlibet.boxable.HorizontalAlignment; +import be.quodlibet.boxable.ImageCell; +import be.quodlibet.boxable.Row; +import be.quodlibet.boxable.image.Image; +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.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +/** + * Tutorial 05: Images in Cells + * + * This tutorial demonstrates: + * - Adding images to table cells + * - Image scaling to fit cells + * - Image padding control + * - Multiple images in a table + * - Different image sizes and aspect ratios + */ +public class Tutorial05_Images { + + @Test + public void demonstrateImages() 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 titleRow = table.createRow(30f); + Cell titleCell = titleRow.createCell(100, "Tutorial 05: Images in Cells"); + titleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + titleCell.setFontSize(16f); + titleCell.setFillColor(new Color(26, 188, 156)); // Teal background + titleCell.setTextColor(Color.WHITE); + titleCell.setAlign(HorizontalAlignment.CENTER); + + // Description + Row descRow = table.createRow(20f); + Cell descCell = descRow.createCell(100, + "Learn how to add images to cells, control scaling, and adjust padding."); + descCell.setFillColor(new Color(236, 240, 241)); + descCell.setAlign(HorizontalAlignment.CENTER); + + // Section 1: Basic Image in Cell + addSectionHeader(table, "Basic Image in Cell"); + + Row basicDesc = table.createRow(15f); + basicDesc.createCell(100, "A simple image in a cell with default settings:"); + + // Create a sample image (red background with green circle) + BufferedImage redGreenImage = createSampleImage(200, 100, Color.RED, Color.GREEN); + Image image1 = new Image(redGreenImage); + + Row imageRow1 = table.createRow(100f); + ImageCell imageCell1 = imageRow1.createImageCell(100, image1); + imageCell1.setAlign(HorizontalAlignment.CENTER); + + // Section 2: Image Scaling + addSectionHeader(table, "Image Scaling"); + + Row scalingHeader = table.createRow(15f); + scalingHeader.createCell(33.33f, "Small Cell"); + scalingHeader.createCell(33.33f, "Medium Cell"); + scalingHeader.createCell(33.34f, "Large Cell"); + styleHeaderRow(scalingHeader); + + Row scalingRow = table.createRow(80f); + + // Small cell with image + BufferedImage blueImage = createSampleImage(200, 100, Color.BLUE, Color.YELLOW); + Image image2 = new Image(blueImage); + ImageCell smallCell = scalingRow.createImageCell(33.33f, image2); + smallCell.setAlign(HorizontalAlignment.CENTER); + + // Medium cell with image + BufferedImage orangeImage = createSampleImage(200, 100, Color.ORANGE, Color.CYAN); + Image image3 = new Image(orangeImage); + ImageCell mediumCell = scalingRow.createImageCell(33.33f, image3); + mediumCell.setAlign(HorizontalAlignment.CENTER); + + // Large cell with image + BufferedImage purpleImage = createSampleImage(200, 100, new Color(155, 89, 182), Color.WHITE); + Image image4 = new Image(purpleImage); + ImageCell largeCell = scalingRow.createImageCell(33.34f, image4); + largeCell.setAlign(HorizontalAlignment.CENTER); + + // Section 3: Image Padding Control + addSectionHeader(table, "Image Padding Control"); + + Row paddingDesc = table.createRow(15f); + paddingDesc.createCell(100, "Control the padding around images:"); + + Row paddingHeader = table.createRow(15f); + paddingHeader.createCell(33.33f, "Default Padding"); + paddingHeader.createCell(33.33f, "No Padding"); + paddingHeader.createCell(33.34f, "Custom Padding"); + styleHeaderRow(paddingHeader); + + Row paddingRow = table.createRow(80f); + + // Default padding + BufferedImage greenImage = createSampleImage(200, 100, new Color(46, 204, 113), Color.BLACK); + Image image5 = new Image(greenImage); + ImageCell defaultPadCell = paddingRow.createImageCell(33.33f, image5); + defaultPadCell.setFillColor(new Color(236, 240, 241)); + + // No padding + BufferedImage yellowImage = createSampleImage(200, 100, new Color(241, 196, 15), Color.BLACK); + Image image6 = new Image(yellowImage); + ImageCell noPadCell = paddingRow.createImageCell(33.33f, image6); + noPadCell.setTopPadding(0); + noPadCell.setBottomPadding(0); + noPadCell.setLeftPadding(0); + noPadCell.setRightPadding(0); + noPadCell.setFillColor(new Color(236, 240, 241)); + + // Custom padding (20pt) + BufferedImage cyanImage = createSampleImage(200, 100, new Color(52, 152, 219), Color.WHITE); + Image image7 = new Image(cyanImage); + ImageCell customPadCell = paddingRow.createImageCell(33.34f, image7); + customPadCell.setTopPadding(20); + customPadCell.setBottomPadding(20); + customPadCell.setLeftPadding(20); + customPadCell.setRightPadding(20); + customPadCell.setFillColor(new Color(236, 240, 241)); + + // Section 4: Different Image Aspect Ratios + addSectionHeader(table, "Different Image Aspect Ratios"); + + Row aspectHeader = table.createRow(15f); + aspectHeader.createCell(33.33f, "Landscape (2:1)"); + aspectHeader.createCell(33.33f, "Square (1:1)"); + aspectHeader.createCell(33.34f, "Portrait (1:2)"); + styleHeaderRow(aspectHeader); + + Row aspectRow = table.createRow(100f); + + // Landscape image + BufferedImage landscapeImage = createSampleImage(200, 100, new Color(231, 76, 60), Color.WHITE); + Image image8 = new Image(landscapeImage); + ImageCell landscapeCell = aspectRow.createImageCell(33.33f, image8); + landscapeCell.setAlign(HorizontalAlignment.CENTER); + + // Square image + BufferedImage squareImage = createSampleImage(150, 150, new Color(52, 73, 94), Color.WHITE); + Image image9 = new Image(squareImage); + ImageCell squareCell = aspectRow.createImageCell(33.33f, image9); + squareCell.setAlign(HorizontalAlignment.CENTER); + + // Portrait image + BufferedImage portraitImage = createSampleImage(100, 200, new Color(155, 89, 182), Color.WHITE); + Image image10 = new Image(portraitImage); + ImageCell portraitCell = aspectRow.createImageCell(33.34f, image10); + portraitCell.setAlign(HorizontalAlignment.CENTER); + + // Section 5: Images with Text + addSectionHeader(table, "Images Combined with Text"); + + Row combinedRow = table.createRow(80f); + + // Image cell + BufferedImage finalImage = createSampleImage(200, 100, new Color(41, 128, 185), Color.WHITE); + Image image11 = new Image(finalImage); + ImageCell imgCell = combinedRow.createImageCell(40f, image11); + imgCell.setAlign(HorizontalAlignment.CENTER); + + // Text description cell + Cell textCell = combinedRow.createCell(60f, + "Product Name: Sample Widget
      " + + "Description: This is a high-quality product with excellent features.
      " + + "Price: $99.99
      " + + "Stock: In Stock"); + textCell.setAlign(HorizontalAlignment.LEFT); + + // Draw the table + table.draw(); + + // Save the document + File file = new File("target/tutorials/Tutorial05_Images.pdf"); + System.out.println("Tutorial 05 PDF saved at: " + file.getAbsolutePath()); + file.getParentFile().mkdirs(); + document.save(file); + document.close(); + } + + /** + * Helper method to create a sample image with colored background and shape + */ + private BufferedImage createSampleImage(int width, int height, Color bgColor, Color shapeColor) { + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + + // Fill background + g.setColor(bgColor); + g.fillRect(0, 0, width, height); + + // Draw a circle or oval + g.setColor(shapeColor); + int size = Math.min(width, height); + int x = (width - size) / 2; + int y = (height - size) / 2; + g.fillOval(x, y, size, size); + + g.dispose(); + return image; + } + + /** + * Helper method to add a section header + */ + private void addSectionHeader(BaseTable table, String title) throws IOException { + Row sectionRow = table.createRow(18f); + Cell sectionCell = sectionRow.createCell(100, title); + sectionCell.setFillColor(new Color(44, 62, 80)); + sectionCell.setTextColor(Color.WHITE); + sectionCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + sectionCell.setAlign(HorizontalAlignment.CENTER); + } + + /** + * Helper method to style header rows + */ + private void styleHeaderRow(Row row) { + Color headerColor = new Color(52, 73, 94); + for (Cell cell : row.getCells()) { + cell.setFillColor(headerColor); + cell.setTextColor(Color.WHITE); + cell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + } + } +} diff --git a/src/test/java/be/quodlibet/boxable/tutorial/Tutorial06_BordersAndStyling.java b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial06_BordersAndStyling.java new file mode 100644 index 0000000..4d38bc1 --- /dev/null +++ b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial06_BordersAndStyling.java @@ -0,0 +1,281 @@ +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 be.quodlibet.boxable.line.LineStyle; +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 06: Borders and Styling + * + * This tutorial demonstrates: + * - Border styles (solid lines with different widths) + * - Border colors + * - Selective borders (top, bottom, left, right) + * - No borders (removing borders) + * - Outer border only + * - Custom border configurations + */ +public class Tutorial06_BordersAndStyling { + + @Test + public void demonstrateBordersAndStyling() 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 titleRow = table.createRow(30f); + Cell titleCell = titleRow.createCell(100, "Tutorial 06: Borders and Styling"); + titleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + titleCell.setFontSize(16f); + titleCell.setFillColor(new Color(192, 57, 43)); // Dark red background + titleCell.setTextColor(Color.WHITE); + titleCell.setAlign(HorizontalAlignment.CENTER); + + // Description + Row descRow = table.createRow(20f); + Cell descCell = descRow.createCell(100, + "Learn how to customize cell borders, including colors, widths, and selective borders."); + descCell.setFillColor(new Color(236, 240, 241)); + descCell.setAlign(HorizontalAlignment.CENTER); + + // Section 1: Default Borders + addSectionHeader(table, "Default Borders"); + + Row defaultDesc = table.createRow(15f); + defaultDesc.createCell(100, "By default, all cells have 1pt black borders:"); + + Row defaultRow = table.createRow(20f); + defaultRow.createCell(33.33f, "Cell 1"); + defaultRow.createCell(33.33f, "Cell 2"); + defaultRow.createCell(33.34f, "Cell 3"); + + // Section 2: Border Width Variations + addSectionHeader(table, "Border Width Variations"); + + Row widthHeader = table.createRow(15f); + widthHeader.createCell(33.33f, "Thin (0.5pt)"); + widthHeader.createCell(33.33f, "Normal (1pt)"); + widthHeader.createCell(33.34f, "Thick (3pt)"); + styleHeaderRow(widthHeader); + + Row widthRow = table.createRow(20f); + + // Thin border + Cell thinCell = widthRow.createCell(33.33f, "Thin Border"); + thinCell.setBorderStyle(new LineStyle(Color.BLACK, 0.5f)); + thinCell.setAlign(HorizontalAlignment.CENTER); + + // Normal border + Cell normalCell = widthRow.createCell(33.33f, "Normal Border"); + normalCell.setBorderStyle(new LineStyle(Color.BLACK, 1f)); + normalCell.setAlign(HorizontalAlignment.CENTER); + + // Thick border + Cell thickCell = widthRow.createCell(33.34f, "Thick Border"); + thickCell.setBorderStyle(new LineStyle(Color.BLACK, 3f)); + thickCell.setAlign(HorizontalAlignment.CENTER); + + // Section 3: Border Colors + addSectionHeader(table, "Border Colors"); + + Row colorHeader = table.createRow(15f); + colorHeader.createCell(25, "Red"); + colorHeader.createCell(25, "Blue"); + colorHeader.createCell(25, "Green"); + colorHeader.createCell(25, "Purple"); + styleHeaderRow(colorHeader); + + Row colorRow = table.createRow(20f); + + Cell redBorder = colorRow.createCell(25, "Red Border"); + redBorder.setBorderStyle(new LineStyle(Color.RED, 2f)); + redBorder.setAlign(HorizontalAlignment.CENTER); + + Cell blueBorder = colorRow.createCell(25, "Blue Border"); + blueBorder.setBorderStyle(new LineStyle(Color.BLUE, 2f)); + blueBorder.setAlign(HorizontalAlignment.CENTER); + + Cell greenBorder = colorRow.createCell(25, "Green Border"); + greenBorder.setBorderStyle(new LineStyle(new Color(46, 204, 113), 2f)); + greenBorder.setAlign(HorizontalAlignment.CENTER); + + Cell purpleBorder = colorRow.createCell(25, "Purple Border"); + purpleBorder.setBorderStyle(new LineStyle(new Color(155, 89, 182), 2f)); + purpleBorder.setAlign(HorizontalAlignment.CENTER); + + // Section 4: Selective Borders + addSectionHeader(table, "Selective Borders"); + + Row selectDesc = table.createRow(15f); + selectDesc.createCell(100, "Control which sides have borders:"); + + Row selectHeader = table.createRow(15f); + selectHeader.createCell(25, "Top Only"); + selectHeader.createCell(25, "Bottom Only"); + selectHeader.createCell(25, "Left Only"); + selectHeader.createCell(25, "Right Only"); + styleHeaderRow(selectHeader); + + Row selectRow = table.createRow(30f); + + Cell topOnly = selectRow.createCell(25, "Top Border Only"); + topOnly.setTopBorderStyle(new LineStyle(Color.BLACK, 2f)); + topOnly.setBottomBorderStyle(null); + topOnly.setLeftBorderStyle(null); + topOnly.setRightBorderStyle(null); + topOnly.setAlign(HorizontalAlignment.CENTER); + + Cell bottomOnly = selectRow.createCell(25, "Bottom Border Only"); + bottomOnly.setTopBorderStyle(null); + bottomOnly.setBottomBorderStyle(new LineStyle(Color.BLACK, 2f)); + bottomOnly.setLeftBorderStyle(null); + bottomOnly.setRightBorderStyle(null); + bottomOnly.setAlign(HorizontalAlignment.CENTER); + + Cell leftOnly = selectRow.createCell(25, "Left Border Only"); + leftOnly.setTopBorderStyle(null); + leftOnly.setBottomBorderStyle(null); + leftOnly.setLeftBorderStyle(new LineStyle(Color.BLACK, 2f)); + leftOnly.setRightBorderStyle(null); + leftOnly.setAlign(HorizontalAlignment.CENTER); + + Cell rightOnly = selectRow.createCell(25, "Right Border Only"); + rightOnly.setTopBorderStyle(null); + rightOnly.setBottomBorderStyle(null); + rightOnly.setLeftBorderStyle(null); + rightOnly.setRightBorderStyle(new LineStyle(Color.BLACK, 2f)); + rightOnly.setAlign(HorizontalAlignment.CENTER); + + // Section 5: No Borders + addSectionHeader(table, "No Borders"); + + Row noBorderDesc = table.createRow(15f); + noBorderDesc.createCell(100, "Cells can be created without any borders:"); + + Row noBorderRow = table.createRow(20f); + + Cell noBorder1 = noBorderRow.createCell(33.33f, "No Border"); + noBorder1.setBorderStyle(null); + noBorder1.setFillColor(new Color(174, 214, 241)); + noBorder1.setAlign(HorizontalAlignment.CENTER); + + Cell noBorder2 = noBorderRow.createCell(33.33f, "No Border"); + noBorder2.setBorderStyle(null); + noBorder2.setFillColor(new Color(255, 243, 224)); + noBorder2.setAlign(HorizontalAlignment.CENTER); + + Cell noBorder3 = noBorderRow.createCell(33.34f, "No Border"); + noBorder3.setBorderStyle(null); + noBorder3.setFillColor(new Color(255, 235, 238)); + noBorder3.setAlign(HorizontalAlignment.CENTER); + + // Section 6: Combined Styling + addSectionHeader(table, "Combined Styling Example"); + + Row comboDesc = table.createRow(15f); + comboDesc.createCell(100, "Combining borders with colors for visual effects:"); + + Row comboRow = table.createRow(25f); + + Cell combo1 = comboRow.createCell(50, "Important: Highlighted Cell"); + combo1.setFillColor(new Color(255, 243, 224)); + combo1.setBorderStyle(new LineStyle(new Color(243, 156, 18), 3f)); + + Cell combo2 = comboRow.createCell(50, "Warning: Alert Cell"); + combo2.setFillColor(new Color(255, 235, 238)); + combo2.setBorderStyle(new LineStyle(new Color(231, 76, 60), 3f)); + + // Draw the table + table.draw(); + + // Create a second table demonstrating outer border only + if (table.getCurrentPage() != page) { + page = table.getCurrentPage(); + } + // Create new page for the second table + page = new PDPage(PDRectangle.A4); + document.addPage(page); + float newYStart = yStartNewPage; + + BaseTable table2 = new BaseTable(newYStart, yStartNewPage, bottomMargin, + tableWidth, margin, document, page, + false, true); // drawLines = false + table2.setOuterBorderStyle(new LineStyle(Color.BLACK, 2f)); + + Row outerTitle = table2.createRow(20f); + Cell outerTitleCell = outerTitle.createCell(100, "Outer Border Only Example"); + outerTitleCell.setFillColor(new Color(52, 73, 94)); + outerTitleCell.setTextColor(Color.WHITE); + outerTitleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + outerTitleCell.setAlign(HorizontalAlignment.CENTER); + + Row outerRow1 = table2.createRow(15f); + outerRow1.createCell(33.33f, "Cell 1"); + outerRow1.createCell(33.33f, "Cell 2"); + outerRow1.createCell(33.34f, "Cell 3"); + + Row outerRow2 = table2.createRow(15f); + outerRow2.createCell(33.33f, "Cell 4"); + outerRow2.createCell(33.33f, "Cell 5"); + outerRow2.createCell(33.34f, "Cell 6"); + + table2.draw(); + + // Save the document + File file = new File("target/tutorials/Tutorial06_BordersAndStyling.pdf"); + System.out.println("Tutorial 06 PDF saved at: " + file.getAbsolutePath()); + file.getParentFile().mkdirs(); + document.save(file); + document.close(); + } + + /** + * Helper method to add a section header + */ + private void addSectionHeader(BaseTable table, String title) throws IOException { + Row sectionRow = table.createRow(18f); + Cell sectionCell = sectionRow.createCell(100, title); + sectionCell.setFillColor(new Color(44, 62, 80)); + sectionCell.setTextColor(Color.WHITE); + sectionCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + sectionCell.setAlign(HorizontalAlignment.CENTER); + } + + /** + * Helper method to style header rows + */ + private void styleHeaderRow(Row row) { + Color headerColor = new Color(52, 73, 94); + for (Cell cell : row.getCells()) { + cell.setFillColor(headerColor); + cell.setTextColor(Color.WHITE); + cell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + } + } +} diff --git a/src/test/java/be/quodlibet/boxable/tutorial/Tutorial07_HeaderRows.java b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial07_HeaderRows.java new file mode 100644 index 0000000..3cc8a3a --- /dev/null +++ b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial07_HeaderRows.java @@ -0,0 +1,243 @@ +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 07: Header Rows + * + * This tutorial demonstrates: + * - Creating single header rows + * - Creating multiple header rows + * - Header styling + * - Headers spanning multiple pages + * - Header row repetition on new pages + */ +public class Tutorial07_HeaderRows { + + @Test + public void demonstrateHeaderRows() 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 titleRow = table.createRow(30f); + Cell titleCell = titleRow.createCell(100, "Tutorial 07: Header Rows"); + titleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + titleCell.setFontSize(16f); + titleCell.setFillColor(new Color(142, 68, 173)); // Purple background + titleCell.setTextColor(Color.WHITE); + titleCell.setAlign(HorizontalAlignment.CENTER); + + // Description + Row descRow = table.createRow(20f); + Cell descCell = descRow.createCell(100, + "Learn how to create header rows that repeat on each page of multi-page tables."); + descCell.setFillColor(new Color(236, 240, 241)); + descCell.setAlign(HorizontalAlignment.CENTER); + + // Section 1: Single Header Row + addSectionHeader(table, "Single Header Row"); + + Row singleHeader = table.createRow(20f); + singleHeader.createCell(25, "Name"); + singleHeader.createCell(25, "Age"); + singleHeader.createCell(25, "City"); + singleHeader.createCell(25, "Country"); + styleHeaderRow(singleHeader); + table.addHeaderRow(singleHeader); + + // Add some data rows + for (int i = 1; i <= 5; i++) { + Row dataRow = table.createRow(15f); + dataRow.createCell(25, "Person " + i); + dataRow.createCell(25, String.valueOf(20 + i)); + dataRow.createCell(25, "City " + i); + dataRow.createCell(25, "Country " + i); + + if (i % 2 == 0) { + for (Cell cell : dataRow.getCells()) { + cell.setFillColor(new Color(236, 240, 241)); + } + } + } + + // Section 2: Multiple Header Rows + addSectionHeader(table, "Multiple Header Rows"); + + // Create a new table to demonstrate multiple headers + float newYStart = table.draw() - 30; + if (table.getCurrentPage() != page) { + page = table.getCurrentPage(); + } + + BaseTable table2 = new BaseTable(newYStart, yStartNewPage, bottomMargin, + tableWidth, margin, document, page, + true, true); + + // First header row - Main title + Row mainHeader = table2.createRow(20f); + Cell mainHeaderCell = mainHeader.createCell(100, "2024 Sales Report"); + mainHeaderCell.setFillColor(new Color(41, 128, 185)); + mainHeaderCell.setTextColor(Color.WHITE); + mainHeaderCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + mainHeaderCell.setAlign(HorizontalAlignment.CENTER); + table2.addHeaderRow(mainHeader); + + // Second header row - Subtitle + Row subHeader = table2.createRow(15f); + Cell subHeaderCell = subHeader.createCell(100, "Quarterly Performance Analysis"); + subHeaderCell.setFillColor(new Color(52, 152, 219)); + subHeaderCell.setTextColor(Color.WHITE); + subHeaderCell.setAlign(HorizontalAlignment.CENTER); + table2.addHeaderRow(subHeader); + + // Third header row - Column headers + Row colHeader = table2.createRow(15f); + colHeader.createCell(40, "Product"); + colHeader.createCell(20, "Q1"); + colHeader.createCell(20, "Q2"); + colHeader.createCell(20, "Q3"); + styleHeaderRow(colHeader); + table2.addHeaderRow(colHeader); + + // Data rows + String[][] salesData = { + {"Widget A", "$10,000", "$12,000", "$11,500"}, + {"Widget B", "$8,500", "$9,200", "$9,800"}, + {"Widget C", "$15,000", "$16,500", "$17,200"}, + {"Widget D", "$7,200", "$7,800", "$8,100"}, + {"Widget E", "$11,000", "$10,500", "$11,800"} + }; + + for (int i = 0; i < salesData.length; i++) { + Row dataRow = table2.createRow(15f); + dataRow.createCell(40, salesData[i][0]); + dataRow.createCell(20, salesData[i][1]); + dataRow.createCell(20, salesData[i][2]); + dataRow.createCell(20, salesData[i][3]); + + // Right-align numeric columns + for (int j = 1; j < 4; j++) { + dataRow.getCells().get(j).setAlign(HorizontalAlignment.RIGHT); + } + + if (i % 2 == 0) { + for (Cell cell : dataRow.getCells()) { + cell.setFillColor(new Color(236, 240, 241)); + } + } + } + + // Draw the second table + newYStart = table2.draw() - 30; + if (table2.getCurrentPage() != page) { + page = table2.getCurrentPage(); + } + + // Section 3: Multi-Page Table with Header Repetition + BaseTable table3 = new BaseTable(newYStart, yStartNewPage, bottomMargin, + tableWidth, margin, document, page, + true, true); + + Row multiTitle = table3.createRow(20f); + Cell multiTitleCell = multiTitle.createCell(100, "Multi-Page Table with Repeating Headers"); + multiTitleCell.setFillColor(new Color(44, 62, 80)); + multiTitleCell.setTextColor(Color.WHITE); + multiTitleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + multiTitleCell.setAlign(HorizontalAlignment.CENTER); + + Row multiDesc = table3.createRow(15f); + Cell multiDescCell = multiDesc.createCell(100, + "This table has many rows. The header will repeat on the next page."); + multiDescCell.setFillColor(new Color(236, 240, 241)); + + Row multiHeader = table3.createRow(15f); + multiHeader.createCell(10, "#"); + multiHeader.createCell(30, "Item Name"); + multiHeader.createCell(30, "Description"); + multiHeader.createCell(15, "Quantity"); + multiHeader.createCell(15, "Price"); + styleHeaderRow(multiHeader); + table3.addHeaderRow(multiHeader); + + // Generate many rows to force page break + for (int i = 1; i <= 60; i++) { + Row dataRow = table3.createRow(15f); + dataRow.createCell(10, String.valueOf(i)); + dataRow.createCell(30, "Item " + i); + dataRow.createCell(30, "Description for item " + i); + dataRow.createCell(15, String.valueOf(i * 10)); + dataRow.createCell(15, "$" + String.format("%.2f", i * 9.99)); + + // Right-align numeric columns + dataRow.getCells().get(3).setAlign(HorizontalAlignment.RIGHT); + dataRow.getCells().get(4).setAlign(HorizontalAlignment.RIGHT); + + if (i % 2 == 0) { + for (Cell cell : dataRow.getCells()) { + cell.setFillColor(new Color(236, 240, 241)); + } + } + } + + table3.draw(); + + // Save the document + File file = new File("target/tutorials/Tutorial07_HeaderRows.pdf"); + System.out.println("Tutorial 07 PDF saved at: " + file.getAbsolutePath()); + file.getParentFile().mkdirs(); + document.save(file); + document.close(); + } + + /** + * Helper method to add a section header + */ + private void addSectionHeader(BaseTable table, String title) throws IOException { + Row sectionRow = table.createRow(18f); + Cell sectionCell = sectionRow.createCell(100, title); + sectionCell.setFillColor(new Color(44, 62, 80)); + sectionCell.setTextColor(Color.WHITE); + sectionCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + sectionCell.setAlign(HorizontalAlignment.CENTER); + } + + /** + * Helper method to style header rows + */ + private void styleHeaderRow(Row row) { + Color headerColor = new Color(52, 73, 94); + for (Cell cell : row.getCells()) { + cell.setFillColor(headerColor); + cell.setTextColor(Color.WHITE); + cell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + } + } +} diff --git a/src/test/java/be/quodlibet/boxable/tutorial/Tutorial08_DataImport.java b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial08_DataImport.java new file mode 100644 index 0000000..7ee3bb3 --- /dev/null +++ b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial08_DataImport.java @@ -0,0 +1,260 @@ +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 be.quodlibet.boxable.datatable.DataTable; +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; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Tutorial 08: Data Import + * + * This tutorial demonstrates: + * - Importing data from CSV strings + * - Importing data from Java Lists + * - Column width customization + * - Data formatting + * - Header handling + */ +public class Tutorial08_DataImport { + + @Test + public void demonstrateDataImport() throws IOException { + // Initialize PDF Document + PDDocument document = new PDDocument(); + PDPage page = new PDPage(PDRectangle.A4); + page.setMediaBox(new PDRectangle(PDRectangle.A4.getHeight(), PDRectangle.A4.getWidth())); // Landscape + 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; + + // Title + BaseTable titleTable = new BaseTable(yStart, yStartNewPage, bottomMargin, + tableWidth, margin, document, page, + true, true); + + Row titleRow = titleTable.createRow(30f); + Cell titleCell = titleRow.createCell(100, "Tutorial 08: Data Import"); + titleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + titleCell.setFontSize(16f); + titleCell.setFillColor(new Color(52, 152, 219)); + titleCell.setTextColor(Color.WHITE); + titleCell.setAlign(HorizontalAlignment.CENTER); + + Row descRow = titleTable.createRow(20f); + Cell descCell = descRow.createCell(100, + "Learn how to import data from CSV and Lists into tables with customization options."); + descCell.setFillColor(new Color(236, 240, 241)); + descCell.setAlign(HorizontalAlignment.CENTER); + + yStart = titleTable.draw() - 30; + if (titleTable.getCurrentPage() != page) { + page = titleTable.getCurrentPage(); + } + + // Section 1: Import from List (Basic) + addSectionHeader(document, page, "Section 1: Import from List (Basic)", + yStart, yStartNewPage, bottomMargin, tableWidth, margin); + yStart -= 48; + + // Create sample data list + List listData = new ArrayList<>(); + listData.add(new ArrayList<>(Arrays.asList("Product", "Category", "Price", "Stock", "Status"))); + listData.add(new ArrayList<>(Arrays.asList("Laptop", "Electronics", "$999.99", "15", "In Stock"))); + listData.add(new ArrayList<>(Arrays.asList("Mouse", "Accessories", "$19.99", "120", "In Stock"))); + listData.add(new ArrayList<>(Arrays.asList("Keyboard", "Accessories", "$49.99", "85", "In Stock"))); + listData.add(new ArrayList<>(Arrays.asList("Monitor", "Electronics", "$299.99", "42", "In Stock"))); + listData.add(new ArrayList<>(Arrays.asList("USB Cable", "Accessories", "$9.99", "200", "In Stock"))); + + BaseTable listTable = new BaseTable(yStart, yStartNewPage, bottomMargin, + tableWidth, margin, document, page, + true, true); + DataTable dt1 = new DataTable(listTable, page); + dt1.addListToTable(listData, DataTable.HASHEADER); + + yStart = listTable.draw() - 30; + if (listTable.getCurrentPage() != page) { + page = listTable.getCurrentPage(); + } + + // Section 2: Import from CSV + addSectionHeader(document, page, "Section 2: Import from CSV", + yStart, yStartNewPage, bottomMargin, tableWidth, margin); + yStart -= 48; + + // Create sample CSV data + String csvData = "Name;Age;City;Country;Occupation\n" + + "John Smith;28;New York;USA;Engineer\n" + + "Jane Doe;32;London;UK;Designer\n" + + "Bob Johnson;45;Toronto;Canada;Manager\n" + + "Alice Williams;25;Sydney;Australia;Developer\n" + + "Charlie Brown;38;Berlin;Germany;Analyst"; + + BaseTable csvTable = new BaseTable(yStart, yStartNewPage, bottomMargin, + tableWidth, margin, document, page, + true, true); + DataTable dt2 = new DataTable(csvTable, page); + dt2.addCsvToTable(csvData, DataTable.HASHEADER, ';'); + + yStart = csvTable.draw() - 30; + if (csvTable.getCurrentPage() != page) { + page = csvTable.getCurrentPage(); + } + + // Section 3: Custom Column Widths + addSectionHeader(document, page, "Section 3: Custom Column Widths", + yStart, yStartNewPage, bottomMargin, tableWidth, margin); + yStart -= 48; + + // CSV with custom column widths + String csvData2 = "Description;Code;Value;Notes\n" + + "This is a very long description that needs more space;ABC;100;Short\n" + + "Another long description with details;DEF;200;Note\n" + + "More descriptive text here;GHI;300;Info"; + + BaseTable customWidthTable = new BaseTable(yStart, yStartNewPage, bottomMargin, + tableWidth, margin, document, page, + true, true); + // Custom column width ratios: Description (3x), Code (1x), Value (1x), Notes (1x) + DataTable dt3 = new DataTable(customWidthTable, page, Arrays.asList(3f, 1f, 1f, 1f)); + dt3.addCsvToTable(csvData2, DataTable.HASHEADER, ';'); + + yStart = customWidthTable.draw() - 30; + if (customWidthTable.getCurrentPage() != page) { + page = customWidthTable.getCurrentPage(); + } + + // Section 4: Styled Data Import + addSectionHeader(document, page, "Section 4: Styled Data Import", + yStart, yStartNewPage, bottomMargin, tableWidth, margin); + yStart -= 48; + + // Create styled table with custom headers + BaseTable styledTable = new BaseTable(yStart, yStartNewPage, bottomMargin, + tableWidth, margin, document, page, + true, true); + + // Add custom header rows before importing data + Row customHeader1 = styledTable.createRow(20f); + Cell header1Cell = customHeader1.createCell(100, "Monthly Sales Report - Q4 2024"); + header1Cell.setFillColor(new Color(41, 128, 185)); + header1Cell.setTextColor(Color.WHITE); + header1Cell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + header1Cell.setAlign(HorizontalAlignment.CENTER); + styledTable.addHeaderRow(customHeader1); + + Row customHeader2 = styledTable.createRow(15f); + Cell header2Cell = customHeader2.createCell(100, "Sales figures in USD"); + header2Cell.setFillColor(new Color(52, 152, 219)); + header2Cell.setTextColor(Color.WHITE); + header2Cell.setAlign(HorizontalAlignment.CENTER); + styledTable.addHeaderRow(customHeader2); + + // Now import data + String salesCsv = "Month;Revenue;Expenses;Profit;Growth\n" + + "October;$125,000;$45,000;$80,000;+12%\n" + + "November;$145,000;$48,000;$97,000;+16%\n" + + "December;$180,000;$52,000;$128,000;+24%"; + + DataTable dt4 = new DataTable(styledTable, page); + // Customize header cell styling + dt4.getHeaderCellTemplate().setFillColor(new Color(52, 73, 94)); + dt4.getHeaderCellTemplate().setTextColor(Color.WHITE); + + // Customize data cell styling for alternating rows + for (Cell cell : dt4.getDataCellTemplateEvenList()) { + cell.setFillColor(Color.WHITE); + } + for (Cell cell : dt4.getDataCellTemplateOddList()) { + cell.setFillColor(new Color(236, 240, 241)); + } + + dt4.addCsvToTable(salesCsv, DataTable.HASHEADER, ';'); + + yStart = styledTable.draw() - 30; + if (styledTable.getCurrentPage() != page) { + page = styledTable.getCurrentPage(); + } + + // Section 5: Large Dataset Import + addSectionHeader(document, page, "Section 5: Large Dataset Import", + yStart, yStartNewPage, bottomMargin, tableWidth, margin); + yStart -= 48; + + // Generate large dataset + List largeData = new ArrayList<>(); + largeData.add(new ArrayList<>(Arrays.asList("ID", "Name", "Department", "Email", "Phone"))); + + for (int i = 1; i <= 30; i++) { + largeData.add(new ArrayList<>(Arrays.asList( + String.valueOf(i), + "Employee " + i, + "Dept " + ((i % 5) + 1), + "employee" + i + "@company.com", + "555-" + String.format("%04d", i) + ))); + } + + BaseTable largeTable = new BaseTable(yStart, yStartNewPage, bottomMargin, + tableWidth, margin, document, page, + true, true); + DataTable dt5 = new DataTable(largeTable, page); + + // Style the large table + dt5.getHeaderCellTemplate().setFillColor(new Color(155, 89, 182)); + dt5.getHeaderCellTemplate().setTextColor(Color.WHITE); + for (Cell cell : dt5.getDataCellTemplateEvenList()) { + cell.setFillColor(Color.WHITE); + } + for (Cell cell : dt5.getDataCellTemplateOddList()) { + cell.setFillColor(new Color(250, 242, 255)); + } + + dt5.addListToTable(largeData, DataTable.HASHEADER); + + largeTable.draw(); + + // Save the document + File file = new File("target/tutorials/Tutorial08_DataImport.pdf"); + System.out.println("Tutorial 08 PDF saved at: " + file.getAbsolutePath()); + file.getParentFile().mkdirs(); + document.save(file); + document.close(); + } + + /** + * Helper method to add a section header + */ + private void addSectionHeader(PDDocument document, PDPage page, String title, + float yStart, float yStartNewPage, float bottomMargin, + float tableWidth, float margin) throws IOException { + BaseTable headerTable = new BaseTable(yStart, yStartNewPage, bottomMargin, + tableWidth, margin, document, page, + true, true); + Row sectionRow = headerTable.createRow(18f); + Cell sectionCell = sectionRow.createCell(100, title); + sectionCell.setFillColor(new Color(44, 62, 80)); + sectionCell.setTextColor(Color.WHITE); + sectionCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + sectionCell.setAlign(HorizontalAlignment.CENTER); + headerTable.draw(); + } +} diff --git a/src/test/java/be/quodlibet/boxable/tutorial/Tutorial09_MultiPageTables.java b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial09_MultiPageTables.java new file mode 100644 index 0000000..f601a6f --- /dev/null +++ b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial09_MultiPageTables.java @@ -0,0 +1,159 @@ +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 09: Multi-Page Tables + * + * This tutorial demonstrates: + * - Automatic page breaks + * - Large content handling across multiple pages + * - Header repetition on new pages + * - Managing page transitions + */ +public class Tutorial09_MultiPageTables { + + @Test + public void demonstrateMultiPageTables() 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 titleRow = table.createRow(30f); + Cell titleCell = titleRow.createCell(100, "Tutorial 09: Multi-Page Tables"); + titleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + titleCell.setFontSize(16f); + titleCell.setFillColor(new Color(22, 160, 133)); // Turquoise background + titleCell.setTextColor(Color.WHITE); + titleCell.setAlign(HorizontalAlignment.CENTER); + + // Description + Row descRow = table.createRow(20f); + Cell descCell = descRow.createCell(100, + "Demonstrate how tables automatically span multiple pages with repeating headers."); + descCell.setFillColor(new Color(236, 240, 241)); + descCell.setAlign(HorizontalAlignment.CENTER); + + // Create repeating header + Row headerRow = table.createRow(20f); + headerRow.createCell(10, "#"); + headerRow.createCell(25, "Product Name"); + headerRow.createCell(25, "Description"); + headerRow.createCell(15, "Price"); + headerRow.createCell(10, "Qty"); + headerRow.createCell(15, "Total"); + styleHeaderRow(headerRow); + table.addHeaderRow(headerRow); + + // Generate 100 rows to force multiple page breaks + Color lightBlue = new Color(236, 240, 241); + Color white = Color.WHITE; + + for (int i = 1; i <= 100; i++) { + Row dataRow = table.createRow(15f); + + // Row color alternation + Color rowColor = (i % 2 == 0) ? white : lightBlue; + + Cell numCell = dataRow.createCell(10, String.valueOf(i)); + dataRow.createCell(25, "Product " + i); + dataRow.createCell(25, + "Description for product number " + i); + Cell priceCell = dataRow.createCell(15, + String.format("$%.2f", i * 9.99)); + Cell qtyCell = dataRow.createCell(10, String.valueOf(i % 50 + 1)); + Cell totalCell = dataRow.createCell(15, + String.format("$%.2f", (i * 9.99) * (i % 50 + 1))); + + // Apply styling + for (Cell cell : dataRow.getCells()) { + cell.setFillColor(rowColor); + } + + // Right-align numeric columns + numCell.setAlign(HorizontalAlignment.CENTER); + priceCell.setAlign(HorizontalAlignment.RIGHT); + qtyCell.setAlign(HorizontalAlignment.CENTER); + totalCell.setAlign(HorizontalAlignment.RIGHT); + + // Add section markers at page transitions + if (i % 25 == 0) { + Row sectionRow = table.createRow(20f); + Cell sectionCell = sectionRow.createCell(100, + String.format("=== Section %d (Items %d-%d) ===", + (i/25), (i-24), i)); + sectionCell.setFillColor(new Color(52, 152, 219)); + sectionCell.setTextColor(Color.WHITE); + sectionCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + sectionCell.setAlign(HorizontalAlignment.CENTER); + } + } + + // Summary row + Row summaryRow = table.createRow(20f); + Cell summaryLabel = summaryRow.createCell(85, "TOTAL ITEMS:"); + summaryLabel.setAlign(HorizontalAlignment.RIGHT); + summaryLabel.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + summaryLabel.setFillColor(new Color(46, 204, 113)); + summaryLabel.setTextColor(Color.WHITE); + + Cell summaryValue = summaryRow.createCell(15, "100"); + summaryValue.setAlign(HorizontalAlignment.CENTER); + summaryValue.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + summaryValue.setFillColor(new Color(46, 204, 113)); + summaryValue.setTextColor(Color.WHITE); + + // Draw the table + table.draw(); + + // Get statistics about the generated PDF + int pageCount = document.getNumberOfPages(); + System.out.println("Multi-page table generated " + pageCount + " pages"); + + // Save the document + File file = new File("target/tutorials/Tutorial09_MultiPageTables.pdf"); + System.out.println("Tutorial 09 PDF saved at: " + file.getAbsolutePath()); + file.getParentFile().mkdirs(); + document.save(file); + document.close(); + } + + /** + * Helper method to style header rows + */ + private void styleHeaderRow(Row row) { + Color headerColor = new Color(52, 73, 94); + for (Cell cell : row.getCells()) { + cell.setFillColor(headerColor); + cell.setTextColor(Color.WHITE); + cell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + } + } +} diff --git a/src/test/java/be/quodlibet/boxable/tutorial/Tutorial10_NestedTables.java b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial10_NestedTables.java new file mode 100644 index 0000000..ae6fd95 --- /dev/null +++ b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial10_NestedTables.java @@ -0,0 +1,297 @@ +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 10: Nested Tables + * + * This tutorial demonstrates: + * - Creating tables within cells using

HTML tag + * - Complex layouts with nested structures + * - Practical examples of nested tables + * - Formatting nested table content + */ +public class Tutorial10_NestedTables { + + @Test + public void demonstrateNestedTables() 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 titleRow = table.createRow(30f); + Cell titleCell = titleRow.createCell(100, "Tutorial 10: Nested Tables"); + titleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + titleCell.setFontSize(16f); + titleCell.setFillColor(new Color(211, 84, 0)); // Orange background + titleCell.setTextColor(Color.WHITE); + titleCell.setAlign(HorizontalAlignment.CENTER); + + // Description + Row descRow = table.createRow(20f); + Cell descCell = descRow.createCell(100, + "Learn how to create tables within cells using HTML
tags for complex layouts."); + descCell.setFillColor(new Color(236, 240, 241)); + descCell.setAlign(HorizontalAlignment.CENTER); + + // Section 1: Simple Nested Table + addSectionHeader(table, "Simple Nested Table"); + + Row simpleDesc = table.createRow(15f); + simpleDesc.createCell(100, "A basic example of a table within a cell:"); + + Row simpleRow = table.createRow(80f); + simpleRow.createCell(40, "Product Details"); + + // Create a cell with nested table using HTML + String nestedTableHtml = "
" + + "" + + "" + + "" + + "
Item:Widget A
Price:$99.99
Stock:Available
"; + Cell nestedCell = simpleRow.createTableCell(60, nestedTableHtml); + nestedCell.setFillColor(new Color(245, 245, 245)); + + // Section 2: Multiple Nested Tables + addSectionHeader(table, "Multiple Nested Tables"); + + Row multiDesc = table.createRow(15f); + multiDesc.createCell(100, "Multiple cells with nested tables:"); + + Row headerRow = table.createRow(15f); + headerRow.createCell(33.33f, "Product A"); + headerRow.createCell(33.33f, "Product B"); + headerRow.createCell(33.34f, "Product C"); + styleHeaderRow(headerRow); + + Row multiRow = table.createRow(70f); + + // First nested table + String table1 = "" + + "" + + "" + + "" + + "
Price:$50
Qty:10
Total:$500
"; + Cell cell1 = multiRow.createTableCell(33.33f, table1); + cell1.setFillColor(new Color(255, 243, 224)); + + // Second nested table + String table2 = "" + + "" + + "" + + "" + + "
Price:$75
Qty:8
Total:$600
"; + Cell cell2 = multiRow.createTableCell(33.33f, table2); + cell2.setFillColor(new Color(230, 245, 255)); + + // Third nested table + String table3 = "" + + "" + + "" + + "" + + "
Price:$100
Qty:5
Total:$500
"; + Cell cell3 = multiRow.createTableCell(33.34f, table3); + cell3.setFillColor(new Color(255, 235, 238)); + + // Section 3: Complex Nested Structure + addSectionHeader(table, "Complex Nested Structure"); + + Row complexDesc = table.createRow(15f); + complexDesc.createCell(100, "A more complex example with formatting:"); + + Row complexRow = table.createRow(100f); + + // Left cell: Regular content + Cell leftCell = complexRow.createCell(40, + "Order #12345

" + + "Customer: John Doe
" + + "Date: 2024-02-07
" + + "Status: Shipped"); + leftCell.setFillColor(new Color(236, 240, 241)); + + // Right cell: Nested table with order details + String orderTable = "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
Order Items
ItemQtyPrice
Widget A2$99.98
Widget B1$49.99
Widget C3$149.97
Total:$299.94
"; + Cell rightCell = complexRow.createTableCell(60, orderTable); + rightCell.setFillColor(Color.WHITE); + + // Section 4: Nested Tables with Lists + addSectionHeader(table, "Nested Tables Combined with Lists"); + + Row listDesc = table.createRow(15f); + listDesc.createCell(100, "Combining nested tables with HTML lists:"); + + Row listRow = table.createRow(90f); + + Cell featuresCell = listRow.createCell(50, + "Product Features:
" + + "
    " + + "
  • High quality
  • " + + "
  • Durable construction
  • " + + "
  • Easy to use
  • " + + "
  • Great value
  • " + + "
"); + featuresCell.setFillColor(new Color(255, 243, 224)); + + String specsTable = "Technical Specifications:
" + + "" + + "" + + "" + + "" + + "" + + "" + + "
Dimension:10x8x2 cm
Weight:500g
Material:Plastic
Color:Blue
Warranty:2 years
"; + Cell specsCell = listRow.createTableCell(50, specsTable); + specsCell.setFillColor(new Color(230, 245, 255)); + + // Section 5: Full Cell Area usage for Inner Table + // Force new page for this example section + table.draw(); + page = new PDPage(PDRectangle.A4); + document.addPage(page); + table = new BaseTable(yStart, yStartNewPage, bottomMargin, tableWidth, margin, document, page, true, true); + + addSectionHeader(table, "Full Cell Area Inner Table"); + + Row fullAreaDesc = table.createRow(15f); + fullAreaDesc.createCell(100, "Using the full cell area for the inner table without padding or margins:"); + + Row fullAreaRow = table.createRow(12f); + fullAreaRow.createCell(30, "Package Info"); + + String innerTableHtml = "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
PropertyValue
Tracking IDTRK-99887766
CarrierFastShip Logistics
ServicePriority Overnight
Weight2.5 kg
Dimensions30x20x15 cm
InsuranceFull Coverage
SignatureRequired
"; + + // Use the new convenience method + be.quodlibet.boxable.TableCell innerTableCell = fullAreaRow.createTableCell(70, innerTableHtml); + + // Configure cell to use full area + innerTableCell.setLeftPadding(0); + innerTableCell.setRightPadding(0); + innerTableCell.setTopPadding(0); + innerTableCell.setBottomPadding(0); + innerTableCell.setMarginBetweenElementsY(0); + innerTableCell.setLeftBorderStyle(null); + innerTableCell.setTopBorderStyle(null); + + // Configure inner table borders and layout + innerTableCell.setInnerTableBorders(false, false, false, false); + innerTableCell.setInnerTableInnerBorders(true, true); + innerTableCell.setInnerTableStartAtTop(true); + innerTableCell.setInnerTableBorderStyle(new be.quodlibet.boxable.line.LineStyle(Color.BLACK, 1)); + innerTableCell.setInnerTableCellPadding(2f, 2f, 2f, 2f); + + // Section 6: Practical Invoice Example + // Force new page for this example section + table.draw(); + page = new PDPage(PDRectangle.A4); + document.addPage(page); + table = new BaseTable(yStart, yStartNewPage, bottomMargin, tableWidth, margin, document, page, true, true); + + addSectionHeader(table, "Practical Example: Invoice"); + + Row invoiceHeader = table.createRow(60f); + + // Company info on left + String companyInfo = "

ACME Corp

" + + "123 Business St.
" + + "New York, NY 10001
" + + "Tel: (555) 123-4567
" + + "Email: info@acme.com"; + Cell companyCell = invoiceHeader.createCell(50, companyInfo); + companyCell.setFillColor(new Color(245, 245, 245)); + + // Invoice details on right + String invoiceDetails = "" + + "" + + "" + + "" + + "" + + "
Invoice #:INV-2024-001
Date:February 7, 2024
Due Date:March 7, 2024
Terms:Net 30
"; + Cell invoiceCell = invoiceHeader.createTableCell(50, invoiceDetails); + invoiceCell.setFillColor(Color.WHITE); + + // Bill to section + Row billToRow = table.createRow(50f); + String billToInfo = "Bill To:
" + + "Jane Smith
" + + "456 Customer Ave.
" + + "Boston, MA 02101"; + billToRow.createCell(100, billToInfo); + + // Draw the table + table.draw(); + + // Save the document + File file = new File("target/tutorials/Tutorial10_NestedTables.pdf"); + System.out.println("Tutorial 10 PDF saved at: " + file.getAbsolutePath()); + file.getParentFile().mkdirs(); + document.save(file); + document.close(); + } + + /** + * Helper method to add a section header + */ + private void addSectionHeader(BaseTable table, String title) throws IOException { + Row sectionRow = table.createRow(18f); + Cell sectionCell = sectionRow.createCell(100, title); + sectionCell.setFillColor(new Color(44, 62, 80)); + sectionCell.setTextColor(Color.WHITE); + sectionCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + sectionCell.setAlign(HorizontalAlignment.CENTER); + } + + /** + * Helper method to style header rows + */ + private void styleHeaderRow(Row row) { + Color headerColor = new Color(52, 73, 94); + for (Cell cell : row.getCells()) { + cell.setFillColor(headerColor); + cell.setTextColor(Color.WHITE); + cell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + } + } +} diff --git a/src/test/java/be/quodlibet/boxable/tutorial/Tutorial11_FixedHeightRows.java b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial11_FixedHeightRows.java new file mode 100644 index 0000000..e9ab9cf --- /dev/null +++ b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial11_FixedHeightRows.java @@ -0,0 +1,272 @@ +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 11: Fixed Height Rows + * + * This tutorial demonstrates: + * - Creating fixed-height rows + * - Auto-fit text in fixed-height cells + * - Flexible vs fixed height rows + * - Font size adjustment for fixed heights + */ +public class Tutorial11_FixedHeightRows { + + @Test + public void demonstrateFixedHeightRows() 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 titleRow = table.createRow(30f); + Cell titleCell = titleRow.createCell(100, "Tutorial 11: Fixed Height Rows"); + titleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + titleCell.setFontSize(16f); + titleCell.setFillColor(new Color(192, 57, 43)); // Dark red background + titleCell.setTextColor(Color.WHITE); + titleCell.setAlign(HorizontalAlignment.CENTER); + + // Description + Row descRow = table.createRow(20f); + Cell descCell = descRow.createCell(100, + "Learn how to create fixed-height rows where text automatically shrinks to fit."); + descCell.setFillColor(new Color(236, 240, 241)); + descCell.setAlign(HorizontalAlignment.CENTER); + + // Section 1: Flexible vs Fixed Height + addSectionHeader(table, "Flexible vs Fixed Height Comparison"); + + Row compHeader = table.createRow(15f); + compHeader.createCell(50, "Row Type"); + compHeader.createCell(50, "Behavior"); + styleHeaderRow(compHeader); + + Row flexDesc = table.createRow(15f); + flexDesc.createCell(50, "Flexible Row"); + flexDesc.createCell(50, "Height expands to fit content"); + + Row fixedDesc = table.createRow(15f); + fixedDesc.createCell(50, "Fixed Height Row"); + fixedDesc.createCell(50, "Text shrinks to fit specified height"); + + // Section 2: Flexible Row Example + addSectionHeader(table, "Flexible Row (Default Behavior)"); + + Row flexDesc2 = table.createRow(15f); + flexDesc2.createCell(100, "This row will grow to accommodate the content:"); + + // Flexible row - will expand + Row flexibleRow = table.createRow(12f); + Cell flexCell = flexibleRow.createCell(100, + "This is a flexible row with a lot of text content that would normally require " + + "more space than the initial 12pt height. The row will automatically expand to " + + "fit all of this text content while maintaining the specified font size."); + flexCell.setFillColor(new Color(174, 214, 241)); + flexCell.setFontSize(12f); + + // Section 3: Fixed Height Row Example + addSectionHeader(table, "Fixed Height Row (Text Shrinks to Fit)"); + + Row fixedDesc2 = table.createRow(15f); + fixedDesc2.createCell(100, "This row will NOT grow - text shrinks instead:"); + + // Fixed height row - text will shrink + Row fixedRow = table.createRow(12f); + fixedRow.setFixedHeight(true); + Cell fixedCell = fixedRow.createCell(100, + "This is a fixed-height row with the same amount of text content. " + + "Instead of expanding, the text will be automatically shrunk to fit " + + "within the specified 12pt height constraint. Notice the smaller font size."); + fixedCell.setFillColor(new Color(255, 243, 224)); + fixedCell.setFontSize(12f); + fixedCell.setTopPadding(1f); + fixedCell.setBottomPadding(1f); + + // Section 4: Multiple Fixed Height Rows + addSectionHeader(table, "Multiple Fixed Height Rows"); + + Row multiDesc = table.createRow(15f); + multiDesc.createCell(100, "Demonstrating various fixed heights:"); + + Row multiHeader = table.createRow(15f); + multiHeader.createCell(20, "Height"); + multiHeader.createCell(80, "Content"); + styleHeaderRow(multiHeader); + + // 10pt fixed height + Row fixed10 = table.createRow(10f); + fixed10.setFixedHeight(true); + fixed10.createCell(20, "10pt"); + Cell content10 = fixed10.createCell(80, + "Fixed at 10pt - Text automatically adjusts to fit this small height."); + content10.setFillColor(new Color(255, 235, 238)); + content10.setTopPadding(1f); + content10.setBottomPadding(1f); + + // 15pt fixed height + Row fixed15 = table.createRow(15f); + fixed15.setFixedHeight(true); + fixed15.createCell(20, "15pt"); + Cell content15 = fixed15.createCell(80, + "Fixed at 15pt - More space allows slightly larger text while still constraining the height."); + content15.setFillColor(new Color(230, 245, 255)); + content15.setTopPadding(1f); + content15.setBottomPadding(1f); + + // 20pt fixed height + Row fixed20 = table.createRow(20f); + fixed20.setFixedHeight(true); + fixed20.createCell(20, "20pt"); + Cell content20 = fixed20.createCell(80, + "Fixed at 20pt - Even more space available, text can be larger but still constrained by the fixed height."); + content20.setFillColor(new Color(255, 250, 240)); + content20.setTopPadding(1f); + content20.setBottomPadding(1f); + + // Section 5: Fixed Height Headers + addSectionHeader(table, "Fixed Height Header Rows"); + + Row headerDesc = table.createRow(15f); + headerDesc.createCell(100, "Headers can also use fixed heights for consistent appearance:"); + + // Create a table with fixed-height header + Row fixedHeader = table.createRow(12f); + fixedHeader.setFixedHeight(true); + Cell hCell1 = fixedHeader.createCell(33.33f, "Column A with Long Name"); + Cell hCell2 = fixedHeader.createCell(33.33f, "Column B Extended Title"); + Cell hCell3 = fixedHeader.createCell(33.34f, "Column C Description"); + hCell1.setFontSize(14f); + hCell2.setFontSize(14f); + hCell3.setFontSize(14f); + hCell1.setTopPadding(1f); + hCell2.setTopPadding(1f); + hCell3.setTopPadding(1f); + hCell1.setBottomPadding(1f); + hCell2.setBottomPadding(1f); + hCell3.setBottomPadding(1f); + styleHeaderRow(fixedHeader); + table.addHeaderRow(fixedHeader); + + // Add data rows + for (int i = 1; i <= 3; i++) { + Row dataRow = table.createRow(15f); + dataRow.createCell(33.33f, "Value A" + i); + dataRow.createCell(33.33f, "Value B" + i); + dataRow.createCell(33.34f, "Value C" + i); + + if (i % 2 == 0) { + for (Cell cell : dataRow.getCells()) { + cell.setFillColor(new Color(236, 240, 241)); + } + } + } + + // Section 6: Practical Use Case + addSectionHeader(table, "Practical Use Case: Compact Data Table"); + + Row practicalDesc = table.createRow(15f); + practicalDesc.createCell(100, + "Fixed heights are useful for creating compact tables with consistent row heights:"); + + // Compact table with fixed-height rows + Row compactHeader = table.createRow(12f); + compactHeader.setFixedHeight(true); + compactHeader.createCell(10, "ID"); + compactHeader.createCell(30, "Name"); + compactHeader.createCell(30, "Email"); + compactHeader.createCell(30, "Department"); + styleHeaderRow(compactHeader); + for (Cell cell : compactHeader.getCells()) { + cell.setFontSize(10f); + cell.setTopPadding(1f); + cell.setBottomPadding(1f); + } + + String[][] compactData = { + {"1", "John Smith", "john@company.com", "Engineering"}, + {"2", "Jane Doe", "jane@company.com", "Marketing"}, + {"3", "Bob Johnson", "bob@company.com", "Sales"}, + {"4", "Alice Williams", "alice@company.com", "HR"}, + {"5", "Charlie Brown", "charlie@company.com", "Finance"} + }; + + for (int i = 0; i < compactData.length; i++) { + Row compactRow = table.createRow(10f); + compactRow.setFixedHeight(true); + for (int j = 0; j < 4; j++) { + Cell cell = compactRow.createCell( + (j == 0) ? 10 : 30, + compactData[i][j] + ); + cell.setFontSize(9f); + cell.setTopPadding(1f); + cell.setBottomPadding(1f); + if (i % 2 == 0) { + cell.setFillColor(new Color(236, 240, 241)); + } + } + } + + // Draw the table + table.draw(); + + // Save the document + File file = new File("target/tutorials/Tutorial11_FixedHeightRows.pdf"); + System.out.println("Tutorial 11 PDF saved at: " + file.getAbsolutePath()); + file.getParentFile().mkdirs(); + document.save(file); + document.close(); + } + + /** + * Helper method to add a section header + */ + private void addSectionHeader(BaseTable table, String title) throws IOException { + Row sectionRow = table.createRow(18f); + Cell sectionCell = sectionRow.createCell(100, title); + sectionCell.setFillColor(new Color(44, 62, 80)); + sectionCell.setTextColor(Color.WHITE); + sectionCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + sectionCell.setAlign(HorizontalAlignment.CENTER); + } + + /** + * Helper method to style header rows + */ + private void styleHeaderRow(Row row) { + Color headerColor = new Color(52, 73, 94); + for (Cell cell : row.getCells()) { + cell.setFillColor(headerColor); + cell.setTextColor(Color.WHITE); + cell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + } + } +} diff --git a/src/test/java/be/quodlibet/boxable/tutorial/Tutorial12_AdvancedFeatures.java b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial12_AdvancedFeatures.java new file mode 100644 index 0000000..5dee46b --- /dev/null +++ b/src/test/java/be/quodlibet/boxable/tutorial/Tutorial12_AdvancedFeatures.java @@ -0,0 +1,305 @@ +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 12: Advanced Features + * + * This tutorial demonstrates: + * - Rotated text (90 degrees) + * - Line spacing adjustments + * - Colspan cells (HTML colspan attribute) + * - Mixed advanced features + * - Creative table layouts + */ +public class Tutorial12_AdvancedFeatures { + + @Test + public void demonstrateAdvancedFeatures() 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 titleRow = table.createRow(30f); + Cell titleCell = titleRow.createCell(100, "Tutorial 12: Advanced Features"); + titleCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + titleCell.setFontSize(16f); + titleCell.setFillColor(new Color(136, 78, 160)); // Purple background + titleCell.setTextColor(Color.WHITE); + titleCell.setAlign(HorizontalAlignment.CENTER); + + // Description + Row descRow = table.createRow(20f); + Cell descCell = descRow.createCell(100, + "Explore advanced Boxable features including rotated text, line spacing, and colspan."); + descCell.setFillColor(new Color(236, 240, 241)); + descCell.setAlign(HorizontalAlignment.CENTER); + + // Section 1: Rotated Text + addSectionHeader(table, "Rotated Text (90 Degrees)"); + + Row rotDesc = table.createRow(15f); + rotDesc.createCell(100, "Text can be rotated 90 degrees for vertical labels:"); + + Row rotRow = table.createRow(80f); + + Cell rotCell1 = rotRow.createCell(10, "VERTICAL"); + rotCell1.setTextRotated(true); + rotCell1.setAlign(HorizontalAlignment.CENTER); + rotCell1.setFillColor(new Color(174, 214, 241)); + + Cell rotCell2 = rotRow.createCell(10, "ROTATED"); + rotCell2.setTextRotated(true); + rotCell2.setAlign(HorizontalAlignment.CENTER); + rotCell2.setFillColor(new Color(255, 243, 224)); + + Cell normalCell = rotRow.createCell(80, + "Regular horizontal text

" + + "The cells on the left demonstrate rotated text, which is useful for " + + "column headers in tables where space is limited. This feature allows " + + "you to create compact table designs."); + normalCell.setFillColor(Color.WHITE); + + // Section 2: Colspan + addSectionHeader(table, "Colspan - Spanning Multiple Columns"); + + Row colspanDesc = table.createRow(15f); + colspanDesc.createCell(100, "Cells can span multiple columns using HTML colspan attribute:"); + + // Row with colspan + Row colspanRow1 = table.createRow(20f); + Cell spanCell = colspanRow1.createTableCell(100, + "" + + "" + + "" + + "" + + "
This cell spans 3 columns
Column 1Column 2Column 3
Spans 2 columnsColumn 3
"); + spanCell.setFillColor(new Color(245, 245, 245)); + + // Section 3: Line Spacing + addSectionHeader(table, "Line Spacing"); + + Row lineDesc = table.createRow(15f); + lineDesc.createCell(100, "Comparing different line spacing:"); + + Row lineHeader = table.createRow(15f); + lineHeader.createCell(50, "Normal Line Spacing"); + lineHeader.createCell(50, "With Line Breaks"); + styleHeaderRow(lineHeader); + + Row lineRow = table.createRow(60f); + + Cell normalSpacing = lineRow.createCell(50, + "This is text with normal line spacing. " + + "Multiple sentences are displayed with standard spacing between them. " + + "This is the default behavior."); + normalSpacing.setFillColor(new Color(230, 245, 255)); + + Cell customSpacing = lineRow.createCell(50, + "First line
" + + "Second line
" + + "Third line
" + + "Fourth line
" + + "Fifth line"); + customSpacing.setFillColor(new Color(255, 250, 240)); + + // Section 4: Complex Table Layout + addSectionHeader(table, "Complex Layout Example"); + + Row complexDesc = table.createRow(15f); + complexDesc.createCell(100, "Combining multiple advanced features:"); + + // Complex header with rotated text + Row complexHeader = table.createRow(60f); + + Cell rotHeader1 = complexHeader.createCell(10, "Q1"); + rotHeader1.setTextRotated(true); + rotHeader1.setAlign(HorizontalAlignment.CENTER); + rotHeader1.setFillColor(new Color(52, 152, 219)); + rotHeader1.setTextColor(Color.WHITE); + + Cell rotHeader2 = complexHeader.createCell(10, "Q2"); + rotHeader2.setTextRotated(true); + rotHeader2.setAlign(HorizontalAlignment.CENTER); + rotHeader2.setFillColor(new Color(52, 152, 219)); + rotHeader2.setTextColor(Color.WHITE); + + Cell rotHeader3 = complexHeader.createCell(10, "Q3"); + rotHeader3.setTextRotated(true); + rotHeader3.setAlign(HorizontalAlignment.CENTER); + rotHeader3.setFillColor(new Color(52, 152, 219)); + rotHeader3.setTextColor(Color.WHITE); + + Cell rotHeader4 = complexHeader.createCell(10, "Q4"); + rotHeader4.setTextRotated(true); + rotHeader4.setAlign(HorizontalAlignment.CENTER); + rotHeader4.setFillColor(new Color(52, 152, 219)); + rotHeader4.setTextColor(Color.WHITE); + + Cell summaryHeader = complexHeader.createCell(60, + "2024 Quarterly Sales Summary

" + + "This table demonstrates:
" + + "• Rotated text for quarter labels
" + + "• Nested tables for data
" + + "• HTML formatting"); + summaryHeader.setFillColor(new Color(174, 214, 241)); + + // Data row with nested table + Row complexData = table.createRow(80f); + + Cell q1Data = complexData.createTableCell(10, + "" + + "" + + "" + + "
Sales:
$100K
"); + q1Data.setAlign(HorizontalAlignment.CENTER); + q1Data.setFillColor(new Color(230, 245, 255)); + + Cell q2Data = complexData.createTableCell(10, + "" + + "" + + "" + + "
Sales:
$120K
"); + q2Data.setAlign(HorizontalAlignment.CENTER); + q2Data.setFillColor(new Color(230, 245, 255)); + + Cell q3Data = complexData.createTableCell(10, + "" + + "" + + "" + + "
Sales:
$135K
"); + q3Data.setAlign(HorizontalAlignment.CENTER); + q3Data.setFillColor(new Color(230, 245, 255)); + + Cell q4Data = complexData.createTableCell(10, + "" + + "" + + "" + + "
Sales:
$150K
"); + q4Data.setAlign(HorizontalAlignment.CENTER); + q4Data.setFillColor(new Color(230, 245, 255)); + + Cell summaryData = complexData.createTableCell(60, + "Annual Summary:

" + + "" + + "" + + "" + + "" + + "" + + "
Total Sales:$505,000
Growth Rate:+15%
Best Quarter:Q4
Target Achievement:102%
"); + summaryData.setFillColor(Color.WHITE); + + // Section 5: Feature Combination Matrix + addSectionHeader(table, "Feature Combination Matrix"); + + Row matrixDesc = table.createRow(15f); + matrixDesc.createCell(100, "A practical example combining various features:"); + + Row matrixHeader = table.createRow(40f); + matrixHeader.createCell(20, ""); + + Cell mh1 = matrixHeader.createCell(20, "Feature A"); + mh1.setTextRotated(true); + mh1.setAlign(HorizontalAlignment.CENTER); + mh1.setFillColor(new Color(52, 73, 94)); + mh1.setTextColor(Color.WHITE); + + Cell mh2 = matrixHeader.createCell(20, "Feature B"); + mh2.setTextRotated(true); + mh2.setAlign(HorizontalAlignment.CENTER); + mh2.setFillColor(new Color(52, 73, 94)); + mh2.setTextColor(Color.WHITE); + + Cell mh3 = matrixHeader.createCell(20, "Feature C"); + mh3.setTextRotated(true); + mh3.setAlign(HorizontalAlignment.CENTER); + mh3.setFillColor(new Color(52, 73, 94)); + mh3.setTextColor(Color.WHITE); + + Cell mh4 = matrixHeader.createCell(20, "Notes"); + mh4.setAlign(HorizontalAlignment.CENTER); + mh4.setFillColor(new Color(52, 73, 94)); + mh4.setTextColor(Color.WHITE); + + // Matrix data rows + String[][] matrixData = { + {"Product X", "Yes", "Yes", "No", "High priority"}, + {"Product Y", "Yes", "No", "Yes", "Medium priority"}, + {"Product Z", "No", "Yes", "Yes", "Low priority"} + }; + + for (int i = 0; i < matrixData.length; i++) { + Row mRow = table.createRow(20f); + for (int j = 0; j < 5; j++) { + Cell mCell = mRow.createCell(20, matrixData[i][j]); + if (j > 0 && j < 4) { + mCell.setAlign(HorizontalAlignment.CENTER); + } + if (i % 2 == 0) { + mCell.setFillColor(new Color(236, 240, 241)); + } + } + } + + // Draw the table + table.draw(); + + // Save the document + File file = new File("target/tutorials/Tutorial12_AdvancedFeatures.pdf"); + System.out.println("Tutorial 12 PDF saved at: " + file.getAbsolutePath()); + file.getParentFile().mkdirs(); + document.save(file); + document.close(); + } + + /** + * Helper method to add a section header + */ + private void addSectionHeader(BaseTable table, String title) throws IOException { + Row sectionRow = table.createRow(18f); + Cell sectionCell = sectionRow.createCell(100, title); + sectionCell.setFillColor(new Color(44, 62, 80)); + sectionCell.setTextColor(Color.WHITE); + sectionCell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + sectionCell.setAlign(HorizontalAlignment.CENTER); + } + + /** + * Helper method to style header rows + */ + private void styleHeaderRow(Row row) { + Color headerColor = new Color(52, 73, 94); + for (Cell cell : row.getCells()) { + cell.setFillColor(headerColor); + cell.setTextColor(Color.WHITE); + cell.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD)); + } + } +} diff --git a/src/test/java/be/quodlibet/boxable/tutorial/TutorialRunner.java b/src/test/java/be/quodlibet/boxable/tutorial/TutorialRunner.java new file mode 100644 index 0000000..1ead15f --- /dev/null +++ b/src/test/java/be/quodlibet/boxable/tutorial/TutorialRunner.java @@ -0,0 +1,216 @@ +package be.quodlibet.boxable.tutorial; + +import org.junit.Test; + +import java.io.File; + +/** + * Tutorial Runner + * + * This class runs all Boxable tutorials and generates all PDF outputs. + * Each tutorial demonstrates different features of the Boxable library. + * + * To run all tutorials, execute this class's main method or run the test. + * All generated PDFs will be saved in the target/tutorials/ directory. + */ +public class TutorialRunner { + + @Test + public void runAllTutorials() throws Exception { + System.out.println("========================================"); + System.out.println(" Boxable Tutorial Runner"); + System.out.println("========================================"); + System.out.println(); + + // Create output directory + File tutorialsDir = new File("target/tutorials"); + if (!tutorialsDir.exists()) { + tutorialsDir.mkdirs(); + System.out.println("Created output directory: " + tutorialsDir.getAbsolutePath()); + } + System.out.println(); + + // Track results + int totalTutorials = 12; + int successful = 0; + int failed = 0; + + System.out.println("Running tutorials..."); + System.out.println(); + + // Tutorial 01: Basic Table + try { + System.out.print("[1/12] Running Tutorial01_BasicTable... "); + new Tutorial01_BasicTable().createBasicTable(); + successful++; + System.out.println("✓ SUCCESS"); + } catch (Exception e) { + failed++; + System.out.println("✗ FAILED: " + e.getMessage()); + } + + // Tutorial 02: HTML Formatting + try { + System.out.print("[2/12] Running Tutorial02_HtmlFormatting... "); + new Tutorial02_HtmlFormatting().demonstrateHtmlFormatting(); + successful++; + System.out.println("✓ SUCCESS"); + } catch (Exception e) { + failed++; + System.out.println("✗ FAILED: " + e.getMessage()); + } + + // Tutorial 03: Colors and Transparency + try { + System.out.print("[3/12] Running Tutorial03_ColorsAndTransparency... "); + new Tutorial03_ColorsAndTransparency().demonstrateColorsAndTransparency(); + successful++; + System.out.println("✓ SUCCESS"); + } catch (Exception e) { + failed++; + System.out.println("✗ FAILED: " + e.getMessage()); + } + + // Tutorial 04: Alignment + try { + System.out.print("[4/12] Running Tutorial04_Alignment... "); + new Tutorial04_Alignment().demonstrateAlignment(); + successful++; + System.out.println("✓ SUCCESS"); + } catch (Exception e) { + failed++; + System.out.println("✗ FAILED: " + e.getMessage()); + } + + // Tutorial 05: Images + try { + System.out.print("[5/12] Running Tutorial05_Images... "); + new Tutorial05_Images().demonstrateImages(); + successful++; + System.out.println("✓ SUCCESS"); + } catch (Exception e) { + failed++; + System.out.println("✗ FAILED: " + e.getMessage()); + } + + // Tutorial 06: Borders and Styling + try { + System.out.print("[6/12] Running Tutorial06_BordersAndStyling... "); + new Tutorial06_BordersAndStyling().demonstrateBordersAndStyling(); + successful++; + System.out.println("✓ SUCCESS"); + } catch (Exception e) { + failed++; + System.out.println("✗ FAILED: " + e.getMessage()); + } + + // Tutorial 07: Header Rows + try { + System.out.print("[7/12] Running Tutorial07_HeaderRows... "); + new Tutorial07_HeaderRows().demonstrateHeaderRows(); + successful++; + System.out.println("✓ SUCCESS"); + } catch (Exception e) { + failed++; + System.out.println("✗ FAILED: " + e.getMessage()); + } + + // Tutorial 08: Data Import + try { + System.out.print("[8/12] Running Tutorial08_DataImport... "); + new Tutorial08_DataImport().demonstrateDataImport(); + successful++; + System.out.println("✓ SUCCESS"); + } catch (Exception e) { + failed++; + System.out.println("✗ FAILED: " + e.getMessage()); + } + + // Tutorial 09: Multi-Page Tables + try { + System.out.print("[9/12] Running Tutorial09_MultiPageTables... "); + new Tutorial09_MultiPageTables().demonstrateMultiPageTables(); + successful++; + System.out.println("✓ SUCCESS"); + } catch (Exception e) { + failed++; + System.out.println("✗ FAILED: " + e.getMessage()); + } + + // Tutorial 10: Nested Tables + try { + System.out.print("[10/12] Running Tutorial10_NestedTables... "); + new Tutorial10_NestedTables().demonstrateNestedTables(); + successful++; + System.out.println("✓ SUCCESS"); + } catch (Exception e) { + failed++; + System.out.println("✗ FAILED: " + e.getMessage()); + } + + // Tutorial 11: Fixed Height Rows + try { + System.out.print("[11/12] Running Tutorial11_FixedHeightRows... "); + new Tutorial11_FixedHeightRows().demonstrateFixedHeightRows(); + successful++; + System.out.println("✓ SUCCESS"); + } catch (Exception e) { + failed++; + System.out.println("✗ FAILED: " + e.getMessage()); + } + + // Tutorial 12: Advanced Features + try { + System.out.print("[12/12] Running Tutorial12_AdvancedFeatures... "); + new Tutorial12_AdvancedFeatures().demonstrateAdvancedFeatures(); + successful++; + System.out.println("✓ SUCCESS"); + } catch (Exception e) { + failed++; + System.out.println("✗ FAILED: " + e.getMessage()); + } + + // Print summary + System.out.println(); + System.out.println("========================================"); + System.out.println(" Summary"); + System.out.println("========================================"); + System.out.println("Total tutorials: " + totalTutorials); + System.out.println("Successful: " + successful); + System.out.println("Failed: " + failed); + System.out.println(); + System.out.println("Output directory: " + tutorialsDir.getAbsolutePath()); + System.out.println(); + + // List generated files + System.out.println("Generated PDF files:"); + File[] pdfFiles = tutorialsDir.listFiles((dir, name) -> name.endsWith(".pdf")); + if (pdfFiles != null && pdfFiles.length > 0) { + for (File pdfFile : pdfFiles) { + System.out.println(" - " + pdfFile.getName() + + " (" + String.format("%.2f", pdfFile.length() / 1024.0) + " KB)"); + } + } else { + System.out.println(" (No PDF files found)"); + } + + System.out.println(); + System.out.println("========================================"); + System.out.println(" Tutorial runner completed!"); + System.out.println("========================================"); + } + + /** + * Main method to run all tutorials from command line + */ + public static void main(String[] args) { + try { + TutorialRunner runner = new TutorialRunner(); + runner.runAllTutorials(); + } catch (Exception e) { + System.err.println("Error running tutorials: " + e.getMessage()); + e.printStackTrace(); + System.exit(1); + } + } +}