/* * Example_21.cs * * Copyright (c) 2026 PDFjet Software * Licensed under the MIT License. See LICENSE file in the project root. */ using System; using System.IO; using System.Collections.Generic; using System.Diagnostics; using PDFjet.NET; /** * Example_21.cs * * Draws two tables. The first, of reading scores, has two header rows, a * header cell that spans two columns, and cells with background colors. The * second spans its cells over columns, with setColSpan, and over rows, with * setRowSpan. */ public class Example_21 { Font font = null; public Example_21() { PDF pdf = new PDF(new BufferedStream( new FileStream("Example_21.pdf", FileMode.Create))); pdf.SetCompliance(Compliance.PDF_UA_1); pdf.SetTitle("Table Headers and Spans"); DrawHeaderRows(pdf); DrawSpans(pdf); pdf.Complete(); } private void DrawHeaderRows(PDF pdf) { Font f1 = new Font(pdf, IBMPlexSans.Bold); f1.SetSize(7f); Font f2 = new Font(pdf, IBMPlexSans.Regular); f2.SetSize(7f); List> tableData = new List>(); StreamReader reader = new StreamReader( new FileStream("data/winter-2009.txt", FileMode.Open, FileAccess.Read)); String line; while ((line = reader.ReadLine()) != null) { List row = new List(); String[] columns = line.Split(new Char[] {'|'}); // C# will not let this be named "column" like the Java original - // that name is used later in the method. foreach (String col in columns) { row.Add(new Cell(f2, col)); } tableData.Add(row); } reader.Close(); Table table = new Table(); table.SetTableData(tableData, 2); table.SetLocation(100f, 50f); table.SetBottomMargin(10f); table.SetFontInRow(0, f1); table.SetFontInRow(1, f1); table.AutoAdjustColumnWidths(); table.RemoveLineBetweenRows(0, 1); Cell cell = table.GetCellAt(1, 1); cell.SetBorder(Border.TOP, true); cell = table.GetCellAt(1, 2); cell.SetBorder(Border.TOP, true); cell = table.GetCellAt(0, 1); cell.SetColSpan(2); cell.SetTextAlignment(Alignment.CENTER); List column = table.GetColumn(7); for (int i = 0; i < column.Count; i++) { cell = column[i]; cell.SetTextAlignment(Alignment.CENTER); } column = table.GetColumn(4); for (int i = 2; i < column.Count; i++) { cell = column[i]; try { cell.SetTextAlignment(Alignment.CENTER); if (Int32.Parse(cell.GetText()) > 40) { cell.SetBackgroundColor(Color.darkseagreen); } else { cell.SetBackgroundColor(Color.yellow); } } catch (Exception) { } } column = table.GetColumn(2); for (int i = 2; i < column.Count; i++) { cell = column[i]; try { if (cell.GetText().Equals("Smith")) { cell.SetUnderline(true); } if (cell.GetText().Equals("Bowden")) { cell.SetStrikeout(true); } } catch (Exception) { } } column = table.GetColumn(2); for (int i = 2; i < column.Count; i++) { cell = column[i]; try { if (cell.GetText().Equals("Bowden")) { cell.SetStrikeout(false); } } catch (Exception) { } } SetBgColorForRow(table, 0, Color.lightgray); SetBgColorForRow(table, 1, Color.lightgray); table.SetColumnWidth(3, 10f); BlankOutColumn(table, 3); table.SetColumnWidth(8, 10f); BlankOutColumn(table, 8); // A heading on the first page only, above the table: the first page is // made here, and the table goes on to new pages. Page first = new Page(pdf, Letter.PORTRAIT, false); new TextLine(f1, "Winter Reading Scores") .SetStructureType(StructElem.H1) .SetFontSize(16f) .SetLocation(100f, 38f) .DrawOn(first); table.SetFirstPageTopMargin(50f); List pages = new List(); pages.Add(first); table.DrawOn(pdf, first, pages, Letter.PORTRAIT); foreach (Page page in pages) { pdf.AddPage(page); } } private void DrawSpans(PDF pdf) { font = new Font(pdf, IBMPlexMono.Regular); Font f1 = new Font(pdf, IBMPlexSans.SemiBold); Font f2 = new Font(pdf, IBMPlexSans.Regular); Page page = new Page(pdf, Letter.LANDSCAPE); TextLine title = new TextLine(f1, "Table Cells That Span Rows and Columns"); title.SetStructureType(StructElem.H1); title.SetFontSize(18f); title.SetLocation(50f, 50f); title.DrawOn(page); TextBlock textBlock = new TextBlock(f2, "The cells of this table span up to five columns and up to four rows, as " + "the name in each cell says: 1x3 is one column wide and three rows tall. " + "A cell spans columns with setColSpan and rows with setRowSpan, and draws " + "its text, its background and its borders once over all of them. The table " + "keeps its shape, so every row holds a cell for every column and the cells " + "a span covers are left empty. The example is also a check of the geometry " + "of the cells: their backgrounds meet without gaps and their borders line " + "up. The header row above them numbers the columns."); textBlock.SetFontSize(11f); textBlock.SetLineSpacing(1.3f); textBlock.SetLocation(50f, 65f); textBlock.SetWidth(500f); float[] xy = textBlock.DrawOn(page); Table table = new Table(); table.SetTableData(CreateTableData(f1), 1); table.SetBottomMargin(10f); table.SetLocation(50f, xy[1] + 20f); table.DrawOn(page); } public void BlankOutColumn(Table table, int index) { List column = table.GetColumn(index); foreach (Cell cell in column) { cell.SetBackgroundColor(Color.white); cell.SetBorder(Border.TOP, false); cell.SetBorder(Border.BOTTOM, false); } } public void SetBgColorForRow(Table table, int index, int color) { List row = table.GetRow(index); foreach (Cell cell in row) { cell.SetBackgroundColor(color); } } /** * Returns a header row that numbers the columns, and under it the cells * of a 10 by 10 table whose cells span columns and rows. They are the * table of this HTML, cell for cell: *
     * <table border="solid">
     * <tr><td colspan="2" rowspan="2">2x2</td><td colspan="2">2x1</td>
     *     <td colspan="2">2x1</td><td colspan="2">2x1</td><td colspan="2">2x1</td></tr>
     * <tr><td colspan="2" rowspan="2">2x2</td><td>1x1</td><td colspan="5">5x1</td></tr>
     * <tr><td rowspan="2">1x2</td><td>1x1</td><td colspan="2" rowspan="2">2x2</td>
     *     <td rowspan="2">1x2</td><td colspan="3">3x1</td></tr>
     * <tr><td>1x1</td><td rowspan="3">1x3</td><td>1x1</td><td colspan="2">2x1</td>
     *     <td rowspan="2">1x2</td></tr>
     * <tr><td rowspan="2">1x2</td><td>1x1</td><td colspan="2">2x1</td>
     *     <td colspan="4" rowspan="4">4x4</td></tr>
     * <tr><td>1x1</td><td rowspan="3">1x3</td><td rowspan="3">1x3</td>
     *     <td rowspan="3">1x3</td></tr>
     * <tr><td rowspan="2">1x2</td><td>1x1</td><td rowspan="4">1x4</td></tr>
     * <tr><td>1x1</td></tr>
     * <tr><td rowspan="2">1x2</td><td>1x1</td><td colspan="2">2x1</td>
     *     <td colspan="2" rowspan="2">2x2</td><td rowspan="2">1x2</td><td>1x1</td>
     *     <td>1x1</td></tr>
     * <tr><td>1x1</td><td>1x1</td><td>1x1</td><td>1x1</td><td>1x1</td></tr>
     * </table>
     * 
* * @param headerFont the font of the header row, which numbers the columns. * @return the rows of the table. */ private List> CreateTableData(Font headerFont) { // The columns and the rows each cell spans, in the order a browser // reads the cells of the HTML above. int[][][] spans = { new int[][] {new int[] {2, 2}, new int[] {2, 1}, new int[] {2, 1}, new int[] {2, 1}, new int[] {2, 1}}, new int[][] {new int[] {2, 2}, new int[] {1, 1}, new int[] {5, 1}}, new int[][] {new int[] {1, 2}, new int[] {1, 1}, new int[] {2, 2}, new int[] {1, 2}, new int[] {3, 1}}, new int[][] {new int[] {1, 1}, new int[] {1, 3}, new int[] {1, 1}, new int[] {2, 1}, new int[] {1, 2}}, new int[][] {new int[] {1, 2}, new int[] {1, 1}, new int[] {2, 1}, new int[] {4, 4}}, new int[][] {new int[] {1, 1}, new int[] {1, 3}, new int[] {1, 3}, new int[] {1, 3}}, new int[][] {new int[] {1, 2}, new int[] {1, 1}, new int[] {1, 4}}, new int[][] {new int[] {1, 1}}, new int[][] {new int[] {1, 2}, new int[] {1, 1}, new int[] {2, 1}, new int[] {2, 2}, new int[] {1, 2}, new int[] {1, 1}, new int[] {1, 1}}, new int[][] {new int[] {1, 1}, new int[] {1, 1}, new int[] {1, 1}, new int[] {1, 1}, new int[] {1, 1}}, }; int columns = 10; Cell[][] grid = new Cell[spans.Length][]; for (int r = 0; r < spans.Length; r++) { grid[r] = new Cell[columns]; } for (int r = 0; r < spans.Length; r++) { int c = 0; foreach (int[] span in spans[r]) { // The next column that no cell of a row above spans over. while (c < columns && grid[r][c] != null) { c++; } grid[r][c] = GetCell(font, span[0], span[1], span[0] + "x" + span[1]); // A table keeps its shape, so every row holds a cell for every // column: the cells a span covers are there and are empty. for (int r2 = r; r2 < r + span[1] && r2 < spans.Length; r2++) { for (int c2 = c; c2 < c + span[0] && c2 < columns; c2++) { if (grid[r2][c2] == null) { grid[r2][c2] = GetCell(font, 1, 1, ""); } } } c += span[0]; } } List> rows = new List>(); // The header row, above the cells: it numbers the columns. List header = new List(); for (int c = 0; c < columns; c++) { header.Add(GetCell(headerFont, 1, 1, (c + 1).ToString())); } rows.Add(header); foreach (Cell[] row in grid) { rows.Add(new List(row)); } return rows; } private Cell GetCell(Font font, int colSpan, int rowSpan, String text) { Cell cell = new Cell(font); cell.SetColSpan(colSpan); cell.SetRowSpan(rowSpan); cell.SetWidth(50f); cell.SetText(text); cell.SetBorder(Border.TOP, true); cell.SetBorder(Border.BOTTOM, true); cell.SetBorder(Border.LEFT, true); cell.SetBorder(Border.RIGHT, true); cell.SetTextAlignment(Alignment.CENTER); cell.SetVerticalAlignment(Alignment.CENTER); cell.SetBackgroundColor(0xD8F0E4); // A pastel mint cell.SetBorderWidth(1f); return cell; } public static void Main(String[] args) { Stopwatch sw = Stopwatch.StartNew(); long time0 = sw.ElapsedMilliseconds; new Example_21(); long time1 = sw.ElapsedMilliseconds; sw.Stop(); Console.WriteLine($"Example_21 => {time1 - time0,4} ms"); } } // End of Example_21.cs