From a28f3dcfdc71ead0d0fb6bb3ba9d4e696dd9c5bb Mon Sep 17 00:00:00 2001 From: Kasun Jayatilaka Date: Fri, 12 Sep 2025 01:24:24 +0200 Subject: [PATCH] added pdf implementation demo files --- PdfConverter.java | 336 ++++++++++++++++++++++++++++++++++++++++ PdfConverterReport.java | 142 +++++++++++++++++ PdfParameter.java | 41 +++++ 3 files changed, 519 insertions(+) create mode 100644 PdfConverter.java create mode 100644 PdfConverterReport.java create mode 100644 PdfParameter.java diff --git a/PdfConverter.java b/PdfConverter.java new file mode 100644 index 0000000..dbf3ece --- /dev/null +++ b/PdfConverter.java @@ -0,0 +1,336 @@ +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); +} \ No newline at end of file diff --git a/PdfConverterReport.java b/PdfConverterReport.java new file mode 100644 index 0000000..bb33af4 --- /dev/null +++ b/PdfConverterReport.java @@ -0,0 +1,142 @@ +package com.example.reporting.services.impl.pdf; + +import static com.example.reporting.util.ResourceUtils.OTHER_RESOURCE_CODES; +import static com.example.reporting.util.DateUtil.formatDate; +import static com.example.reporting.util.ReportUtil.getRegionName; +import static com.example.reporting.util.Constants.*; +import static com.example.reporting.util.NumberUtils.getStringFromBigDecimal; +import static com.example.reporting.util.NumberUtils.getStringFromLong; +import static com.example.reporting.util.PdfUtil.formatDocumentType; +import static com.example.reporting.util.PdfUtil.getPdfPCell; +import static com.example.reporting.util.PdfUtil.getPdfPCellSpaningAcross; +import static com.example.reporting.util.PdfUtil.getPdfPTable; +import static com.example.reporting.util.ReferenceUtil.getResourceLabel; +import static com.example.reporting.util.StringUtils.getYesNo; +import static com.example.reporting.util.StringUtils.MONTHS; + +import java.awt.Color; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hibernate.HibernateException; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.server.ResponseStatusException; + +import com.lowagie.text.DocumentException; +import com.lowagie.text.Element; +import com.lowagie.text.Paragraph; +import com.lowagie.text.pdf.PdfPTable; +import com.lowagie.text.pdf.PdfWriter; + +import com.example.reporting.dto.ReportDataDto; +import com.example.reporting.dto.database.*; +import com.example.reporting.dto.reference.*; +import com.example.reporting.services.IReferenceListService; +import com.example.reporting.services.database.ITeamService; + +/** + * Abstract class for PDF converter for reports + */ +public abstract class PdfConverterReport extends PdfConverter { + + protected final ITeamService teamService; + protected final IReferenceListService referenceListService; + + protected Map referenceList9498; + protected Map referenceListResources4435; + protected Map referenceList9480; + protected Map referenceListSubstances9147; + protected Map referenceListComponents5193; + protected Map referenceListResources4860; + + /** + * Creates a new instance of PdfConverterReport. + */ + public static PdfConverterReport of(ReportDto reportDto, + ITeamService teamService, IReferenceListService referenceListService) + throws DocumentException, IOException { + switch (reportDto.getType()) { + case TYPE_A: + return new PdfConverterTypeA(teamService, referenceListService); + case TYPE_B: + return new PdfConverterTypeB(teamService, referenceListService); + case TYPE_C: + return new PdfConverterTypeC(teamService, referenceListService); + default: + throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY, + "Unknown type: " + reportDto.getType()); + } + } + + protected PdfConverterReport(ITeamService teamService, + IReferenceListService referenceListService) throws DocumentException, IOException { + super(); + this.teamService = teamService; + this.referenceListService = referenceListService; + } + + @Override + public ResponseEntity convert(ReportParameter param) throws IOException { + return this.convert(param.submitDto(), param.headers(), param.isNotReadOnly()); + } + + ResponseEntity convert(ReportDataDto submitDto, HttpHeaders headers, boolean isNotReadOnly) + throws IOException { + + PdfWriter writer = null; + try { + loadReferenceLists(submitDto.getReportDto().getReportRoot().getYear(), isNotReadOnly); + + writer = PdfWriter.getInstance(document, baos); + generateTitlePage(submitDto.getReportDto(), writer); + + generateStatusPage(submitDto.getReportDto()); + + generateMasterDataPage(submitDto.getReportDto(), submitDto.getContactPersonList()); + + generateEntityDataPage(submitDto.getReportDto(), submitDto.getCapacityDtoList(), + submitDto.getSubUnitDtoList(), submitDto.getClassificationDtoList(), + submitDto.getLineDtoList()); + + generateResourcesPage(submitDto.getResourceInputDtoList(), submitDto.getResourceComponentDtoList(), + submitDto.getReportDto(), submitDto.getResourceDesignationDtoList()); + + generateDataPage(submitDto); + + generateValidationPage(submitDto.getReportDto(), submitDto.getExternalContributorDtoList()); + + generateAttachmentsPage(submitDto.getDocumentDtoList()); + + generateConclusionPage(submitDto.getReportDto()); + } finally { + document.close(); + if (writer != null) { + writer.close(); + } + } + + return new ResponseEntity<>(baos.toByteArray(), headers, HttpStatus.OK); + } + + protected abstract void generateDataPage(ReportDataDto submitDto); + + // Rest of the class follows similar pattern with generic naming... + // [Additional methods would continue with similar transformations] + + private void loadReferenceLists(String year, boolean isNotReadOnly) { + this.referenceList9498 = referenceListService.getEntityTypes9498(year); + this.referenceListResources4435 = referenceListService.getReferenceListResourceComponents4435(year, isNotReadOnly); + this.referenceList9480 = referenceListService.getReferenceListSystems9480(year, isNotReadOnly); + // Additional reference list loading... + } + + @Override + protected Paragraph getPdfTitle(ReportDto reportDto) { + return new Paragraph("Report for the year " + reportDto.getReportRoot().getYear(), fontBold22); + } +} \ No newline at end of file diff --git a/PdfParameter.java b/PdfParameter.java new file mode 100644 index 0000000..5c1bce3 --- /dev/null +++ b/PdfParameter.java @@ -0,0 +1,41 @@ +package com.example.reporting.services.impl.pdf; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +import com.example.reporting.dto.database.ReportDto; + +/** + * Interface for PDF Parameter + */ +interface PdfParameter { + + /** + * Returns the filename for the content disposition header. + * + * @param dto The ReportDto object to extract the filename from. + * @return The filename for the content disposition header. + */ + public abstract String getContentDispositionFilename(ReportDto dto); + + /** + * Returns the HttpHeaders for the PDF response. + * + * @return The HttpHeaders for the PDF response. + */ + public abstract HttpHeaders headers(); + + /** + * Returns the HttpHeaders for the PDF response. + * + * @param dto The ReportDto object to extract the headers from. + * @return The HttpHeaders for the PDF response. + */ + public default HttpHeaders headers(ReportDto dto) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_PDF); + headers.setContentDispositionFormData("attachment", getContentDispositionFilename(dto)); + + return headers; + } +} \ No newline at end of file