diff --git a/build.gradle b/build.gradle index 0915992..f49413c 100644 --- a/build.gradle +++ b/build.gradle @@ -55,6 +55,9 @@ dependencies { implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' + implementation 'com.itextpdf:itext7-core:7.2.5' // Changed from 7.x.x to a specific version + implementation 'com.itextpdf:layout:7.2.5' // Match the core version + runtimeOnly 'com.mysql:mysql-connector-j' runtimeOnly 'com.unboundid:unboundid-ldapsdk:6.0.9' diff --git a/src/main/java/com/ffii/lioner/modules/lioner/pdf/service/PdfService.java b/src/main/java/com/ffii/lioner/modules/lioner/pdf/service/PdfService.java index 5cc7624..86e4893 100644 --- a/src/main/java/com/ffii/lioner/modules/lioner/pdf/service/PdfService.java +++ b/src/main/java/com/ffii/lioner/modules/lioner/pdf/service/PdfService.java @@ -222,6 +222,17 @@ public class PdfService extends AbstractBaseEntityService getPdfBlob(Long id) { + String sql = "SELECT " + + " fb.*, " + + " fb.bytes AS blobValue " + + " FROM file_blob fb " + + " WHERE fb.deleted = FALSE " + + " AND fb.id = :id "; + + return jdbcDao.queryForMap(sql, Map.of("id", id)).orElseThrow(NotFoundException::new); + } public Integer getIdByName(String name) { StringBuilder sql = new StringBuilder("SELECT" diff --git a/src/main/java/com/ffii/lioner/modules/lioner/pdf/web/Pdf2Controller.java b/src/main/java/com/ffii/lioner/modules/lioner/pdf/web/Pdf2Controller.java index b8d2de9..0ccd172 100644 --- a/src/main/java/com/ffii/lioner/modules/lioner/pdf/web/Pdf2Controller.java +++ b/src/main/java/com/ffii/lioner/modules/lioner/pdf/web/Pdf2Controller.java @@ -1,5 +1,7 @@ package com.ffii.lioner.modules.lioner.pdf.web; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -15,10 +17,10 @@ import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDField; -import org.apache.poi.ss.formula.atp.Switch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.http.HttpHeaders; @@ -35,9 +37,14 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import com.ffii.core.exception.NotFoundException; import com.ffii.lioner.modules.lioner.commonField.entity.CommonField; import com.ffii.lioner.modules.lioner.commonField.service.CommonFieldService; +import com.ffii.lioner.modules.lioner.pdf.service.PdfService; +import com.itextpdf.forms.PdfAcroForm; +import com.itextpdf.forms.fields.PdfFormField; +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfReader; +import com.itextpdf.kernel.pdf.PdfWriter; @RestController @@ -51,27 +58,159 @@ public class Pdf2Controller { @Autowired private CommonFieldService commonFieldService; + + @Autowired + private PdfService pdfService; - // public Pdf2Controller(CommonFieldService commonFieldService) { - // this.commonFieldService = commonFieldService; - // } + @GetMapping("/get") + public ResponseEntity get(@RequestParam long id, @RequestParam long clientId) { + logger.info("Fetching PDF for id: " + id); + + Map pdfData = null; + try { + pdfData = pdfService.getPdfBlob(id); + } catch (Exception e) { + logger.error("Error calling pdfService.getPdfBlob for ID: " + id, e); + return ResponseEntity.internalServerError().body(null); + } + + if (pdfData == null || !pdfData.containsKey("blobValue")) { + logger.warn("PDF data or 'blobValue' missing for ID: " + id); + return ResponseEntity.notFound().build(); + } + + Object blobObject = pdfData.get("blobValue"); + if (!(blobObject instanceof byte[])) { + logger.error("Value for 'blobValue' is not a byte array for ID: " + id + ". Actual type: " + (blobObject != null ? blobObject.getClass().getName() : "null")); + return ResponseEntity.internalServerError().body(null); + } + + byte[] pdfBytes = (byte[]) blobObject; + logger.info("Original PDF byte array length: " + pdfBytes.length); + + byte[] modifiedPdfBytes; + try (ByteArrayInputStream inputStream = new ByteArrayInputStream(pdfBytes); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + + PdfReader reader = new PdfReader(inputStream); + PdfWriter writer = new PdfWriter(outputStream); + PdfDocument pdfDoc = new PdfDocument(reader, writer); + PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true); + if (form != null) { + // --- START DEBUGGING CODE TO LIST ALL PDF FORM FIELDS --- + logger.info("--- Listing all form fields in the PDF (for ID: {}) ---", id); + if (form.getFormFields().isEmpty()) { + logger.info("No form fields found in this PDF."); + } else { + form.getFormFields().forEach((fieldName, pdfFormField) -> { + logger.info(" - Field Name: '{}', Type: '{}'", fieldName, pdfFormField.getFormType()); + }); + } + logger.info("--- End of form fields list ---"); + // --- END DEBUGGING CODE --- + + // Load common field data + Long commonFieldId = commonFieldService.getByClientId(clientId); + logger.info("id? " + id); + CommonField commonField = commonFieldService.find(commonFieldId).orElse(null); + + if(commonField == null){ + logger.info("No common field data found for clientId: " + clientId); + // do nothing + } else { + // It's highly recommended to check if the field exists before setting its value + // This prevents NullPointerExceptions if a field is missing in a template + + if(id == 1){ // This is IDA + logger.info("Processing IDA form (ID: 1)"); + setValueIfPresent(form, "fill_6", commonField.getName(), "commonField.getName()"); + setValueIfPresent(form, "fill_7", commonField.getNameChi(), "commonField.getNameChi()"); + setValueIfPresent(form, "fill_11", commonField.getIdCard(), "commonField.getIdCard()"); + + } else if(id == 2){ // This is FNA + logger.info("Processing FNA form (ID: 2)"); + setValueIfPresent(form, "fna_a_ name", commonField.getName(), "commonField.getName()"); + setValueIfPresent(form, "fna_a_birth", commonField.getDateOfBirth(), "commonField.getDateOfBirth()"); + setValueIfPresent(form, "fna_a_occupation", commonField.getOccupation(), "commonField.getOccupation()"); + + } else if(id == 3){ // This is HSBCFIN + logger.info("Processing HSBCFIN form (ID: 3)"); + setValueIfPresent(form, "fill_3", commonField.getName(), "commonField.getName()"); + setValueIfPresent(form, "fill_4", commonField.getNameChi(), "commonField.getNameChi()"); + setValueIfPresent(form, "fill_5", commonField.getGender(), "commonField.getGender()"); + setValueIfPresent(form, "fill_6", commonField.getDateOfBirth(), "commonField.getDateOfBirth()"); // Note: fill_6 used here again + setValueIfPresent(form, "fill_9", commonField.getOccupation(), "commonField.getOccupation()"); + } + + // These fields are set unconditionally if commonField is not null + // Ensure these fields exist across all your templates or handle them conditionally + setValueIfPresent(form, "address", "123 Main St, Anytown, USA", "hardcoded address"); + setValueIfPresent(form, "dateOfBirth", "01/01/1990", "hardcoded dateOfBirth"); + } + + //form.flattenFields(); // Flatten fields after setting all values + + } else { + logger.warn("No AcroForm found in PDF for ID: " + id + ". PDF might not have fillable fields."); + } + + pdfDoc.close(); + modifiedPdfBytes = outputStream.toByteArray(); + logger.info("Modified PDF byte array length: " + modifiedPdfBytes.length); + + } catch (IOException e) { + logger.error("Error processing PDF with iText for ID: " + id, e); + return ResponseEntity.internalServerError().body(null); + } + + Resource resource = new ByteArrayResource(modifiedPdfBytes); + String filename = (String) pdfData.getOrDefault("fileName", "document_" + id + ".pdf"); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + filename + "\"") + .contentType(MediaType.APPLICATION_PDF) // Corrected: no charset for binary PDFs + .body(resource); + } + + // Helper method to set field value safely + private void setValueIfPresent(PdfAcroForm form, String fieldName, String value, String valueSource) { + PdfFormField field = form.getField(fieldName); + if (field != null) { + if (value != null) { + try { + field.setValue(value); + logger.info("Set field '{}' to value '{}' (from {})", fieldName, value, valueSource); + } catch (Exception e) { + logger.error("Error setting value for field '{}' with value '{}' (from {}): {}", fieldName, value, valueSource, e.getMessage()); + } + } else { + logger.warn("Value for field '{}' (from {}) is null. Skipping setting value.", fieldName, valueSource); + } + } else { + logger.warn("PDF field '{}' not found in the document. Skipping setting value.", fieldName); + } + } + + //no use // Endpoint to download the blank AcroForm @GetMapping("/download/template") public ResponseEntity downloadPdfTemplate(@RequestParam String formCode) throws IOException { logger.info("download/template formCode:" + formCode); - + //BAD EXAMPLE Path filePath = null; // Paths.get("src/main/resources/template/pdf/template_form.pdf").normalize(); // Path to your blank AcroForm switch (formCode) { - case "HSBCFIN" -> filePath = Paths.get("src/main/resources/template/pdf/HSBCFIN.pdf").normalize(); - case "IDA" -> filePath = Paths.get("src/main/resources/template/pdf/IDA.pdf").normalize(); - case "FNA" -> filePath = Paths.get("src/main/resources/template/pdf/FNA.pdf").normalize(); + case "HSBCFIN" -> filePath = Paths.get("../../../../../resources/templates/pdf/HSBCFIN.pdf").normalize(); + case "IDA" -> filePath = Paths.get("./IDA232.pdf").normalize(); + case "FNA" -> filePath = Paths.get("../../../../../resources/templates/pdf/FNA.pdf").normalize(); }; Resource resource = new UrlResource(filePath.toUri()); + logger.info("File Found: " + (resource != null)); + if (resource.exists()) { return ResponseEntity.ok() .contentType(MediaType.APPLICATION_PDF) @@ -122,6 +261,7 @@ public class Pdf2Controller { } } + //no use // Optional: Endpoint to upload only the form data (FDF/XFDF) @PostMapping("/upload/data") public ResponseEntity uploadFormData(@RequestBody Map formData) { @@ -174,6 +314,27 @@ public class Pdf2Controller { String fieldName = field.getPartialName(); String fieldValue = field.getValueAsString(); + if("IDA".equals(formCode)){ + logger.info("IDA "); + + switch (fieldName) { + case "fill_6" -> commonField.setName(fieldValue); + case "fill_7" -> commonField.setNameChi(fieldValue); + case "fill_11" -> commonField.setIdCard(fieldValue); + + }; + } + + if("IDA".equals(formCode)){ + logger.info("IDA "); + + switch (fieldName) { + case "fna_a_ name" -> commonField.setName(fieldValue); + case "fna_a_birth" -> commonField.setDateOfBirth(fieldValue); + case "fna_a_occupation" -> commonField.setOccupation(fieldValue); + }; + } + if("HSBCFIN".equals(formCode)){ logger.info("HSBCFIN "); @@ -183,7 +344,7 @@ public class Pdf2Controller { case "fill_5" -> commonField.setGender(fieldValue); case "fill_6" -> commonField.setDateOfBirth(fieldValue); - case "fill_9" -> commonField.setOccupation(fileName); + case "fill_9" -> commonField.setOccupation(fieldValue); }; } diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index cd49fab..6f35b9f 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -4,7 +4,7 @@ server: encoding: charset: UTF-8 enabled: true - force: true + force: false port: 8090 error: include-message: always diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4227313..a5563cd 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -4,7 +4,7 @@ server: encoding: charset: UTF-8 enabled: true - force: true + force: false port: 8090 error: include-message: always