Nested tables

This is a code example of iText PDF, discover more.

31st May 2016
admin-marketing

Switch code for this example

NestedTableProblem.java
  1. /*
  2.  
  3.     This file is part of the iText (R) project.
  4.     Copyright (c) 1998-2016 iText Group NV
  5.  
  6. */
  7.  
  8. /**
  9.  * Example written by Bruno Lowagie in answer to:
  10.  * http://stackoverflow.com/questions/28418108/itext-how-to-add-an-inner-table-surrounded-by-text-to-a-table
  11.  */
  12. package com.itextpdf.samples.sandbox.tables;
  13.  
  14. import com.itextpdf.kernel.color.Color;
  15. import com.itextpdf.kernel.geom.PageSize;
  16. import com.itextpdf.kernel.pdf.PdfDocument;
  17. import com.itextpdf.kernel.pdf.PdfWriter;
  18. import com.itextpdf.layout.Document;
  19. import com.itextpdf.layout.border.SolidBorder;
  20. import com.itextpdf.layout.element.Cell;
  21. import com.itextpdf.layout.element.Table;
  22. import com.itextpdf.layout.property.HorizontalAlignment;
  23. import com.itextpdf.samples.GenericTest;
  24. import com.itextpdf.test.annotations.type.SampleTest;
  25.  
  26. import org.junit.experimental.categories.Category;
  27.  
  28. import java.io.File;
  29.  
  30. @Category(SampleTest.class)
  31. public class NestedTableProblem extends GenericTest {
  32.     public static final String DEST = "./target/test/resources/sandbox/tables/nested_table_problem.pdf";
  33.  
  34.     public static void main(String[] args) throws Exception {
  35.         File file = new File(DEST);
  36.         file.getParentFile().mkdirs();
  37.         new NestedTableProblem().manipulatePdf(DEST);
  38.     }
  39.  
  40.     @Override
  41.     protected void manipulatePdf(String dest) throws Exception {
  42.         PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
  43.         Document doc = new Document(pdfDoc, new PageSize(612, 792));
  44.         doc.setMargins(30, 21, 35, 21);
  45.  
  46.         // table 2
  47.         Table table2 = new Table(1);
  48.         table2.setHorizontalAlignment(HorizontalAlignment.LEFT);
  49.         table2.addCell(new Cell().setBorder(new SolidBorder(Color.RED, 1)).add("Goodbye World"));
  50.         table2.setWidthPercent(80);
  51.         // table 1
  52.         Table table1 = new Table(1);
  53.         table1.setHorizontalAlignment(HorizontalAlignment.LEFT);
  54.         Cell cell = new Cell();
  55.         cell.setBorder(new SolidBorder(Color.BLACK, 1));
  56.         cell.add("Hello World");
  57.         cell.add(table2);
  58.         cell.add("Hello World");
  59.         table1.addCell(cell);
  60.         doc.add(table1);
  61.  
  62.         doc.close();
  63.     }
  64. }
NestedTableRoundedBorder.java
  1. /*
  2.  
  3.     This file is part of the iText (R) project.
  4.     Copyright (c) 1998-2016 iText Group NV
  5.  
  6. */
  7.  
  8. /**
  9.  * This example was written in answer to the following question:
  10.  * http://stackoverflow.com/questions/31330062/need-to-make-pdf-sample-with-boxes-as-table-columns-by-android-app
  11.  */
  12. package com.itextpdf.samples.sandbox.tables;
  13.  
  14. import com.itextpdf.kernel.pdf.PdfDocument;
  15. import com.itextpdf.kernel.pdf.PdfWriter;
  16. import com.itextpdf.layout.Document;
  17. import com.itextpdf.layout.border.Border;
  18. import com.itextpdf.layout.element.Cell;
  19. import com.itextpdf.layout.element.Table;
  20. import com.itextpdf.layout.renderer.CellRenderer;
  21. import com.itextpdf.layout.renderer.DrawContext;
  22. import com.itextpdf.samples.GenericTest;
  23. import com.itextpdf.test.annotations.type.SampleTest;
  24.  
  25. import org.junit.experimental.categories.Category;
  26.  
  27. import java.io.File;
  28.  
  29. @Category(SampleTest.class)
  30. public class NestedTableRoundedBorder extends GenericTest {
  31.     public static final String DEST = "./target/test/resources/sandbox/tables/nested_table_rounded_border.pdf";
  32.  
  33.     public static void main(String[] args) throws Exception {
  34.         File file = new File(DEST);
  35.         file.getParentFile().mkdirs();
  36.         new NestedTableRoundedBorder().manipulatePdf(DEST);
  37.     }
  38.  
  39.     @Override
  40.     protected void manipulatePdf(String dest) throws Exception {
  41.         PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
  42.         Document doc = new Document(pdfDoc);
  43.  
  44.         Cell cell;
  45.         // outer table
  46.         Table outertable = new Table(1);
  47.         // inner table 1
  48.         Table innertable = new Table(new float[]{8, 12, 1, 4, 12});
  49.         innertable.setWidthPercent(100);
  50.         // first row
  51.         // column 1
  52.         cell = new Cell().add("Record Ref:");
  53.         cell.setBorder(Border.NO_BORDER);
  54.         innertable.addCell(cell);
  55.         // column 2
  56.         cell = new Cell().add("GN Staff");
  57.         cell.setPaddingLeft(2);
  58.         innertable.addCell(cell);
  59.         // column 3
  60.         cell = new Cell();
  61.         cell.setBorder(Border.NO_BORDER);
  62.         innertable.addCell(cell);
  63.         // column 4
  64.         cell = new Cell().add("Date: ");
  65.         cell.setBorder(Border.NO_BORDER);
  66.         innertable.addCell(cell);
  67.         // column 5
  68.         cell = new Cell().add("30/4/2015");
  69.         cell.setPaddingLeft(2);
  70.         innertable.addCell(cell);
  71.         // spacing
  72.         cell = new Cell(1, 5);
  73.         cell.setHeight(3);
  74.         cell.setBorder(Border.NO_BORDER);
  75.         innertable.addCell(cell);
  76.         // second row
  77.         // column 1
  78.         cell = new Cell().add("Hospital:");
  79.         cell.setBorder(Border.NO_BORDER);
  80.         innertable.addCell(cell);
  81.         // column 2
  82.         cell = new Cell().add("Derby Royal");
  83.         cell.setPaddingLeft(2);
  84.         innertable.addCell(cell);
  85.         // column 3
  86.         cell = new Cell();
  87.         cell.setBorder(Border.NO_BORDER);
  88.         innertable.addCell(cell);
  89.         // column 4
  90.         cell = new Cell().add("Ward: ");
  91.         cell.setBorder(Border.NO_BORDER);
  92.         cell.setPaddingLeft(5);
  93.         innertable.addCell(cell);
  94.         // column 5
  95.         cell = new Cell().add("21");
  96.         cell.setPaddingLeft(2);
  97.         innertable.addCell(cell);
  98.         // spacing
  99.         cell = new Cell(1, 5);
  100.         cell.setHeight(3);
  101.         cell.setBorder(Border.NO_BORDER);
  102.         innertable.addCell(cell);
  103.         // first nested table
  104.         cell = new Cell().add(innertable);
  105.         cell.setNextRenderer(new RoundedBorderCellRenderer(cell));
  106.         cell.setBorder(Border.NO_BORDER);
  107.         cell.setPadding(8);
  108.         outertable.addCell(cell);
  109.         // inner table 2
  110.         innertable = new Table(new float[]{3, 17, 1, 16});
  111.         innertable.setWidthPercent(100);
  112.         // first row
  113.         // column 1
  114.         cell = new Cell();
  115.         cell.setBorder(Border.NO_BORDER);
  116.         innertable.addCell(cell);
  117.         // column 2
  118.         cell = new Cell().add("Name");
  119.         cell.setBorder(Border.NO_BORDER);
  120.         innertable.addCell(cell);
  121.         // column 3
  122.         cell = new Cell();
  123.         cell.setBorder(Border.NO_BORDER);
  124.         innertable.addCell(cell);
  125.         // column 4
  126.         cell = new Cell().add("Signature: ");
  127.         cell.setBorder(Border.NO_BORDER);
  128.         innertable.addCell(cell);
  129.         // spacing
  130.         cell = new Cell(1, 4);
  131.         cell.setHeight(3);
  132.         cell.setBorder(Border.NO_BORDER);
  133.         innertable.addCell(cell);
  134.         // subsequent rows
  135.         for (int i = 1; i < 4; i++) {
  136.             // column 1
  137.             cell = new Cell().add(String.format("%s:", i));
  138.             cell.setBorder(Border.NO_BORDER);
  139.             innertable.addCell(cell);
  140.             // column 2
  141.             cell = new Cell();
  142.             innertable.addCell(cell);
  143.             // column 3
  144.             cell = new Cell();
  145.             cell.setBorder(Border.NO_BORDER);
  146.             innertable.addCell(cell);
  147.             // column 4
  148.             cell = new Cell();
  149.             innertable.addCell(cell);
  150.             // spacing
  151.             cell = new Cell(1, 4);
  152.             cell.setHeight(3);
  153.             cell.setBorder(Border.NO_BORDER);
  154.             innertable.addCell(cell);
  155.         }
  156.         // second nested table
  157.         cell = new Cell().add(innertable);
  158.         cell.setNextRenderer(new RoundedBorderCellRenderer(cell));
  159.         cell.setBorder(Border.NO_BORDER);
  160.         cell.setPaddingLeft(8);
  161.         cell.setPaddingTop(8);
  162.         cell.setPaddingRight(8);
  163.         cell.setPaddingBottom(8);
  164.         outertable.addCell(cell);
  165.         // add the table
  166.         doc.add(outertable);
  167.  
  168.         doc.close();
  169.     }
  170.  
  171.  
  172.     private class RoundedBorderCellRenderer extends CellRenderer {
  173.         public RoundedBorderCellRenderer(Cell modelElement) {
  174.             super(modelElement);
  175.         }
  176.  
  177.         @Override
  178.         public void draw(DrawContext drawContext) {
  179.             drawContext.getCanvas().roundRectangle(getOccupiedAreaBBox().getX() + 1.5f, getOccupiedAreaBBox().getY() + 1.5f,
  180.                     getOccupiedAreaBBox().getWidth() - 3, getOccupiedAreaBBox().getHeight() - 3, 4);
  181.             drawContext.getCanvas().stroke();
  182.             super.draw(drawContext);
  183.         }
  184.     }
  185. }
NestedTables.java
  1. /*
  2.  
  3.     This file is part of the iText (R) project.
  4.     Copyright (c) 1998-2016 iText Group NV
  5.  
  6. */
  7.  
  8. /**
  9.  * This example was written by Bruno Lowagie in answer to the following questions:
  10.  * http://stackoverflow.com/questions/24562448/the-table-width-must-be-greater-than-zero-exception-when-using-nested-tables
  11.  * and
  12.  * http://stackoverflow.com/questions/28444598/nested-table-stretches
  13.  */
  14. package com.itextpdf.samples.sandbox.tables;
  15.  
  16. import com.itextpdf.kernel.geom.PageSize;
  17. import com.itextpdf.kernel.pdf.PdfDocument;
  18. import com.itextpdf.kernel.pdf.PdfWriter;
  19. import com.itextpdf.layout.Document;
  20. import com.itextpdf.layout.element.Cell;
  21. import com.itextpdf.layout.element.Paragraph;
  22. import com.itextpdf.layout.element.Table;
  23. import com.itextpdf.samples.GenericTest;
  24. import com.itextpdf.test.annotations.type.SampleTest;
  25.  
  26. import org.junit.experimental.categories.Category;
  27.  
  28. import java.io.File;
  29.  
  30. @Category(SampleTest.class)
  31. public class NestedTables extends GenericTest {
  32.     public static final String DEST = "./target/test/resources/sandbox/tables/nested_tables.pdf";
  33.  
  34.     public static void main(String[] args) throws Exception {
  35.         File file = new File(DEST);
  36.         file.getParentFile().mkdirs();
  37.         new NestedTables().manipulatePdf(DEST);
  38.     }
  39.  
  40.     @Override
  41.     protected void manipulatePdf(String dest) throws Exception {
  42.         PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
  43.         // Note that it is not necessary to create new PageSize object,
  44.         // but for testing reasons (connected to parallelization) we call constructor here
  45.         Document doc = new Document(pdfDoc, new PageSize(PageSize.A4).rotate());
  46.  
  47.         float[] columnWidths = {150, 40, 90, 51, 35, 25, 35, 35, 35, 32, 32, 33, 35, 60, 46, 26};
  48.         Table table = new Table(columnWidths);
  49.         table.setWidth(770F);
  50.         buildNestedTables(table);
  51.         doc.add(new Paragraph("Add table straight to another table"));
  52.         doc.add(table);
  53.  
  54.         // IMPORTANT!!!
  55.         // Two other examples (with methods buildNestedTables1 and buildNestedTables2)
  56.         // make no sense in itext7 because there is only one way of adding cells to tables
  57.         // in itext7. Please, check NestedTables.java in itext5.
  58.  
  59.         doc.close();
  60.     }
  61.  
  62.     private void buildNestedTables(Table outerTable) {
  63.         Table innerTable1 = new Table(1);
  64.         Table innerTable2 = new Table(2);
  65.         Cell cell;
  66.         innerTable1.addCell("Cell 1");
  67.         innerTable1.addCell("Cell 2");
  68.         outerTable.addCell(innerTable1);
  69.         innerTable2.addCell("Cell 3");
  70.         innerTable2.addCell("Cell 4");
  71.         outerTable.addCell(innerTable2);
  72.         cell = new Cell(1, 14);
  73.         outerTable.addCell(cell);
  74.     }
  75. }
NestedTables2.java
  1. /*
  2.  
  3.     This file is part of the iText (R) project.
  4.     Copyright (c) 1998-2016 iText Group NV
  5.  
  6. */
  7.  
  8. /**
  9.  * This example was written by Bruno Lowagie in answer to the following questions:
  10.  * http://stackoverflow.com/questions/28503491/large-table-in-table-cell-invoke-page-break
  11.  */
  12. package com.itextpdf.samples.sandbox.tables;
  13.  
  14. import com.itextpdf.kernel.pdf.PdfDocument;
  15. import com.itextpdf.kernel.pdf.PdfWriter;
  16. import com.itextpdf.layout.Document;
  17. import com.itextpdf.layout.element.Table;
  18. import com.itextpdf.samples.GenericTest;
  19. import com.itextpdf.test.annotations.type.SampleTest;
  20.  
  21. import org.junit.experimental.categories.Category;
  22.  
  23. import java.io.File;
  24.  
  25. @Category(SampleTest.class)
  26. public class NestedTables2 extends GenericTest {
  27.     public static final String DEST = "./target/test/resources/sandbox/tables/nested_tables2.pdf";
  28.  
  29.     public static void main(String[] args) throws Exception {
  30.         File file = new File(DEST);
  31.         file.getParentFile().mkdirs();
  32.         new NestedTables2().manipulatePdf(DEST);
  33.     }
  34.  
  35.     @Override
  36.     protected void manipulatePdf(String dest) throws Exception {
  37.         PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
  38.         Document doc = new Document(pdfDoc);
  39.  
  40.         Table table = new Table(new float[]{1, 15});
  41.         table.setWidthPercent(100);
  42.         for (int i = 1; i
NestedTables3.java
  1. /*
  2.  
  3.     This file is part of the iText (R) project.
  4.     Copyright (c) 1998-2016 iText Group NV
  5.  
  6. */
  7.  
  8. /**
  9.  * This example was written by Bruno Lowagie in answer to the following questions:
  10.  * http://stackoverflow.com/questions/31108488/pdfptable-header-repeat-when-data-in-a-different-table-increases-in-itext
  11.  */
  12. package com.itextpdf.samples.sandbox.tables;
  13.  
  14. import com.itextpdf.kernel.geom.PageSize;
  15. import com.itextpdf.kernel.geom.Rectangle;
  16. import com.itextpdf.kernel.pdf.PdfDocument;
  17. import com.itextpdf.kernel.pdf.PdfWriter;
  18. import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
  19. import com.itextpdf.layout.Document;
  20. import com.itextpdf.layout.element.Cell;
  21. import com.itextpdf.layout.element.Paragraph;
  22. import com.itextpdf.layout.element.Table;
  23. import com.itextpdf.layout.property.Property;
  24. import com.itextpdf.layout.renderer.AbstractRenderer;
  25. import com.itextpdf.layout.renderer.DrawContext;
  26. import com.itextpdf.layout.renderer.IRenderer;
  27. import com.itextpdf.layout.renderer.TableRenderer;
  28. import com.itextpdf.samples.GenericTest;
  29. import com.itextpdf.test.annotations.type.SampleTest;
  30.  
  31. import java.io.File;
  32.  
  33. import org.junit.experimental.categories.Category;
  34.  
  35. @Category(SampleTest.class)
  36. public class NestedTables3 extends GenericTest {
  37.     public static final String DEST = "./target/test/resources/sandbox/tables/nested_tables3.pdf";
  38.  
  39.     public static void main(String[] args) throws Exception {
  40.         File file = new File(DEST);
  41.         file.getParentFile().mkdirs();
  42.         new NestedTables3().manipulatePdf(DEST);
  43.     }
  44.  
  45.     @Override
  46.     protected void manipulatePdf(String dest) throws Exception {
  47.         PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
  48.         // Note that it is not necessary to create new PageSize object,
  49.         // but for testing reasons (connected to parallelization) we call constructor here
  50.         Document doc = new Document(pdfDoc, new PageSize(PageSize.A4).rotate());
  51.  
  52.         Table table = new Table(2);
  53.         table.setNextRenderer(new InnerTableRenderer(table, new Table.RowRange(0, 0)));
  54.         Cell cell = new Cell(1, 2).add("This outer header is repeated on every page");
  55.         table.addHeaderCell(cell);
  56.         Table inner1 = new Table(1);
  57.         cell = new Cell();
  58.         cell.setHeight(20);
  59.         inner1.addHeaderCell(cell);
  60.         cell = new Cell().add("This inner header won't be repeated on every page");
  61.         inner1.addHeaderCell(cell);
  62.         for (int i = 0; i < 10; i++) {
  63.             inner1.addCell(new Cell().add(new Paragraph("test")));
  64.         }
  65.         cell = new Cell().add(inner1);
  66.         table.addCell(cell.setPadding(0));
  67.         Table inner2 = new Table(1);
  68.         cell = new Cell();
  69.         cell.setHeight(20);
  70.         inner2.addHeaderCell(cell);
  71.         cell = new Cell().add("This inner may be repeated on every page");
  72.         inner2.addHeaderCell(cell);
  73.         for (int i = 0; i < 35; i++) {
  74.             inner2.addCell("test");
  75.         }
  76.         cell = new Cell().add(inner2);
  77.         table.addCell(cell.setPadding(0));
  78.         doc.add(table);
  79.  
  80.         doc.close();
  81.     }
  82.  
  83.     private class InnerTableRenderer extends TableRenderer {
  84.         public InnerTableRenderer(Table modelElement, Table.RowRange rowRange) {
  85.             super(modelElement, rowRange);
  86.         }
  87.  
  88.         protected InnerTableRenderer(Table modelElement) {
  89.             super(modelElement);
  90.         }
  91.  
  92.         @Override
  93.         protected TableRenderer[] split(int row) {
  94.             InnerTableRenderer splitRenderer = (InnerTableRenderer) createSplitRenderer(
  95.                     new Table.RowRange(rowRange.getStartRow(), rowRange.getStartRow() + row));
  96.             splitRenderer.rows = rows.subList(0, row);
  97.             InnerTableRenderer overflowRenderer = (InnerTableRenderer) createOverflowRenderer(
  98.                     new Table.RowRange(rowRange.getStartRow() + row, rowRange.getFinishRow()));
  99.             overflowRenderer.rows = rows.subList(row, rows.size());
  100.             splitRenderer.occupiedArea = occupiedArea;
  101.  
  102.             return new TableRenderer[]{splitRenderer, overflowRenderer};
  103.         }
  104.  
  105.         @Override
  106.         public void drawChildren(DrawContext drawContext) {
  107.             super.drawChildren(drawContext);
  108.             for (IRenderer renderer : childRenderers) {
  109.                 PdfCanvas canvas = drawContext.getCanvas();
  110.                 canvas.beginText();
  111.                 Rectangle box = ((AbstractRenderer) renderer).getInnerAreaBBox();
  112.                 canvas.moveText(box.getLeft(), box.getTop() - this.getPropertyAsFloat(Property.FONT_SIZE));
  113.                 canvas.setFontAndSize(this.getPropertyAsFont(Property.FONT),
  114.                         this.getPropertyAsFloat(Property.FONT_SIZE));
  115.                 canvas.showText("This inner table header will always be repeated");
  116.                 canvas.endText();
  117.                 canvas.stroke();
  118.             }
  119.         }
  120.  
  121.         @Override
  122.         public InnerTableRenderer getNextRenderer() {
  123.             return new InnerTableRenderer((Table) modelElement);
  124.         }
  125.     }
  126. }
NestedTablesAligned.java
  1. /*
  2.  
  3.     This file is part of the iText (R) project.
  4.     Copyright (c) 1998-2016 iText Group NV
  5.  
  6. */
  7.  
  8. /**
  9.  * This example was written by Bruno Lowagie in answer to the following question:
  10.  * http://stackoverflow.com/questions/26625455/unable-to-left-align-nested-tables-inside-a-cell
  11.  */
  12. package com.itextpdf.samples.sandbox.tables;
  13.  
  14. import com.itextpdf.kernel.geom.PageSize;
  15. import com.itextpdf.kernel.pdf.PdfDocument;
  16. import com.itextpdf.kernel.pdf.PdfWriter;
  17. import com.itextpdf.layout.Document;
  18. import com.itextpdf.layout.element.Table;
  19. import com.itextpdf.layout.property.HorizontalAlignment;
  20. import com.itextpdf.samples.GenericTest;
  21. import com.itextpdf.test.annotations.type.SampleTest;
  22.  
  23. import org.junit.experimental.categories.Category;
  24.  
  25. import java.io.File;
  26.  
  27. @Category(SampleTest.class)
  28. public class NestedTablesAligned extends GenericTest {
  29.     public static final String DEST = "./target/test/resources/sandbox/tables/nested_tables_aligned.pdf";
  30.  
  31.     public static void main(String[] args) throws Exception {
  32.         File file = new File(DEST);
  33.         file.getParentFile().mkdirs();
  34.         new NestedTablesAligned().manipulatePdf(DEST);
  35.     }
  36.  
  37.     @Override
  38.     protected void manipulatePdf(String dest) throws Exception {
  39.         PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
  40.         // Note that it is not necessary to create new PageSize object,
  41.         // but for testing reasons (connected to parallelization) we call constructor here
  42.         Document doc = new Document(pdfDoc, new PageSize(PageSize.A4).rotate());
  43.  
  44.         float[] columnWidths = {200f, 200f, 200f};
  45.         Table table = new Table(columnWidths);
  46.         buildNestedTables(table);
  47.         doc.add(table);
  48.  
  49.         doc.close();
  50.     }
  51.  
  52.     private void buildNestedTables(Table outerTable) {
  53.         Table innerTable1 = new Table(1);
  54.         innerTable1.setWidth(100f);
  55.         innerTable1.setHorizontalAlignment(HorizontalAlignment.LEFT);
  56.         innerTable1.addCell("Cell 1");
  57.         innerTable1.addCell("Cell 2");
  58.         outerTable.addCell(innerTable1);
  59.         Table innerTable2 = new Table(2);
  60.         innerTable2.setWidth(100f);
  61.         innerTable2.setHorizontalAlignment(HorizontalAlignment.CENTER);
  62.         innerTable2.addCell("Cell 3");
  63.         innerTable2.addCell("Cell 4");
  64.         outerTable.addCell(innerTable2);
  65.         Table innerTable3 = new Table(2);
  66.         innerTable3.setWidth(100f);
  67.         innerTable3.setHorizontalAlignment(HorizontalAlignment.RIGHT);
  68.         innerTable3.addCell("Cell 5");
  69.         innerTable3.addCell("Cell 6");
  70.         outerTable.addCell(innerTable3);
  71.     }
  72. }
Contact

Still have questions? 

We're happy to answer your questions. Reach out to us and we'll get back to you shortly.

Contact us
Stay updated

Join 11,000+ subscribers and become an iText PDF expert by staying up to date with our new products, updates, tips, technical solutions and happenings.

Subscribe Now