Using forms for reporting

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

31st May 2016
admin-marketing

Switch code for this example

MergeForms.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/26174675/copy-pdf-with-annotations-using-itext
  11.  */
  12. package com.itextpdf.samples.sandbox.acroforms;
  13.  
  14. import com.itextpdf.forms.PdfPageFormCopier;
  15. import com.itextpdf.kernel.pdf.PdfDocument;
  16. import com.itextpdf.kernel.pdf.PdfReader;
  17. import com.itextpdf.kernel.pdf.PdfWriter;
  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. import java.io.FileNotFoundException;
  25. import java.io.FileOutputStream;
  26.  
  27. @Category(SampleTest.class)
  28. public class MergeForms extends GenericTest {
  29.     public static final String DEST = "./target/test/resources/sandbox/acroforms/merge_forms.pdf";
  30.     public static final String SRC1 = "./src/test/resources/pdfs/subscribe.pdf";
  31.     public static final String SRC2 = "./src/test/resources/pdfs/state.pdf";
  32.  
  33.     public static void main(String[] args) throws Exception {
  34.         File file = new File(DEST);
  35.         file.getParentFile().mkdirs();
  36.         new MergeForms().manipulatePdf(DEST);
  37.     }
  38.  
  39.     @Override
  40.     protected void manipulatePdf(String dest) throws Exception {
  41.         PdfReader[] readers = {
  42.                 new PdfReader(getFile1()),
  43.                 new PdfReader(getFile2())
  44.         };
  45.         manipulatePdf(dest, readers);
  46.     }
  47.  
  48.     protected void manipulatePdf(String dest, PdfReader[] readers) throws FileNotFoundException {
  49.         PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
  50.         pdfDoc.initializeOutlines();
  51.         PdfPageFormCopier formCopier = new PdfPageFormCopier();
  52.         for (PdfReader reader : readers) {
  53.             PdfDocument readerDoc = new PdfDocument(reader);
  54.             readerDoc.copyPagesTo(1, readerDoc.getNumberOfPages(), pdfDoc, formCopier);
  55.             readerDoc.close();
  56.         }
  57.         pdfDoc.close();
  58.     }
  59.  
  60.     public String getFile1() {
  61.         return SRC1;
  62.     }
  63.  
  64.     public String getFile2() {
  65.         return SRC2;
  66.     }
  67. }
MergeForms2.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/26174675/copy-pdf-with-annotations-using-itext
  11.  */
  12. package com.itextpdf.samples.sandbox.acroforms;
  13.  
  14. import com.itextpdf.forms.PdfAcroForm;
  15. import com.itextpdf.forms.PdfPageFormCopier;
  16. import com.itextpdf.forms.fields.PdfFormField;
  17. import com.itextpdf.io.source.ByteArrayOutputStream;
  18. import com.itextpdf.io.source.RandomAccessSourceFactory;
  19. import com.itextpdf.kernel.pdf.PdfDocument;
  20. import com.itextpdf.kernel.pdf.PdfReader;
  21. import com.itextpdf.kernel.pdf.PdfWriter;
  22. import com.itextpdf.kernel.pdf.ReaderProperties;
  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. import java.io.IOException;
  30.  
  31. @Category(SampleTest.class)
  32. public class MergeForms2 extends GenericTest {
  33.     public static final String DEST = "./target/test/resources/sandbox/acroforms/merge_forms2.pdf";
  34.     public static final String SRC = "./src/test/resources/pdfs/state.pdf";
  35.  
  36.     public static void main(String[] args) throws Exception {
  37.         File file = new File(DEST);
  38.         file.getParentFile().mkdirs();
  39.         new MergeForms2().manipulatePdf(DEST);
  40.     }
  41.  
  42.     @Override
  43.     protected void manipulatePdf(String dest) throws Exception {
  44.         PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
  45.         pdfDoc.initializeOutlines();
  46.         PdfPageFormCopier formCopier = new PdfPageFormCopier();
  47.         for (int i = 0; i < 3; ) {
  48.             PdfDocument readerDoc = new PdfDocument(new PdfReader(
  49.                     new RandomAccessSourceFactory().createSource(renameFields(SRC, ++i)),
  50.                     new ReaderProperties()));
  51.             readerDoc.copyPagesTo(1, readerDoc.getNumberOfPages(), pdfDoc, formCopier);
  52.             readerDoc.close();
  53.         }
  54.         pdfDoc.close();
  55.     }
  56.  
  57.     protected byte[] renameFields(String src, int i) throws IOException {
  58.         ByteArrayOutputStream baos = new ByteArrayOutputStream();
  59.         PdfDocument pdfDoc = new PdfDocument(new PdfReader(src), new PdfWriter(baos));
  60.  
  61.         PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
  62.         for (PdfFormField field : form.getFormFields().values()) {
  63.             field.setFieldName(String.format("%s_%d", field.getFieldName().toString(), i));
  64.         }
  65.  
  66.         pdfDoc.close();
  67.         return baos.toByteArray();
  68.     }
  69. }
FillFlattenMerge1.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. package com.itextpdf.samples.sandbox.acroforms.reporting;
  9.  
  10. import com.itextpdf.forms.PdfAcroForm;
  11. import com.itextpdf.forms.PdfPageFormCopier;
  12. import com.itextpdf.forms.fields.PdfFormField;
  13. import com.itextpdf.io.source.ByteArrayOutputStream;
  14. import com.itextpdf.kernel.pdf.PdfDocument;
  15. import com.itextpdf.kernel.pdf.PdfReader;
  16. import com.itextpdf.kernel.pdf.PdfWriter;
  17. import com.itextpdf.samples.GenericTest;
  18. import com.itextpdf.test.annotations.type.SampleTest;
  19.  
  20. import org.junit.experimental.categories.Category;
  21.  
  22. import java.io.BufferedReader;
  23. import java.io.ByteArrayInputStream;
  24. import java.io.File;
  25. import java.io.FileReader;
  26. import java.util.Map;
  27. import java.util.StringTokenizer;
  28.  
  29. @Category(SampleTest.class)
  30. public class FillFlattenMerge1 extends GenericTest {
  31.     public static final String DEST = "./target/test/resources/sandbox/acroforms/reporting/fill_flatten_merge1.pdf";
  32.     public static final String DATA = "./src/test/resources/data/united_states.csv";
  33.     public static final String SRC = "./src/test/resources/pdfs/state.pdf";
  34.  
  35.     public static void main(String[] args) throws Exception {
  36.         File file = new File(DEST);
  37.         file.getParentFile().mkdirs();
  38.         new FillFlattenMerge1().manipulatePdf(DEST);
  39.     }
  40.  
  41.     @Override
  42.     protected void manipulatePdf(String dest) throws Exception {
  43.         PdfDocument pdfDoc = new PdfDocument(new PdfWriter(DEST));
  44.         PdfPageFormCopier formCopier = new PdfPageFormCopier();
  45.         pdfDoc.initializeOutlines();
  46.  
  47.         ByteArrayOutputStream baos;
  48.         PdfDocument pdfInnerDoc;
  49.         Map fields;
  50.         PdfAcroForm form;
  51.         StringTokenizer tokenizer;
  52.  
  53.         BufferedReader br = new BufferedReader(new FileReader(DATA));
  54.         String line = br.readLine();
  55.         while ((line = br.readLine()) != null) {
  56.             // create a PDF in memory
  57.             baos = new ByteArrayOutputStream();
  58.             pdfInnerDoc = new PdfDocument(new PdfReader(SRC), new PdfWriter(baos));
  59.             form = PdfAcroForm.getAcroForm(pdfInnerDoc, true);
  60.             tokenizer = new StringTokenizer(line, ";");
  61.             fields = form.getFormFields();
  62.             fields.get("name").setValue(tokenizer.nextToken());
  63.             fields.get("abbr").setValue(tokenizer.nextToken());
  64.             fields.get("capital").setValue(tokenizer.nextToken());
  65.             fields.get("city").setValue(tokenizer.nextToken());
  66.             fields.get("population").setValue(tokenizer.nextToken());
  67.             fields.get("surface").setValue(tokenizer.nextToken());
  68.             fields.get("timezone1").setValue(tokenizer.nextToken());
  69.             fields.get("timezone2").setValue(tokenizer.nextToken());
  70.             fields.get("dst").setValue(tokenizer.nextToken());
  71.             form.flattenFields();
  72.             pdfInnerDoc.close();
  73.  
  74.             pdfInnerDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(baos.toByteArray())));
  75.             pdfInnerDoc.copyPagesTo(1, pdfInnerDoc.getNumberOfPages(), pdfDoc, formCopier);
  76.             pdfInnerDoc.close();
  77.         }
  78.         br.close();
  79.  
  80.         pdfDoc.close();
  81.     }
  82. }
FillFlattenMerge2.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. package com.itextpdf.samples.sandbox.acroforms.reporting;
  9.  
  10. import com.itextpdf.forms.PdfAcroForm;
  11. import com.itextpdf.forms.PdfPageFormCopier;
  12. import com.itextpdf.forms.fields.PdfFormField;
  13. import com.itextpdf.io.source.ByteArrayOutputStream;
  14. import com.itextpdf.kernel.pdf.PdfDocument;
  15. import com.itextpdf.kernel.pdf.PdfReader;
  16. import com.itextpdf.kernel.pdf.PdfWriter;
  17. import com.itextpdf.samples.GenericTest;
  18. import com.itextpdf.test.annotations.type.SampleTest;
  19.  
  20. import org.junit.experimental.categories.Category;
  21.  
  22. import java.io.BufferedReader;
  23. import java.io.ByteArrayInputStream;
  24. import java.io.File;
  25. import java.io.FileReader;
  26. import java.util.Map;
  27. import java.util.StringTokenizer;
  28.  
  29. @Category(SampleTest.class)
  30. public class FillFlattenMerge2 extends GenericTest {
  31.     public static final String DEST = "./target/test/resources/sandbox/acroforms/reporting/fill_flatten_merge2.pdf";
  32.     public static final String DATA = "./src/test/resources/data/united_states.csv";
  33.     public static final String SRC = "./src/test/resources/pdfs/state.pdf";
  34.  
  35.     public static void main(String[] args) throws Exception {
  36.         File file = new File(DEST);
  37.         file.getParentFile().mkdirs();
  38.         new FillFlattenMerge2().manipulatePdf(DEST);
  39.     }
  40.  
  41.     @Override
  42.     protected void manipulatePdf(String dest) throws Exception {
  43.         PdfWriter writer = new PdfWriter(DEST);
  44.         writer.setSmartMode(true);
  45.         PdfDocument pdfDoc = new PdfDocument(writer);
  46.         PdfPageFormCopier formCopier = new PdfPageFormCopier();
  47.         pdfDoc.initializeOutlines();
  48.  
  49.         ByteArrayOutputStream baos;
  50.         PdfReader reader;
  51.         PdfDocument pdfInnerDoc;
  52.         Map fields;
  53.         PdfAcroForm form;
  54.         StringTokenizer tokenizer;
  55.  
  56.         BufferedReader br = new BufferedReader(new FileReader(DATA));
  57.         String line = br.readLine();
  58.         while ((line = br.readLine()) != null) {
  59.             // create a PDF in memory
  60.             baos = new ByteArrayOutputStream();
  61.             reader = new PdfReader(SRC);
  62.             pdfInnerDoc = new PdfDocument(reader, new PdfWriter(baos));
  63.             form = PdfAcroForm.getAcroForm(pdfInnerDoc, true);
  64.             tokenizer = new StringTokenizer(line, ";");
  65.             fields = form.getFormFields();
  66.             fields.get("name").setValue(tokenizer.nextToken());
  67.             fields.get("abbr").setValue(tokenizer.nextToken());
  68.             fields.get("capital").setValue(tokenizer.nextToken());
  69.             fields.get("city").setValue(tokenizer.nextToken());
  70.             fields.get("population").setValue(tokenizer.nextToken());
  71.             fields.get("surface").setValue(tokenizer.nextToken());
  72.             fields.get("timezone1").setValue(tokenizer.nextToken());
  73.             fields.get("timezone2").setValue(tokenizer.nextToken());
  74.             fields.get("dst").setValue(tokenizer.nextToken());
  75.             form.flattenFields();
  76.             pdfInnerDoc.close();
  77.  
  78.             pdfInnerDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(baos.toByteArray())));
  79.             pdfInnerDoc.copyPagesTo(1, pdfInnerDoc.getNumberOfPages(), pdfDoc, formCopier);
  80.             pdfInnerDoc.close();
  81.         }
  82.         br.close();
  83.  
  84.         pdfDoc.close();
  85.     }
  86. }
FillFlattenMerge3.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/24270195/wrapping-of-arabic-text-using-acrofields-in-itext-5-5
  11.  */
  12. package com.itextpdf.samples.sandbox.acroforms.reporting;
  13.  
  14. import com.itextpdf.forms.PdfAcroForm;
  15. import com.itextpdf.forms.fields.PdfFormField;
  16. import com.itextpdf.io.font.FontConstants;
  17. import com.itextpdf.kernel.events.Event;
  18. import com.itextpdf.kernel.events.IEventHandler;
  19. import com.itextpdf.kernel.events.PdfDocumentEvent;
  20. import com.itextpdf.kernel.font.PdfFont;
  21. import com.itextpdf.kernel.font.PdfFontFactory;
  22. import com.itextpdf.kernel.geom.Rectangle;
  23. import com.itextpdf.kernel.pdf.PdfDocument;
  24. import com.itextpdf.kernel.pdf.PdfReader;
  25. import com.itextpdf.kernel.pdf.PdfWriter;
  26. import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
  27. import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
  28. import com.itextpdf.layout.Canvas;
  29. import com.itextpdf.layout.Document;
  30. import com.itextpdf.layout.element.Paragraph;
  31. import com.itextpdf.layout.property.TextAlignment;
  32. import com.itextpdf.layout.property.VerticalAlignment;
  33. import com.itextpdf.samples.GenericTest;
  34. import com.itextpdf.test.annotations.type.SampleTest;
  35.  
  36. import org.junit.experimental.categories.Category;
  37.  
  38. import java.io.BufferedReader;
  39. import java.io.File;
  40. import java.io.FileReader;
  41. import java.io.IOException;
  42. import java.util.HashMap;
  43. import java.util.Map;
  44. import java.util.StringTokenizer;
  45.  
  46. @Category(SampleTest.class)
  47. public class FillFlattenMerge3 extends GenericTest {
  48.     public static final String DEST = "./target/test/resources/sandbox/acroforms/reporting/fill_flatten_merge3.pdf";
  49.     public static final String DATA = "./src/test/resources/data/united_states.csv";
  50.     public static final String[] FIELDS = {
  51.             "name", "abbr", "capital", "city", "population", "surface", "timezone1", "timezone2", "dst"
  52.     };
  53.     public static final String SRC = "./src/test/resources/pdfs/state.pdf";
  54.  
  55.     protected Map positions;
  56.  
  57.     public static void main(String[] args) throws Exception {
  58.         File file = new File(DEST);
  59.         file.getParentFile().mkdirs();
  60.         new FillFlattenMerge3().manipulatePdf(DEST);
  61.     }
  62.  
  63.     @Override
  64.     protected void manipulatePdf(String dest) throws Exception {
  65.         PdfDocument srcDoc = new PdfDocument(new PdfReader(SRC));
  66.         PdfAcroForm form = PdfAcroForm.getAcroForm(srcDoc, true);
  67.         positions = new HashMap();
  68.         Map fields = form.getFormFields();
  69.         for (PdfFormField field : fields.values()) {
  70.             positions.put(field.getFieldName().getValue(), field.getWidgets().get(0).getRectangle().toRectangle());
  71.         }
  72.  
  73.         PdfDocument pdfDoc = new PdfDocument(new PdfWriter(DEST));
  74.         Document doc = new Document(pdfDoc);
  75.         PdfFont font = PdfFontFactory.createFont(FontConstants.HELVETICA);
  76.         pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, new PaginationEventHandler(srcDoc.getFirstPage().copyAsFormXObject(pdfDoc)));
  77.         srcDoc.close();
  78.  
  79.         StringTokenizer tokenizer;
  80.         BufferedReader br = new BufferedReader(new FileReader(DATA));
  81.         String line = br.readLine();
  82.         while ((line = br.readLine()) != null) {
  83.             pdfDoc.addNewPage();
  84.             int i = 0;
  85.             tokenizer = new StringTokenizer(line, ";");
  86.             while (tokenizer.hasMoreTokens()) {
  87.                 process(doc, FIELDS[i++], tokenizer.nextToken(), font);
  88.             }
  89.         }
  90.         br.close();
  91.  
  92.         doc.close();
  93.     }
  94.  
  95.     protected void process(Document doc, String name, String value, PdfFont font) {
  96.         Rectangle rect = positions.get(name);
  97.         Paragraph p = new Paragraph(value).setFont(font).setFontSize(10);
  98.         doc.showTextAligned(p, rect.getLeft() + 2, rect.getBottom() + 2, doc.getPdfDocument().getNumberOfPages(),
  99.                 TextAlignment.LEFT, VerticalAlignment.BOTTOM, 0);
  100.     }
  101.  
  102.  
  103.     protected class PaginationEventHandler implements IEventHandler {
  104.         PdfFormXObject background;
  105.  
  106.         public PaginationEventHandler(PdfFormXObject background) throws IOException {
  107.             this.background = background;
  108.         }
  109.  
  110.         @Override
  111.         public void handleEvent(Event event) {
  112.             PdfDocument pdfDoc = ((PdfDocumentEvent) event).getDocument();
  113.             int pageNum = pdfDoc.getPageNumber(((PdfDocumentEvent) event).getPage());
  114.             // Add the background
  115.             PdfCanvas canvas = new PdfCanvas(pdfDoc.getPage(pageNum).newContentStreamBefore(),
  116.                     ((PdfDocumentEvent) event).getPage().getResources(), pdfDoc)
  117.                     .addXObject(background, 0, 0);
  118.             // Add the page number
  119.             new Canvas(canvas, pdfDoc, ((PdfDocumentEvent) event).getPage().getPageSize())
  120.                     .showTextAligned("page " + pageNum, 550, 800, TextAlignment.RIGHT);
  121.         }
  122.     }
  123. }
FillForm.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. package com.itextpdf.samples.sandbox.acroforms.reporting;
  9.  
  10. import com.itextpdf.forms.PdfAcroForm;
  11. import com.itextpdf.kernel.pdf.PdfDocument;
  12. import com.itextpdf.kernel.pdf.PdfReader;
  13. import com.itextpdf.kernel.pdf.PdfWriter;
  14. import com.itextpdf.samples.GenericTest;
  15. import com.itextpdf.test.annotations.type.SampleTest;
  16.  
  17. import org.junit.experimental.categories.Category;
  18.  
  19. import java.io.File;
  20.  
  21. @Category(SampleTest.class)
  22. public class FillForm extends GenericTest {
  23.     public static final String DEST = "./target/test/resources/sandbox/acroforms/reporting/fill_form.pdf";
  24.     public static final String SRC = "./src/test/resources/pdfs/state.pdf";
  25.  
  26.     public static void main(String[] args) throws Exception {
  27.         File file = new File(DEST);
  28.         file.getParentFile().mkdirs();
  29.         new FillForm().manipulatePdf(DEST);
  30.     }
  31.  
  32.     @Override
  33.     protected void manipulatePdf(String dest) throws Exception {
  34.         PdfDocument pdfDoc = new PdfDocument(new PdfReader(SRC), new PdfWriter(DEST));
  35.         PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
  36.  
  37.         form.getField("name").setValue("CALIFORNIA");
  38.         form.getField("abbr").setValue("CA");
  39.         form.getField("capital").setValue("Sacramento");
  40.         form.getField("city").setValue("Los Angeles");
  41.         form.getField("population").setValue("36,961,664");
  42.         form.getField("surface").setValue("163,707");
  43.         form.getField("timezone1").setValue("PT (UTC-8)");
  44.         form.getField("timezone2").setValue("-");
  45.         form.getField("dst").setValue("YES");
  46.  
  47.         pdfDoc.close();
  48.     }
  49. }
FlattenForm.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. package com.itextpdf.samples.sandbox.acroforms.reporting;
  9.  
  10. import com.itextpdf.forms.PdfAcroForm;
  11. import com.itextpdf.kernel.pdf.PdfDocument;
  12. import com.itextpdf.kernel.pdf.PdfReader;
  13. import com.itextpdf.kernel.pdf.PdfWriter;
  14. import com.itextpdf.samples.GenericTest;
  15. import com.itextpdf.test.annotations.type.SampleTest;
  16.  
  17. import org.junit.experimental.categories.Category;
  18.  
  19. import java.io.File;
  20.  
  21. @Category(SampleTest.class)
  22. public class FlattenForm extends GenericTest {
  23.     public static final String DEST = "./target/test/resources/sandbox/acroforms/reporting/flatten_form.pdf";
  24.     public static final String SRC = "./src/test/resources/pdfs/state.pdf";
  25.  
  26.     public static void main(String[] args) throws Exception {
  27.         File file = new File(DEST);
  28.         file.getParentFile().mkdirs();
  29.         new FlattenForm().manipulatePdf(DEST);
  30.     }
  31.  
  32.     @Override
  33.     protected void manipulatePdf(String dest) throws Exception {
  34.         PdfDocument pdfDoc = new PdfDocument(new PdfReader(SRC), new PdfWriter(DEST));
  35.         PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
  36.  
  37.         form.getField("name").setValue("CALIFORNIA");
  38.         form.getField("abbr").setValue("CA");
  39.         form.getField("capital").setValue("Sacramento");
  40.         form.getField("city").setValue("Los Angeles");
  41.         form.getField("population").setValue("36,961,664");
  42.         form.getField("surface").setValue("163,707");
  43.         form.getField("timezone1").setValue("PT (UTC-8)");
  44.         form.getField("timezone2").setValue("-");
  45.         form.getField("dst").setValue("YES");
  46.  
  47.         form.flattenFields();
  48.  
  49.         pdfDoc.close();
  50.     }
  51. }
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