From 29c0b906b5845bdd935ac20c073b88a8f590c51c Mon Sep 17 00:00:00 2001 From: SGWebFreelancer Date: Wed, 13 Dec 2023 10:33:56 +0800 Subject: [PATCH] BAEL-7278 Modify the code based on the feedback --- .../exceltopdf/ExcelToPDFConverter.java | 140 ++++++++++++++---- pdf-2/src/main/resources/excelsample.xlsx | Bin 9352 -> 9591 bytes pdf-2/src/main/resources/pdfsample.pdf | Bin 1591 -> 1713 bytes 3 files changed, 111 insertions(+), 29 deletions(-) diff --git a/pdf-2/src/main/java/com/baeldung/exceltopdf/ExcelToPDFConverter.java b/pdf-2/src/main/java/com/baeldung/exceltopdf/ExcelToPDFConverter.java index 38318616aa..b4013f0d49 100644 --- a/pdf-2/src/main/java/com/baeldung/exceltopdf/ExcelToPDFConverter.java +++ b/pdf-2/src/main/java/com/baeldung/exceltopdf/ExcelToPDFConverter.java @@ -3,10 +3,13 @@ package com.baeldung.exceltopdf; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.math.BigDecimal; import java.util.Iterator; import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.VerticalAlignment; import org.apache.poi.xssf.usermodel.XSSFColor; import org.apache.poi.xssf.usermodel.XSSFFont; import org.apache.poi.xssf.usermodel.XSSFSheet; @@ -14,31 +17,40 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Cell; + import com.itextpdf.text.BaseColor; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Element; import com.itextpdf.text.Font; +import com.itextpdf.text.FontFactory; import com.itextpdf.text.Paragraph; import com.itextpdf.text.Phrase; import com.itextpdf.text.pdf.PdfPCell; import com.itextpdf.text.pdf.PdfPTable; import com.itextpdf.text.pdf.PdfWriter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class ExcelToPDFConverter { + private static final Logger logger = LogManager.getLogger(ExcelToPDFConverter.class); + public static XSSFWorkbook readExcelFile(String excelFilePath) throws IOException { FileInputStream inputStream = new FileInputStream(excelFilePath); XSSFWorkbook workbook = new XSSFWorkbook(inputStream); inputStream.close(); return workbook; } + private static Document createPDFDocument(String pdfFilePath) throws IOException, DocumentException { Document document = new Document(); PdfWriter.getInstance(document, new FileOutputStream(pdfFilePath)); document.open(); return document; } + public static void convertExcelToPDF(String excelFilePath, String pdfFilePath) throws IOException, DocumentException { XSSFWorkbook workbook = readExcelFile(excelFilePath); Document document = createPDFDocument(pdfFilePath); @@ -48,6 +60,7 @@ public class ExcelToPDFConverter { // Add header with sheet name as title Paragraph title = new Paragraph(worksheet.getSheetName(), new Font(Font.FontFamily.HELVETICA, 18, Font.BOLD)); + title.setSpacingAfter(20f); title.setAlignment(Element.ALIGN_CENTER); document.add(title); @@ -63,7 +76,8 @@ public class ExcelToPDFConverter { } private static void createAndAddTable(XSSFSheet worksheet, Document document) throws DocumentException, IOException { - PdfPTable table = new PdfPTable(worksheet.getRow(0).getPhysicalNumberOfCells()); + PdfPTable table = new PdfPTable(worksheet.getRow(0) + .getPhysicalNumberOfCells()); table.setWidthPercentage(100); addTableHeader(worksheet, table); addTableData(worksheet, table); @@ -74,9 +88,27 @@ public class ExcelToPDFConverter { Row headerRow = worksheet.getRow(0); for (int i = 0; i < headerRow.getPhysicalNumberOfCells(); i++) { Cell cell = headerRow.getCell(i); - String headerText = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : String.valueOf(cell.getNumericCellValue()); + + String headerText; + switch (cell.getCellType()) { + case STRING: + headerText = cell.getStringCellValue(); + break; + case NUMERIC: + headerText = String.valueOf(BigDecimal.valueOf(cell.getNumericCellValue())); + break; + case BLANK: + headerText = ""; // or null + break; + default: + logger.warn("Unsupported cell type: {}", cell.getCellType()); + headerText = ""; // or throw an exception + break; + } + PdfPCell headerCell = new PdfPCell(new Phrase(headerText, getCellStyle(cell))); - headerCell.setHorizontalAlignment(Element.ALIGN_CENTER); + setBackgroundColor(cell, headerCell); + setCellAlignment(cell, headerCell); table.addCell(headerCell); } } @@ -91,42 +123,81 @@ public class ExcelToPDFConverter { for (int i = 0; i < row.getPhysicalNumberOfCells(); i++) { Cell cell = row.getCell(i); String cellValue; - if (cell != null) { - if (cell.getCellType() == CellType.STRING) { - cellValue = cell.getStringCellValue(); - } else if (cell.getCellType() == CellType.NUMERIC) { - cellValue = String.valueOf(cell.getNumericCellValue()); - } else { - cellValue = ""; - } + if (cell.getCellType() == CellType.STRING) { + cellValue = cell.getStringCellValue(); + } else if (cell.getCellType() == CellType.NUMERIC) { + cellValue = String.valueOf(cell.getNumericCellValue()); } else { cellValue = ""; } PdfPCell cellPdf = new PdfPCell(new Phrase(cellValue, getCellStyle(cell))); - // Set background color - short bgColorIndex = cell.getCellStyle() - .getFillForegroundColor(); - if (bgColorIndex != IndexedColors.AUTOMATIC.getIndex()) { - XSSFColor bgColor = (XSSFColor) cell.getCellStyle() - .getFillForegroundColorColor(); - if (bgColor != null) { - byte[] rgb = bgColor.getRGB(); - if (rgb != null && rgb.length == 3) { - cellPdf.setBackgroundColor(new BaseColor(rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF)); - } - } - } - - cellPdf.setHorizontalAlignment(Element.ALIGN_CENTER); + setBackgroundColor(cell, cellPdf); + setCellAlignment(cell, cellPdf); table.addCell(cellPdf); } } } - private static Font getCellStyle(Cell cell) throws DocumentException, IOException { - Font font = new Font(Font.FontFamily.HELVETICA, 12); + private static void setBackgroundColor(Cell cell, PdfPCell cellPdf) { + // Set background color + short bgColorIndex = cell.getCellStyle() + .getFillForegroundColor(); + if (bgColorIndex != IndexedColors.AUTOMATIC.getIndex()) { + XSSFColor bgColor = (XSSFColor) cell.getCellStyle() + .getFillForegroundColorColor(); + if (bgColor != null) { + byte[] rgb = bgColor.getRGB(); + if (rgb != null && rgb.length == 3) { + cellPdf.setBackgroundColor(new BaseColor(rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF)); + } + } + } + } + + private static void setCellAlignment(Cell cell, PdfPCell cellPdf) { CellStyle cellStyle = cell.getCellStyle(); - org.apache.poi.ss.usermodel.Font cellFont = cell.getSheet().getWorkbook().getFontAt(cellStyle.getFontIndexAsInt()); + + HorizontalAlignment horizontalAlignment = cellStyle.getAlignment(); + VerticalAlignment verticalAlignment = cellStyle.getVerticalAlignment(); + + switch (horizontalAlignment) { + case LEFT: + cellPdf.setHorizontalAlignment(Element.ALIGN_LEFT); + break; + case CENTER: + cellPdf.setHorizontalAlignment(Element.ALIGN_CENTER); + break; + case JUSTIFY: + case FILL: + cellPdf.setVerticalAlignment(Element.ALIGN_JUSTIFIED); + break; + case RIGHT: + cellPdf.setHorizontalAlignment(Element.ALIGN_RIGHT); + break; + } + + switch (verticalAlignment) { + case TOP: + cellPdf.setVerticalAlignment(Element.ALIGN_TOP); + break; + case CENTER: + cellPdf.setVerticalAlignment(Element.ALIGN_MIDDLE); + break; + case JUSTIFY: + cellPdf.setVerticalAlignment(Element.ALIGN_JUSTIFIED); + break; + case BOTTOM: + cellPdf.setVerticalAlignment(Element.ALIGN_BOTTOM); + break; + } + } + + private static Font getCellStyle(Cell cell) throws DocumentException, IOException { + Font font = new Font(); + CellStyle cellStyle = cell.getCellStyle(); + org.apache.poi.ss.usermodel.Font cellFont = cell.getSheet() + .getWorkbook() + .getFontAt(cellStyle.getFontIndexAsInt()); short fontColorIndex = cellFont.getColor(); if (fontColorIndex != IndexedColors.AUTOMATIC.getIndex() && cellFont instanceof XSSFFont) { @@ -138,6 +209,7 @@ public class ExcelToPDFConverter { } } } + if (cellFont.getItalic()) { font.setStyle(Font.ITALIC); } @@ -156,6 +228,16 @@ public class ExcelToPDFConverter { if (cellFont.getBold()) { font.setStyle(Font.BOLD); } + + String fontName = cellFont.getFontName(); + if (FontFactory.isRegistered(fontName)) { + font.setFamily(fontName); // Use extracted font family if supported by iText + } else { + logger.warn("Unsupported font type: {}", fontName); + // - Use a fallback font (e.g., Helvetica) + font.setFamily("Helvetica"); + } + return font; } diff --git a/pdf-2/src/main/resources/excelsample.xlsx b/pdf-2/src/main/resources/excelsample.xlsx index fb83598a71b1b53eed3538c0f6182cd1aca76f48..771d312ae0146760a9f537e3eb71272b9df79a07 100644 GIT binary patch delta 3732 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 delta 3489 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 diff --git a/pdf-2/src/main/resources/pdfsample.pdf b/pdf-2/src/main/resources/pdfsample.pdf index f646f54db7a6a37969462df2da737e4eb4e949c4..320931762ae109d8c8a23e77131b969818f8a054 100644 GIT binary patch delta 1023 zcmdnavypd#V!gSkp}t#YPDyH!zFSUWNvca~a(+sxon3KBQEFl?SH+yE;irRc8;IQf zt^ID2UO?&BEz=z4PYF_(cdq1RCeMDRhl zx;A}Xn-5DkWvcNdc|47%6>!cx8qy}%ruX2^?~gY(oBlb?TRv&Qp9^j#_C11Uz2z?M z*>iH5$~&Pa>Gk14jcT4Ov+7j@GbXtd@=BP4(_$5_-bo(o^0VAetp61Y zR0CGBz*FexSKqC=J6(V*uvV48nEO}WPiRtM@}0zC;;D2&rSs9RWj~S<(kg?2W5x?vG z$)aC8;%{daI9SvlEA3p*yZUp?S=P;}uk!*Qfk7h4*dCC7b(ZAkfnRoSu1&cPY z9}hVH=4be{qxbU<-Ojd^nbDkk>EI%#=T}!fOp`lj`@gIE-^AF-FXJwzBvqVDne}&X z?7H2SJ`!`b)tjgL-OJw9b5pp8HS1u4iuk6==7VlFsLteE)l`^<5Ej(Je76B_{f0YGu6o7V?Lkt%FT`^HwG{CJkdl z17jmoOKk%Sbpr!+O?}_|6c=Cy(QvUcGB7eWG%|!LnEaT(h8AWRVnB-~PhmB8N;66|GE6csPf0Z~F|#l=G&4&w kPcunLv$QlaNU}&Yva=ziVlpqA0++F+A(yJEtG^o;0Fu|Xq5uE@ delta 879 zcmdnUyPao(V!fG>rM_EcPDyH!zFSUWNvca~a(+sxon3KBQEFl?SH+y(u+u@e9R&8g z)_&%}H}B1x>VAiLEfW}4-3i@j;P*#^Wt~X<{7COIo^NX&ck6upwxueq+MEAsWUM5A zVC9PAqOSd#2RzzqzJFt8bY1a&)#UuUJKjDh`t^h5{%lX~FU#uxh4!{R)zS|$P}=+N z`@VJSR=0aC{C3Z{>l&NhdV?P?cuOXGEpOm!;&Nz{x4s~?Yum2q&n7Vi8~e|ce^B2a zop5PGUrk)Ob;6U{I*muoJuK0(f#M%@a_@?Dw=YqxnK11^@0OPI%(%G)?;oY- zYzvD-1yl|>%wcB#x`%)7xrO1`GagqJZ9OuloOQCyJK1Gnf|Z4hx8!B!-P zvwmMyP1L!aM?#JB`NcOXD!zZ_G_mUEl{0*kCp)HEcC38<%2sb-{iHq7PuAVska}zS znc$#5$_IBoJUMgHhnGozc&4BAGLYq*to>1T`?eQ1C%9bQls?aV>WaOYv1YoL^=}=0 zBx$F6dRb~+$BeFJCp|-yXD)kUAm5d_d+#H~^KKtjhMwt`CzcK9#I@VgDBH6Z^W8?|mv~>b286EAFf|qi(%a ziR*vG4$jJl`|Il_+uJ1BMYYRqIJ4)HjM}Exvm@4UPd*09bxUui#(w?JtGyb!7zwvb1tk2BG#BO9@VQOJyHra+%3eJvYH50cm zHc~JE0fjsTE-=Huz|zQc@+wwUdjmsb3^{W{Lv%4i12Yp0F+&5($$V_)P6nn)#wp3k xsg|Y|7OBQ5sb*=(rb(6t21%C2mc}W@X?8Y*R7`GXQ{XZ(v*c1$b@g}S0s!cQhMoWb