Home > Software design >  PDF generation fails with large number of records
PDF generation fails with large number of records

Time:03-17

I am using itext 7 pdf to generate PDF from jdbc query . It works fine for 4000 records but once we inserted 17000 records in the table, I started getting Java Out of Memory space . I am getting all the data in one shot as shown below , how do I modify it to use paginated query and stitch all the paginated results into one PDF .

This is my driver code which gets in table name and HttpServlet and passes it to class which implements ResultSetExtractor.

public void generatePDF(String tableName, HttpServletResponse response, String[] filter) throws DataAccessException, IOException {
        //Table validation logic
        String sql = "select * from table";
        mainJdbcTemplate.query(sql, paramMap,
                new StreamingPDFResultSetExtractor(response.getOutputStream(), tableName));
    }

Custom class which implements Result Set extractor . I get in all the result set data and use itext to generate PDF.

public class StreamingPDFResultSetExtractor implements ResultSetExtractor<Void> {

    private final OutputStream os;
    private String tableName;

    /**
     * @param os the OutputStream to stream the PDF to
     */
    public StreamingPDFResultSetExtractor(final OutputStream os, final String tableName) {
        this.os = os;
        this.tableName = tableName;
    }

    @Override
    public Void extractData(final ResultSet rs) throws SQLException {
        // Creating a PdfDocument
        PdfDocument pdfDoc = new PdfDocument(new PdfWriter(os));
        // Creating a Document and setting page size
        Document document = new Document(pdfDoc, new PageSize(2384, 3370));
        // Adding a new page
        pdfDoc.addNewPage();
        final var rsmd = rs.getMetaData();
        final var columnCount = rsmd.getColumnCount();
        try {
            log.info("Generating PDF");
            Paragraph tablename = new Paragraph(tableName);
            tablename.setFontSize(20);
            // Add table
            Table table = new Table(columnCount);
            // Set width of table
            table.setWidth(UnitValue.createPercentValue(100)).setFixedLayout();
            table.setHorizontalAlignment(HorizontalAlignment.CENTER);
            table.setTextAlignment(TextAlignment.CENTER);
            // Header Font and color
            DeviceRgb hColor = new DeviceRgb(3, 148, 252);

            for (var i = 1; i <= columnCount; i  ) {
                Cell hcell = new Cell();
                hcell.add(new Paragraph(rsmd.getColumnName(i)));
                hcell.setFontSize(14);
                hcell.setBackgroundColor(hColor);
                table.addHeaderCell(hcell);
            }
            while (rs.next()) {
                for (var i = 1; i <= columnCount; i  ) {
                    final var value = rs.getObject(i);
                    String v = value == null ? "" : value.toString();
                        Cell cell = new Cell();
                        cell.add(new Paragraph(v));
                        table.addCell(cell);
                    }

                }
            }
            document.add(tablename);
            document.add(table);
            document.close();
            log.info("PDF generation complete");

        } catch (Exception ex) {

            log.error("Error occurred: {0}", ex);
        }
        return null;

    }
}

CodePudding user response:

You can basically use another overloaded constructor of Table class that is specially meant for large tables. It takes boolean as an argument to basically reduce memory footprint if you set it to true. Please refer to this example of how it can be done in iText 7 https://kb.itextpdf.com/home/it7kb/examples/large-tables

    // The second argument determines 'large table' functionality is used
    // It defines whether parts of the table will be written before all data is added.
    Table table = new Table(UnitValue.createPercentArray(5), true);
  • Related