From 1b7d16413f83c7bbd51b49e4e6244bbdc10a25df Mon Sep 17 00:00:00 2001 From: kelvinsuen Date: Wed, 9 Jul 2025 14:21:26 +0800 Subject: [PATCH] 0708 demo version --- .../lioner/pdf/service/PdfService.java | 253 +++++++++++++++++- .../lioner/pdf/web/Pdf2Controller.java | 79 +++++- .../modules/lioner/pdf/web/PdfController.java | 16 +- src/main/resources/templates/pdf/HSBCFIN.pdf | Bin 644138 -> 695011 bytes 4 files changed, 332 insertions(+), 16 deletions(-) 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 86e4893..2e11519 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 @@ -1,5 +1,8 @@ package com.ffii.lioner.modules.lioner.pdf.service; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -14,17 +17,29 @@ import java.util.stream.Collectors; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; +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.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ClassPathResource; import com.ffii.lioner.modules.lioner.pdf.entity.Pdf; import com.ffii.lioner.modules.lioner.pdf.entity.PdfRepository; +import com.ffii.lioner.modules.lioner.commonField.entity.CommonField; +import com.ffii.lioner.modules.lioner.commonField.service.CommonFieldService; import com.ffii.lioner.modules.lioner.entity.ImpEvent; import com.ffii.lioner.modules.lioner.pdf.req.UpdatePdfReq; import com.ffii.lioner.modules.lioner.service.FileService; import com.ffii.lioner.modules.common.SecurityUtils; import com.ffii.lioner.modules.common.service.AuditLogService; import com.ffii.lioner.modules.master.entity.SubDivision; +import com.itextpdf.forms.PdfAcroForm; +import com.itextpdf.forms.fields.PdfFormField; +import com.itextpdf.io.source.ByteArrayOutputStream; +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfReader; +import com.itextpdf.kernel.pdf.PdfWriter; import com.fasterxml.jackson.databind.ObjectMapper; import com.ffii.core.exception.NotFoundException; import com.ffii.core.exception.UnprocessableEntityException; @@ -35,6 +50,7 @@ import com.ffii.core.utils.JsonUtils; import com.ffii.core.utils.Params; import jakarta.persistence.Table; +import com.ffii.lioner.modules.lioner.pdf.web.Pdf2Controller; @Service public class PdfService extends AbstractBaseEntityService { @@ -43,11 +59,13 @@ public class PdfService extends AbstractBaseEntityService getAuditLogObject(Map req){ @@ -130,10 +148,7 @@ public class PdfService extends AbstractBaseEntityService { + // 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(templateId.intValue() == 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(templateId.intValue() == 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(templateId.intValue() == 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); + } + + + + + if (!pdfFile.exists()) { + throw new UnprocessableEntityException("PDF template not found."); + } + // try (InputStream is = pdfFile.getInputStream()) { + // return pdfBytes; + // } catch (Exception e) { + // throw new UnprocessableEntityException("Fail to load PDF: " + e); + // } + return modifiedPdfBytes; // return fileService.getfileBlob((long) 2); } @@ -245,4 +378,100 @@ public class PdfService extends AbstractBaseEntityService 0){ + commonField = commonFieldService.find(id).orElse(new CommonField()); + }else{ + commonField = new CommonField(); + } + //logger.info("commonField? " + commonField.toString()); + commonField.setClientId(record.getClientId()); + + logger.info("commonField? " + commonField.getId()); + // Example: Iterate through fields and print values + for (PDField field : acroForm.getFields()) { + String fieldName = field.getPartialName(); + String fieldValue = field.getValueAsString(); + + if (record.getTemplateId().intValue() == 1) { + logger.info("IDA "); + + switch (fieldName) { + case "fill_6" -> commonField.setName(fieldValue); + case "fill_7" -> commonField.setNameChi(fieldValue); + case "fill_11" -> commonField.setIdCard(fieldValue); + + }; + } + + if(record.getTemplateId().intValue() == 2){ + logger.info("FNA "); + + switch (fieldName) { + case "fna_a_name" -> commonField.setName(fieldValue); + case "fna_a_birth" -> commonField.setDateOfBirth(fieldValue); + case "fna_a_occupation" -> commonField.setOccupation(fieldValue); + }; + } + + if(record.getTemplateId().intValue() == 3){ + logger.info("HSBCFIN "); + + switch (fieldName) { + case "fill_3" -> commonField.setName(fieldValue); + case "fill_4" -> commonField.setNameChi(fieldValue); + case "fill_5" -> commonField.setGender(fieldValue); + case "fill_6" -> commonField.setDateOfBirth(fieldValue); + + case "fill_9" -> commonField.setOccupation(fieldValue); + }; + } + + logger.info("end Field: " + field.getPartialName() + ", Value: " + field.getValueAsString()); + // You can save this data to a database, or perform other operations + } + + commonFieldService.save(commonField); + } + logger.info("ended"); + document.close(); + logger.info("closed"); + } + + 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 newline at end of file 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 0ccd172..9dc0995 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 @@ -39,6 +39,7 @@ import org.springframework.web.multipart.MultipartFile; 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.entity.Pdf; import com.ffii.lioner.modules.lioner.pdf.service.PdfService; import com.itextpdf.forms.PdfAcroForm; import com.itextpdf.forms.fields.PdfFormField; @@ -361,8 +362,82 @@ public class Pdf2Controller { return ResponseEntity.ok("PDF saved successfully at " + file.getAbsolutePath()); } catch (IOException e) { return ResponseEntity.status(500).body("Error saving PDF: " + e.getMessage()); - } + } + } - + public void saveCmField(File file, Pdf record) throws IOException { + // byte[] pdfBytes = Files.readAllBytes(file.toPath()); + + // try (FileOutputStream fos = new FileOutputStream(file)) { + // IOUtils.write(pdfBytes, fos); + // } + + //PDDocument document = PDDocument.load(targetLocation.toFile()); + PDDocument document = Loader.loadPDF(file); // Changed! + PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm(); + + logger.info("acroForm? " + (acroForm != null) ); + if (acroForm != null) { + //create CommonField if not exists for this client Id + Long id = commonFieldService.getByClientId(record.getClientId()); + logger.info("id? " + id); + CommonField commonField = null; + if(id > 0){ + commonField = commonFieldService.find(id).orElse(new CommonField()); + }else{ + commonField = new CommonField(); + } + //logger.info("commonField? " + commonField.toString()); + commonField.setClientId(record.getClientId()); + + logger.info("commonField? " + commonField.getId()); + // Example: Iterate through fields and print values + for (PDField field : acroForm.getFields()) { + String fieldName = field.getPartialName(); + String fieldValue = field.getValueAsString(); + + if (record.getTemplateId().intValue() == 1) { + logger.info("IDA "); + + switch (fieldName) { + case "fill_6" -> commonField.setName(fieldValue); + case "fill_7" -> commonField.setNameChi(fieldValue); + case "fill_11" -> commonField.setIdCard(fieldValue); + + }; + } + + if(record.getTemplateId().intValue() == 2){ + logger.info("FNA "); + + switch (fieldName) { + case "fna_a_ name" -> commonField.setName(fieldValue); + case "fna_a_birth" -> commonField.setDateOfBirth(fieldValue); + case "fna_a_occupation" -> commonField.setOccupation(fieldValue); + }; + } + + if(record.getTemplateId().intValue() == 3){ + logger.info("HSBCFIN "); + + switch (fieldName) { + case "fill_3" -> commonField.setName(fieldValue); + case "fill_4" -> commonField.setNameChi(fieldValue); + case "fill_5" -> commonField.setGender(fieldValue); + case "fill_6" -> commonField.setDateOfBirth(fieldValue); + + case "fill_9" -> commonField.setOccupation(fieldValue); + }; + } + + logger.info("end Field: " + field.getPartialName() + ", Value: " + field.getValueAsString()); + // You can save this data to a database, or perform other operations + } + + commonFieldService.save(commonField); + } + logger.info("ended"); + document.close(); + logger.info("closed"); } } diff --git a/src/main/java/com/ffii/lioner/modules/lioner/pdf/web/PdfController.java b/src/main/java/com/ffii/lioner/modules/lioner/pdf/web/PdfController.java index 23c82ff..eac37d3 100644 --- a/src/main/java/com/ffii/lioner/modules/lioner/pdf/web/PdfController.java +++ b/src/main/java/com/ffii/lioner/modules/lioner/pdf/web/PdfController.java @@ -80,9 +80,21 @@ public class PdfController { // Endpoint to serve the initial template PDF @GetMapping(value = "/template", produces = MediaType.APPLICATION_PDF_VALUE) - public ResponseEntity getPdfTemplate() throws IOException { + public ResponseEntity getPdfTemplate(@RequestParam Long templateId, @RequestParam Long clientId) throws IOException { - byte[] pdfBytes = pdfService.getTemplateForm(); + byte[] pdfBytes = pdfService.getTemplateForm(templateId, clientId); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentDispositionFormData("inline", "template_form.pdf"); + headers.setContentType(MediaType.APPLICATION_PDF); + return new ResponseEntity<>(pdfBytes, headers, HttpStatus.OK); + } + + // Get template by template id (Unused now) + @GetMapping(value = "/template/{id}", produces = MediaType.APPLICATION_PDF_VALUE) + public ResponseEntity getPdfTemplateById(@PathVariable Long id) throws IOException { + + byte[] pdfBytes = pdfService.getTemplateForm(id, (long) 0); HttpHeaders headers = new HttpHeaders(); headers.setContentDispositionFormData("inline", "template_form.pdf"); diff --git a/src/main/resources/templates/pdf/HSBCFIN.pdf b/src/main/resources/templates/pdf/HSBCFIN.pdf index aec14da16c332503a37c71d3ee2aeb97b82451f7..19421db3c601623ad9b6d015fdffcfce769d6a23 100644 GIT binary patch delta 41800 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