package co.smartreceipts.android.workers.reports.pdf.renderer.impl; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.google.common.base.Preconditions; import com.tom_roush.pdfbox.pdmodel.PDDocument; import java.io.IOException; import java.util.ArrayList; import java.util.List; import co.smartreceipts.android.filters.Filter; import co.smartreceipts.android.model.Column; import co.smartreceipts.android.workers.reports.TableGenerator; import co.smartreceipts.android.workers.reports.pdf.colors.PdfColorStyle; import co.smartreceipts.android.workers.reports.pdf.fonts.PdfFontStyle; import co.smartreceipts.android.workers.reports.pdf.pdfbox.PdfBoxContext; import co.smartreceipts.android.workers.reports.pdf.renderer.constraints.WidthConstraint; import co.smartreceipts.android.workers.reports.pdf.renderer.formatting.BackgroundColor; import co.smartreceipts.android.workers.reports.pdf.renderer.formatting.Padding; import co.smartreceipts.android.workers.reports.pdf.renderer.grid.GridRowRenderer; import co.smartreceipts.android.workers.reports.pdf.renderer.text.TextRenderer; import co.smartreceipts.android.workers.reports.pdf.misc.ColumnWidthCalculator; import co.smartreceipts.android.workers.reports.pdf.utils.HeavyHandedReplaceIllegalCharacters; public class PdfTableGenerator<DataType> implements TableGenerator<List<GridRowRenderer>, DataType> { private static final Padding DEFAULT_PADDING = new Padding(4f); private final PdfBoxContext pdfBoxContext; private final List<Column<DataType>> columns; private final PDDocument pdDocument; private final Filter<DataType> filter; private final boolean printHeaders; private final boolean printFooters; public PdfTableGenerator(@NonNull PdfBoxContext context, @NonNull List<Column<DataType>> columns, @NonNull PDDocument pdDocument, @Nullable Filter<DataType> receiptFilter, boolean printHeaders, boolean printFooters) { this.pdfBoxContext = Preconditions.checkNotNull(context); this.columns = Preconditions.checkNotNull(columns); this.pdDocument = Preconditions.checkNotNull(pdDocument); this.filter = receiptFilter; this.printHeaders = printHeaders; this.printFooters = printFooters; } @NonNull @Override public List<GridRowRenderer> generate(@NonNull List<DataType> list) throws IOException { final List<GridRowRenderer> gridRows = new ArrayList<>(); final int colCount = columns.size(); final List<DataType> filteredList = new ArrayList<>(list.size()); float availableWidth = pdfBoxContext.getPageSize().getWidth() - 2 * pdfBoxContext.getPageMarginHorizontal(); // calculate column widths // TODO: Include this as part of the measure pass float[] colWidths; ColumnWidthCalculator columnWidthCalculator = new ColumnWidthCalculator<>( list, columns, availableWidth, DEFAULT_PADDING.value(), pdfBoxContext.getFontManager().getFont(PdfFontStyle.TableHeader), pdfBoxContext.getFontManager().getFont(PdfFontStyle.Default)); colWidths = columnWidthCalculator.calculate(); // Add the header final GridRowRenderer headerRow; if (printHeaders) { final List<TextRenderer> headerColumns = new ArrayList<>(); for (int i = 0; i < colCount; i++) { final TextRenderer textRenderer = new TextRenderer( pdfBoxContext.getAndroidContext(), pdDocument, columns.get(i).getHeader(), pdfBoxContext.getColorManager().getColor(PdfColorStyle.Outline), pdfBoxContext.getFontManager().getFont(PdfFontStyle.TableHeader)); textRenderer.getRenderingFormatting().addFormatting(DEFAULT_PADDING); textRenderer.getRenderingConstraints().addConstraint(new WidthConstraint(colWidths[i])); headerColumns.add(textRenderer); } headerRow = new GridRowRenderer(headerColumns); headerRow.getRenderingFormatting().addFormatting(new BackgroundColor(pdfBoxContext.getColorManager().getColor(PdfColorStyle.TableHeader))); gridRows.add(headerRow); } else { headerRow = null; } if (!list.isEmpty()) { // Add each row for (int j = 0; j < list.size(); j++) { final DataType data = list.get(j); if (filter == null || filter.accept(data)) { filteredList.add(data); final List<TextRenderer> columnRenderers = new ArrayList<>(); for (int i = 0; i < colCount; i++) { final TextRenderer textRenderer = new TextRenderer( pdfBoxContext.getAndroidContext(), pdDocument, HeavyHandedReplaceIllegalCharacters.getSafeString(columns.get(i).getValue(data)), pdfBoxContext.getColorManager().getColor(PdfColorStyle.Default), pdfBoxContext.getFontManager().getFont(PdfFontStyle.Default)); textRenderer.getRenderingFormatting().addFormatting(DEFAULT_PADDING); textRenderer.getRenderingConstraints().addConstraint(new WidthConstraint(colWidths[i])); columnRenderers.add(textRenderer); } final GridRowRenderer rowRenderer = new GridRowRenderer(columnRenderers); if (j % 2 == 0) { rowRenderer.getRenderingFormatting().addFormatting(new BackgroundColor(pdfBoxContext.getColorManager().getColor(PdfColorStyle.TableCell))); } rowRenderer.associateHeaderRow(headerRow); gridRows.add(rowRenderer); } } } // Add the footer if (printFooters) { final List<TextRenderer> footerColumns = new ArrayList<>(); for (int i = 0; i < colCount; i++) { final TextRenderer textRenderer = new TextRenderer( pdfBoxContext.getAndroidContext(), pdDocument, columns.get(i).getFooter(filteredList), pdfBoxContext.getColorManager().getColor(PdfColorStyle.Outline), pdfBoxContext.getFontManager().getFont(PdfFontStyle.Default)); textRenderer.getRenderingFormatting().addFormatting(DEFAULT_PADDING); textRenderer.getRenderingConstraints().addConstraint(new WidthConstraint(colWidths[i])); footerColumns.add(textRenderer); } final GridRowRenderer footerRow = new GridRowRenderer(footerColumns); footerRow.associateHeaderRow(headerRow); footerRow.getRenderingFormatting().addFormatting(new BackgroundColor(pdfBoxContext.getColorManager().getColor(PdfColorStyle.TableHeader))); gridRows.add(footerRow); } return gridRows; } }