package com.example.reporting.services.impl.pdf; import static com.example.reporting.util.DateUtil.formatDate; import static com.example.reporting.util.Constants.MARGIN_2; import static com.example.reporting.util.Constants.MARGIN_2_9; import static com.example.reporting.util.Constants.WHITE_COLOR; import static com.example.reporting.util.Constants.GREEN_COLOR; import static com.example.reporting.util.Constants.LIGHTEST_GREEN_COLOR; import static com.example.reporting.util.PdfUtil.getPdfPTable; import static com.example.reporting.util.PdfUtil.getPdfPCellSpaningAcross; import static com.example.reporting.util.PdfUtil.getPdfPCell; import java.awt.Color; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Date; import javax.imageio.ImageIO; import org.springframework.http.ResponseEntity; import com.lowagie.text.BadElementException; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.Element; import com.lowagie.text.Font; import com.lowagie.text.Image; import com.lowagie.text.Paragraph; import com.lowagie.text.pdf.BaseFont; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfPTable; import com.lowagie.text.pdf.PdfPageEventHelper; import com.lowagie.text.pdf.PdfTemplate; import com.lowagie.text.pdf.PdfWriter; import com.example.reporting.dto.database.ReportDto; import com.example.reporting.dto.database.TeamDto; import com.example.reporting.services.database.ITeamService; import com.example.reporting.util.DateUtil; import com.example.reporting.util.ReportUtil; import com.example.reporting.util.NumberUtils; import com.example.reporting.util.SessionUtil; /** * Abstract class for PDF conversion. * * @param

the type of the parameter used for conversion */ public abstract class PdfConverter

{ protected final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); protected final SimpleDateFormat dateFormatSimple = new SimpleDateFormat("dd.MM.yyyy"); protected final SimpleDateFormat timeFormatSimple = new SimpleDateFormat("HH:mm"); protected final SimpleDateFormat dateTimeFormatSimple = new SimpleDateFormat("dd.MM.yyyy HH:mm"); protected BaseFont baseFont; protected final Font fontBold22; protected final Font fontBold16; protected final Font fontBold14; protected final Font font14; protected final Font font12; protected final Font fontBold9; protected final Font fontBold10; protected final Font font10; protected Color conditionalColorStatus; protected Color conditionalColorTeam; protected Color conditionalColorUnit; protected Color conditionalColorId; protected final Document document = new Document(); protected final ByteArrayOutputStream baos = new ByteArrayOutputStream(); protected PdfConverter() throws DocumentException, IOException { try { // loading DejaVu from resources this.baseFont = BaseFont.createFont("font/DejaVuSans.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); } catch (DocumentException | IOException e) { // fallback to Helvetica if loading of DejaVu fails this.baseFont = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.IDENTITY_H, BaseFont.EMBEDDED); } this.fontBold22 = new Font(this.baseFont, 22, Font.BOLD); this.fontBold16 = new Font(this.baseFont, 16, Font.BOLD); this.fontBold14 = new Font(this.baseFont, 14, Font.BOLD); this.font14 = new Font(this.baseFont, 14, Font.NORMAL); this.font12 = new Font(this.baseFont, 12, Font.NORMAL); this.fontBold9 = new Font(this.baseFont, 9, Font.BOLD); this.fontBold10 = new Font(this.baseFont, 10, Font.BOLD); this.font10 = new Font(this.baseFont, 10, Font.NORMAL); } /** * This class is used to create a header and footer for the PDF document. */ public static class HeaderFooterPageEvent extends PdfPageEventHelper { private String headerText; private Image headerImage; private int currentPageNumber; private BaseFont baseFont; private PdfTemplate totalPageTemplate; private String entityId; /** * Constructor for HeaderFooterPageEvent. * * @param headerText the text to be displayed in the header * @param baseFont the font to be used for the header text * @param headerImage the image to be displayed in the header * @param entityId the ID of the entity * @throws BadElementException if there is an error */ public HeaderFooterPageEvent(String headerText, BaseFont baseFont, Image headerImage, String entityId) { this.headerText = headerText; this.headerImage = headerImage; this.currentPageNumber = 0; this.baseFont = baseFont; this.entityId = entityId; } @Override public void onOpenDocument(PdfWriter writer, Document document) { this.totalPageTemplate = writer.getDirectContent().createTemplate(50, 50); } @Override public void onEndPage(PdfWriter writer, Document document) { PdfContentByte cb = writer.getDirectContent(); // Increment the current page number this.currentPageNumber++; String[] lines = headerText.split("\\|"); // Split the header text if it contains a pipe character if (currentPageNumber == 1) { // Page text for title page cb.beginText(); cb.setFontAndSize(this.baseFont, 8); String entityText = "Entity-ID: " + this.entityId; cb.setTextMatrix(MARGIN_2, (MARGIN_2_9 / 2)); cb.showText(entityText); cb.endText(); } else { // Header for regular pages - first line cb.beginText(); cb.setFontAndSize(this.baseFont, 10); cb.setTextMatrix(MARGIN_2, document.getPageSize().getHeight() - (MARGIN_2_9 / 2)); cb.showText(lines[0]); cb.endText(); // Second line (if exists) if (lines.length > 1) { cb.beginText(); cb.setFontAndSize(this.baseFont, 10); cb.setTextMatrix(MARGIN_2, document.getPageSize().getHeight() - (MARGIN_2_9 / 2) - 12); cb.showText(lines[1]); cb.endText(); } // Header image for regular pages float imageX = document.getPageSize().getWidth() - headerImage.getScaledWidth() - MARGIN_2; headerImage.setAbsolutePosition(imageX, document.getPageSize().getHeight() - (MARGIN_2_9 / 2 + 10)); cb.addImage(headerImage); // Footer for regular pages cb.beginText(); cb.setFontAndSize(this.baseFont, 8); String footerText = "Page " + currentPageNumber + " of "; // Calculate the width of the footer text float textWidth = baseFont.getWidthPoint(footerText, 8); float rightAlignX = document.getPageSize().getWidth() - textWidth - MARGIN_2 - 11; cb.setTextMatrix(rightAlignX, (MARGIN_2_9 / 2)); cb.showText(footerText); cb.endText(); cb.addTemplate(totalPageTemplate, rightAlignX + textWidth, (MARGIN_2_9 / 2)); cb.beginText(); cb.setFontAndSize(this.baseFont, 8); String entityText = "Entity-ID: " + this.entityId; cb.setTextMatrix(MARGIN_2, (MARGIN_2_9 / 2)); cb.showText(entityText); cb.endText(); } } @Override public void onCloseDocument(PdfWriter writer, Document document) { totalPageTemplate.beginText(); totalPageTemplate.setFontAndSize(baseFont, 8); totalPageTemplate.setTextMatrix(0, 0); totalPageTemplate.showText(String.valueOf(currentPageNumber)); totalPageTemplate.endText(); } } protected void generateTitlePage(ReportDto reportDto, PdfWriter writer) throws IOException { try (InputStream imageStream = getClass().getClassLoader().getResourceAsStream("logo/logo.png")) { Image logoImage = Image.getInstance(ImageIO.read(imageStream), null); logoImage.scaleToFit(154, 25); // Resize the image if necessary // Create a clone of the image for the header/footer Image headerLogoImage = Image.getInstance(logoImage); writer.setPageEvent(new HeaderFooterPageEvent(getHeaderText(reportDto), this.baseFont, headerLogoImage, reportDto.getEntityId())); // Pass the logo image document.setMargins(MARGIN_2, MARGIN_2, MARGIN_2_9, MARGIN_2); document.open(); logoImage.setAlignment(Element.ALIGN_LEFT); document.add(logoImage); document.add(new Paragraph("\n", fontBold22)); // Title Paragraph title = getPdfTitle(reportDto); title.setAlignment(Element.ALIGN_CENTER); document.add(title); document.add(new Paragraph("\n", font14)); // Add a blank line // Owner generateParagraphWithBlankLine("Owner: " + reportDto.getOwnerName()); // Location generateParagraphWithBlankLine("Location: " + reportDto.getLocationName()); // Entity name generateParagraphWithBlankLine("Entity Name: " + reportDto.getEntityName()); // Unit type generateParagraphWithBlankLine("Unit Type: " + reportDto.getUnitType()); // Version generateParagraphWithBlankLine("Version: " + reportDto.getVersion()); getTeamDetails(reportDto); // Query date generateParagraphWithBlankLine("Query Date: " + formatDate(new Date(), dateFormat)); // Query by generateParagraphWithBlankLine("Query By: " + SessionUtil.getCurrentSession().getCurrentUser().getFullname()); // Force a new page document.newPage(); } } protected void generateStatusPage(ReportDto reportDto) { // Status title Paragraph statusTitle = new Paragraph("STATUS", fontBold16); statusTitle.setAlignment(Element.ALIGN_LEFT); document.add(statusTitle); // Status Table PdfPTable statusTable = getPdfPTable(2); // Add headers to the table statusTable.addCell(getPdfPCellSpaningAcross("Processing Status", fontBold14, GREEN_COLOR, 2)); // header // row 1 statusTable.addCell(getPdfPCell("Version", fontBold10, WHITE_COLOR)); statusTable.addCell( getPdfPCell(NumberUtils.getStringFromBigDecimal(reportDto.getVersion()), font10, WHITE_COLOR)); // row 2 statusTable.addCell(getPdfPCell("Status", fontBold10, LIGHTEST_GREEN_COLOR)); getReportStatus(reportDto, statusTable); this.conditionalColorStatus = WHITE_COLOR; this.conditionalColorTeam = WHITE_COLOR; // row 3 getTeamDetails(reportDto, statusTable, conditionalColorTeam); statusTable.addCell(getPdfPCell("Submission Date", fontBold10, conditionalColorStatus)); statusTable.addCell(getPdfPCell(DateUtil.formatDate(reportDto.getSubmissionDate(), dateTimeFormatSimple), font10, conditionalColorStatus)); document.add(statusTable); // Force a new page document.newPage(); } protected void getReportStatus(ReportDto reportDto, PdfPTable statusTable) { statusTable.addCell(getPdfPCell(ReportUtil.convertStatusToString(reportDto.getStatus()), font10, LIGHTEST_GREEN_COLOR)); } protected void getTeamDetails(ReportDto reportDto, PdfPTable statusTable, Color conditionalColorTeam) { // to override in subclass } protected void addTeamDetails(ReportDto reportDto, PdfPTable statusTable, ITeamService teamService, Color conditionalColorTeam) { TeamDto teamDto = teamService.getTeamByRootRef(reportDto.getRootRef()); String teamName = teamDto != null ? teamDto.getTeamName() : "No team assigned/found"; statusTable.addCell(getPdfPCell("Assigned Team", fontBold10, conditionalColorTeam)); statusTable.addCell(getPdfPCell(teamName, font10, conditionalColorTeam)); this.conditionalColorStatus = LIGHTEST_GREEN_COLOR; this.conditionalColorUnit = WHITE_COLOR; this.conditionalColorId = WHITE_COLOR; } /** * This method is used to convert into PDF * * @param param the parameter to be converted to a PDF document and returns it as a byte array. * * @return a ResponseEntity containing the PDF document as a byte array * * @throws IOException possible exception */ public abstract ResponseEntity convert(P param) throws IOException; abstract String getHeaderText(ReportDto reportDto); protected void getTeamDetails(ReportDto reportDto) { // implementation for specific types } protected void generateParagraphWithBlankLine(String text) { Paragraph paragraph = new Paragraph(text, font14); paragraph.setAlignment(Element.ALIGN_CENTER); document.add(paragraph); document.add(new Paragraph("\n", font14)); // Add a blank line } abstract Paragraph getPdfTitle(ReportDto reportDto); }