|
|
@@ -4,12 +4,15 @@ import java.io.ByteArrayInputStream; |
|
|
import java.io.ByteArrayOutputStream; |
|
|
import java.io.ByteArrayOutputStream; |
|
|
import java.io.File; |
|
|
import java.io.File; |
|
|
import java.io.IOException; |
|
|
import java.io.IOException; |
|
|
|
|
|
import java.time.LocalDate; |
|
|
|
|
|
import java.util.Optional; |
|
|
|
|
|
|
|
|
import org.apache.pdfbox.Loader; |
|
|
import org.apache.pdfbox.Loader; |
|
|
import org.apache.pdfbox.pdmodel.PDDocument; |
|
|
import org.apache.pdfbox.pdmodel.PDDocument; |
|
|
import org.apache.pdfbox.pdmodel.PDPage; |
|
|
import org.apache.pdfbox.pdmodel.PDPage; |
|
|
import org.springframework.stereotype.Service; |
|
|
import org.springframework.stereotype.Service; |
|
|
|
|
|
|
|
|
|
|
|
import com.ffii.lioner.modules.lioner.pdf.entity.FormSigPage; |
|
|
import com.itextpdf.forms.PdfAcroForm; |
|
|
import com.itextpdf.forms.PdfAcroForm; |
|
|
import com.itextpdf.kernel.pdf.PdfDocument; |
|
|
import com.itextpdf.kernel.pdf.PdfDocument; |
|
|
import com.itextpdf.kernel.pdf.PdfReader; |
|
|
import com.itextpdf.kernel.pdf.PdfReader; |
|
|
@@ -19,394 +22,250 @@ import com.itextpdf.kernel.utils.PdfMerger; |
|
|
@Service |
|
|
@Service |
|
|
public class PdfMergeService { |
|
|
public class PdfMergeService { |
|
|
|
|
|
|
|
|
|
|
|
private static final String SKIP_AND_APPEND = "SKIP_AND_APPEND"; |
|
|
|
|
|
|
|
|
|
|
|
private final FormSigPageService formSigPageService; |
|
|
|
|
|
|
|
|
|
|
|
public PdfMergeService(FormSigPageService formSigPageService) { |
|
|
|
|
|
this.formSigPageService = formSigPageService; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// --- ACROFORM REMOVAL HELPER METHOD --- |
|
|
// --- ACROFORM REMOVAL HELPER METHOD --- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Flattens form fields, preserving their appearance (text/checkmarks) |
|
|
|
|
|
|
|
|
* Flattens form fields, preserving their appearance (text/checkmarks) |
|
|
* but making the PDF plain and non-interactive. |
|
|
* but making the PDF plain and non-interactive. |
|
|
*/ |
|
|
*/ |
|
|
private void flattenPdf(PdfDocument doc) { |
|
|
private void flattenPdf(PdfDocument doc) { |
|
|
// Get the AcroForm instance for the document. 'true' creates it if none exists. |
|
|
|
|
|
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(doc, true); |
|
|
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(doc, true); |
|
|
|
|
|
|
|
|
// This method automatically converts all field values into static content |
|
|
|
|
|
// and removes the form dictionary (making the PDF plain). |
|
|
|
|
|
acroForm.flattenFields(); |
|
|
|
|
|
|
|
|
acroForm.flattenFields(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public void removePdfPassword() throws IOException { |
|
|
public void removePdfPassword() throws IOException { |
|
|
// Load PDF A and PDF B |
|
|
|
|
|
//String filePathA = "C:\\dev\\pdf\\pdfA.pdf"; |
|
|
|
|
|
//File fileA = new File(filePathA); |
|
|
|
|
|
|
|
|
|
|
|
String filePathB = "C:\\dev\\pdf\\pdfB.pdf"; |
|
|
String filePathB = "C:\\dev\\pdf\\pdfB.pdf"; |
|
|
File fileB = new File(filePathB); |
|
|
File fileB = new File(filePathB); |
|
|
|
|
|
|
|
|
String outputPath = "C:\\dev\\pdf\\pdfOut.pdf"; |
|
|
String outputPath = "C:\\dev\\pdf\\pdfOut.pdf"; |
|
|
|
|
|
|
|
|
try ( |
|
|
try ( |
|
|
//PDDocument pdfA = Loader.loadPDF(fileA); |
|
|
|
|
|
PDDocument pdfB = Loader.loadPDF(fileB); |
|
|
PDDocument pdfB = Loader.loadPDF(fileB); |
|
|
|
|
|
|
|
|
PDDocument mergedPdf = new PDDocument()) { |
|
|
PDDocument mergedPdf = new PDDocument()) { |
|
|
|
|
|
|
|
|
for (int i = 0; i < pdfB.getNumberOfPages(); i++) { |
|
|
for (int i = 0; i < pdfB.getNumberOfPages(); i++) { |
|
|
PDPage page = pdfB.getPage(i); |
|
|
PDPage page = pdfB.getPage(i); |
|
|
mergedPdf.addPage(page); |
|
|
mergedPdf.addPage(page); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
//this is for remove the password and copy the whole pdf |
|
|
|
|
|
pdfB.setAllSecurityToBeRemoved(true); |
|
|
pdfB.setAllSecurityToBeRemoved(true); |
|
|
pdfB.save(new File(outputPath)); |
|
|
pdfB.save(new File(outputPath)); |
|
|
|
|
|
|
|
|
// Save the merged PDF |
|
|
|
|
|
mergedPdf.save(new File(outputPath)); |
|
|
mergedPdf.save(new File(outputPath)); |
|
|
mergedPdf.close(); |
|
|
mergedPdf.close(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void mergePdfs() throws IOException { |
|
|
public void mergePdfs() throws IOException { |
|
|
// This method uses PDFBox and is left unchanged as the focus was iText 7 |
|
|
|
|
|
String filePathA = "C:\\dev\\pdf\\pdfA.pdf"; |
|
|
String filePathA = "C:\\dev\\pdf\\pdfA.pdf"; |
|
|
File fileA = new File(filePathA); |
|
|
File fileA = new File(filePathA); |
|
|
|
|
|
|
|
|
String filePathB = "C:\\dev\\pdf\\pdfB.pdf"; |
|
|
String filePathB = "C:\\dev\\pdf\\pdfB.pdf"; |
|
|
File fileB = new File(filePathB); |
|
|
File fileB = new File(filePathB); |
|
|
|
|
|
|
|
|
String outputPath = "C:\\dev\\pdf\\pdfOut.pdf"; |
|
|
String outputPath = "C:\\dev\\pdf\\pdfOut.pdf"; |
|
|
|
|
|
|
|
|
try ( |
|
|
try ( |
|
|
PDDocument pdfA = Loader.loadPDF(fileA); |
|
|
PDDocument pdfA = Loader.loadPDF(fileA); |
|
|
PDDocument pdfB = Loader.loadPDF(fileB); |
|
|
PDDocument pdfB = Loader.loadPDF(fileB); |
|
|
|
|
|
|
|
|
PDDocument mergedPdf = new PDDocument()) { |
|
|
PDDocument mergedPdf = new PDDocument()) { |
|
|
|
|
|
|
|
|
for (int i = 0; i < pdfB.getNumberOfPages(); i++) { |
|
|
for (int i = 0; i < pdfB.getNumberOfPages(); i++) { |
|
|
PDPage page = pdfB.getPage(i); |
|
|
PDPage page = pdfB.getPage(i); |
|
|
mergedPdf.addPage(page); |
|
|
mergedPdf.addPage(page); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
mergedPdf.save(new File(outputPath)); |
|
|
mergedPdf.save(new File(outputPath)); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public byte[] flatPdfsItext7(byte[] pdfABytes) throws IOException { |
|
|
public byte[] flatPdfsItext7(byte[] pdfABytes) throws IOException { |
|
|
|
|
|
|
|
|
// --- STEP 1: Flatten PDF A and get the modified bytes --- |
|
|
|
|
|
byte[] pdfAFlattenedBytes; |
|
|
byte[] pdfAFlattenedBytes; |
|
|
try (PdfReader readerA = new PdfReader(new ByteArrayInputStream(pdfABytes)); |
|
|
try (PdfReader readerA = new PdfReader(new ByteArrayInputStream(pdfABytes)); |
|
|
ByteArrayOutputStream tempBaosA = new ByteArrayOutputStream(); |
|
|
ByteArrayOutputStream tempBaosA = new ByteArrayOutputStream(); |
|
|
PdfWriter tempWriterA = new PdfWriter(tempBaosA); |
|
|
PdfWriter tempWriterA = new PdfWriter(tempBaosA); |
|
|
PdfDocument docA_mod = new PdfDocument(readerA, tempWriterA)) { |
|
|
PdfDocument docA_mod = new PdfDocument(readerA, tempWriterA)) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flattenPdf(docA_mod); |
|
|
flattenPdf(docA_mod); |
|
|
docA_mod.close(); // IMPORTANT: Close to finalize writing to tempBaosA |
|
|
|
|
|
|
|
|
docA_mod.close(); |
|
|
pdfAFlattenedBytes = tempBaosA.toByteArray(); |
|
|
pdfAFlattenedBytes = tempBaosA.toByteArray(); |
|
|
return tempBaosA.toByteArray(); |
|
|
return tempBaosA.toByteArray(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public byte[] mergePdfsItext7(String formCode, byte[] pdfABytes, byte[] pdfBBytes) throws IOException { |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Merge upload1 (signature) PDF using config from form_sig_page. Uses asOfDate for versioning. |
|
|
|
|
|
*/ |
|
|
|
|
|
public byte[] mergePdfsItext7(String formCode, LocalDate asOfDate, byte[] pdfABytes, byte[] pdfBBytes) throws IOException { |
|
|
|
|
|
Optional<FormSigPage> configOpt = formSigPageService.getUpload1Config(formCode, asOfDate); |
|
|
|
|
|
|
|
|
// Defined constants for clarity |
|
|
|
|
|
final int IDA_SIG_PAGE = 15; // Page to skip for IDA |
|
|
|
|
|
final int FNA_SIG_PAGE = 10; // Page to skip for FNA |
|
|
|
|
|
final int HSBC_REP_PAGE = 11; // Page to replace for HSBCFIN |
|
|
|
|
|
final int HSBCA31_REP_START = 28; // Start page to replace for HSBCA31 (Page 28) |
|
|
|
|
|
final int HSBCA31_REP_END = 29; // End page to replace for HSBCA31 (Page 29) |
|
|
|
|
|
final int HSBCA31_REP_COUNT = 2; // Number of pages from pdfB to insert |
|
|
|
|
|
|
|
|
|
|
|
final int MLB03S_REP_PAGE = 9; |
|
|
|
|
|
final int MLFNA_REP_PAGE = 4; |
|
|
|
|
|
|
|
|
|
|
|
final int SLFNA_REP_PAGE = 5; |
|
|
|
|
|
final int SLAPP_REP_PAGE = 17; |
|
|
|
|
|
final int SLGII_REP_PAGE = 13; |
|
|
|
|
|
|
|
|
|
|
|
// --- STEP 1: Flatten PDF A and get the modified bytes --- |
|
|
|
|
|
byte[] pdfAFlattenedBytes; |
|
|
byte[] pdfAFlattenedBytes; |
|
|
try (PdfReader readerA = new PdfReader(new ByteArrayInputStream(pdfABytes)); |
|
|
try (PdfReader readerA = new PdfReader(new ByteArrayInputStream(pdfABytes)); |
|
|
ByteArrayOutputStream tempBaosA = new ByteArrayOutputStream(); |
|
|
ByteArrayOutputStream tempBaosA = new ByteArrayOutputStream(); |
|
|
PdfWriter tempWriterA = new PdfWriter(tempBaosA); |
|
|
PdfWriter tempWriterA = new PdfWriter(tempBaosA); |
|
|
PdfDocument docA_mod = new PdfDocument(readerA, tempWriterA)) { |
|
|
PdfDocument docA_mod = new PdfDocument(readerA, tempWriterA)) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flattenPdf(docA_mod); |
|
|
flattenPdf(docA_mod); |
|
|
docA_mod.close(); // IMPORTANT: Close to finalize writing to tempBaosA |
|
|
|
|
|
|
|
|
docA_mod.close(); |
|
|
pdfAFlattenedBytes = tempBaosA.toByteArray(); |
|
|
pdfAFlattenedBytes = tempBaosA.toByteArray(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// --- STEP 2: Flatten PDF B and get the modified bytes (if needed) --- |
|
|
|
|
|
byte[] pdfBFlattenedBytes = null; |
|
|
byte[] pdfBFlattenedBytes = null; |
|
|
if (pdfBBytes != null && pdfBBytes.length > 0) { |
|
|
if (pdfBBytes != null && pdfBBytes.length > 0) { |
|
|
try (PdfReader readerB = new PdfReader(new ByteArrayInputStream(pdfBBytes)); |
|
|
try (PdfReader readerB = new PdfReader(new ByteArrayInputStream(pdfBBytes)); |
|
|
ByteArrayOutputStream tempBaosB = new ByteArrayOutputStream(); |
|
|
ByteArrayOutputStream tempBaosB = new ByteArrayOutputStream(); |
|
|
PdfWriter tempWriterB = new PdfWriter(tempBaosB); |
|
|
PdfWriter tempWriterB = new PdfWriter(tempBaosB); |
|
|
PdfDocument docB_mod = new PdfDocument(readerB, tempWriterB)) { |
|
|
PdfDocument docB_mod = new PdfDocument(readerB, tempWriterB)) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flattenPdf(docB_mod); |
|
|
flattenPdf(docB_mod); |
|
|
docB_mod.close(); // IMPORTANT: Close to finalize writing to tempBaosB |
|
|
|
|
|
|
|
|
docB_mod.close(); |
|
|
pdfBFlattenedBytes = tempBaosB.toByteArray(); |
|
|
pdfBFlattenedBytes = tempBaosB.toByteArray(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// --- STEP 3: Perform the merge using the flattened bytes --- |
|
|
|
|
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
|
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
|
|
PdfWriter writer = new PdfWriter(baos); |
|
|
PdfWriter writer = new PdfWriter(baos); |
|
|
PdfDocument mergedPdf = new PdfDocument(writer); |
|
|
PdfDocument mergedPdf = new PdfDocument(writer); |
|
|
PdfReader readerA_merge = new PdfReader(new ByteArrayInputStream(pdfAFlattenedBytes)); |
|
|
PdfReader readerA_merge = new PdfReader(new ByteArrayInputStream(pdfAFlattenedBytes)); |
|
|
PdfDocument docA = new PdfDocument(readerA_merge)) { |
|
|
|
|
|
|
|
|
PdfDocument docA = new PdfDocument(readerA_merge)) { |
|
|
|
|
|
|
|
|
// ⭐️ FIX: PdfMerger is NOT AutoCloseable, so we instantiate it here. |
|
|
|
|
|
PdfMerger merger = new PdfMerger(mergedPdf); |
|
|
PdfMerger merger = new PdfMerger(mergedPdf); |
|
|
|
|
|
|
|
|
int totalPagesA = docA.getNumberOfPages(); |
|
|
int totalPagesA = docA.getNumberOfPages(); |
|
|
|
|
|
|
|
|
// --- Multi/Single Page Replacement Logic --- |
|
|
|
|
|
int repPage = -1; |
|
|
|
|
|
int repStartA = -1; |
|
|
|
|
|
int repEndA = -1; |
|
|
|
|
|
int repCountB = -1; |
|
|
|
|
|
boolean isMultiPageReplace = false; |
|
|
|
|
|
|
|
|
|
|
|
// Single Page Replacements |
|
|
|
|
|
if ("MLB03S".equals(formCode)) { |
|
|
|
|
|
repPage = MLB03S_REP_PAGE; |
|
|
|
|
|
} else if ("MLFNA_EN".equals(formCode) || "MLFNA_CHI".equals(formCode)) { |
|
|
|
|
|
repPage = MLFNA_REP_PAGE; |
|
|
|
|
|
} else if ("SLFNA_EN".equals(formCode) || "SLFNA_CHI".equals(formCode)) { |
|
|
|
|
|
repPage = SLFNA_REP_PAGE; |
|
|
|
|
|
} else if ("SLAPP".equals(formCode)) { |
|
|
|
|
|
repPage = SLAPP_REP_PAGE; |
|
|
|
|
|
} else if ("SLGII".equals(formCode)) { |
|
|
|
|
|
repPage = SLGII_REP_PAGE; |
|
|
|
|
|
} |
|
|
|
|
|
// Multi-Page Replacement HSBCA31 |
|
|
|
|
|
else if ("HSBCA31".equals(formCode) && totalPagesA >= HSBCA31_REP_END) { |
|
|
|
|
|
isMultiPageReplace = true; |
|
|
|
|
|
repStartA = HSBCA31_REP_START; |
|
|
|
|
|
repEndA = HSBCA31_REP_END; |
|
|
|
|
|
repCountB = HSBCA31_REP_COUNT; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (configOpt.isEmpty()) { |
|
|
|
|
|
merger.merge(docA, 1, totalPagesA); |
|
|
|
|
|
mergedPdf.close(); |
|
|
|
|
|
return baos.toByteArray(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if ((repPage != -1 && totalPagesA >= repPage) || isMultiPageReplace) { |
|
|
|
|
|
|
|
|
|
|
|
// Determine start/end pages for copy segments |
|
|
|
|
|
int firstSegmentEnd = isMultiPageReplace ? repStartA - 1 : repPage - 1; |
|
|
|
|
|
int secondSegmentStart = isMultiPageReplace ? repEndA + 1 : repPage + 1; |
|
|
|
|
|
int pagesToInsertB = isMultiPageReplace ? repCountB : 1; |
|
|
|
|
|
|
|
|
FormSigPage cfg = configOpt.get(); |
|
|
|
|
|
int pageFrom = cfg.getPageFrom(); |
|
|
|
|
|
int pageTo = cfg.getPageTo(); |
|
|
|
|
|
boolean skipAndAppend = SKIP_AND_APPEND.equals(cfg.getAction()); |
|
|
|
|
|
|
|
|
// A. Copy pages 1 up to the start of replacement |
|
|
|
|
|
if (firstSegmentEnd >= 1) { |
|
|
|
|
|
merger.merge(docA, 1, firstSegmentEnd); |
|
|
|
|
|
|
|
|
if (skipAndAppend && totalPagesA >= pageFrom) { |
|
|
|
|
|
if (pageFrom > 1) { |
|
|
|
|
|
merger.merge(docA, 1, pageFrom - 1); |
|
|
|
|
|
} |
|
|
|
|
|
if (totalPagesA > pageFrom) { |
|
|
|
|
|
merger.merge(docA, pageFrom + 1, totalPagesA); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// B. Insert replacement pages from PDF B (if available) |
|
|
|
|
|
if (pdfBFlattenedBytes != null) { |
|
|
if (pdfBFlattenedBytes != null) { |
|
|
try (PdfReader readerB_merge = new PdfReader(new ByteArrayInputStream(pdfBFlattenedBytes)); |
|
|
try (PdfReader readerB_merge = new PdfReader(new ByteArrayInputStream(pdfBFlattenedBytes)); |
|
|
PdfDocument docB = new PdfDocument(readerB_merge)) { |
|
|
PdfDocument docB = new PdfDocument(readerB_merge)) { |
|
|
|
|
|
|
|
|
if (docB.getNumberOfPages() >= pagesToInsertB) { |
|
|
|
|
|
merger.merge(docB, 1, pagesToInsertB); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
merger.merge(docB, 1, 1); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// C. Copy pages after replacement through the end |
|
|
|
|
|
if (totalPagesA >= secondSegmentStart) { |
|
|
|
|
|
merger.merge(docA, secondSegmentStart, totalPagesA); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// --- Existing Logic (Skip Pages) --- |
|
|
|
|
|
} else if ("IDA".equals(formCode) && totalPagesA >= IDA_SIG_PAGE) { |
|
|
|
|
|
// IDA: SKIP page 15 |
|
|
|
|
|
if (IDA_SIG_PAGE > 1) { |
|
|
|
|
|
merger.merge(docA, 1, IDA_SIG_PAGE - 1); |
|
|
|
|
|
} |
|
|
|
|
|
if (totalPagesA > IDA_SIG_PAGE) { |
|
|
|
|
|
merger.merge(docA, IDA_SIG_PAGE + 1, totalPagesA); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} else if ("FNA".equals(formCode) && totalPagesA >= FNA_SIG_PAGE) { |
|
|
|
|
|
// FNA: SKIP page 10 |
|
|
|
|
|
if (FNA_SIG_PAGE > 1) { |
|
|
|
|
|
merger.merge(docA, 1, FNA_SIG_PAGE - 1); |
|
|
|
|
|
} |
|
|
|
|
|
if (totalPagesA > FNA_SIG_PAGE) { |
|
|
|
|
|
merger.merge(docA, FNA_SIG_PAGE + 1, totalPagesA); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} else if ("HSBCFIN".equals(formCode) && totalPagesA >= HSBC_REP_PAGE) { |
|
|
|
|
|
// HSBCFIN: REPLACE page 11 with PDF B page 1 |
|
|
|
|
|
|
|
|
|
|
|
if (HSBC_REP_PAGE > 1) { |
|
|
|
|
|
merger.merge(docA, 1, HSBC_REP_PAGE - 1); |
|
|
|
|
|
|
|
|
} else if (!skipAndAppend && totalPagesA >= pageTo) { |
|
|
|
|
|
int repStartA = pageFrom; |
|
|
|
|
|
int repEndA = pageTo; |
|
|
|
|
|
int pagesToInsertB = pageTo - pageFrom + 1; |
|
|
|
|
|
if (repStartA > 1) { |
|
|
|
|
|
merger.merge(docA, 1, repStartA - 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (pdfBFlattenedBytes != null) { |
|
|
if (pdfBFlattenedBytes != null) { |
|
|
try (PdfReader readerB_merge = new PdfReader(new ByteArrayInputStream(pdfBFlattenedBytes)); |
|
|
try (PdfReader readerB_merge = new PdfReader(new ByteArrayInputStream(pdfBFlattenedBytes)); |
|
|
PdfDocument docB = new PdfDocument(readerB_merge)) { |
|
|
|
|
|
merger.merge(docB, 1, 1); |
|
|
|
|
|
|
|
|
PdfDocument docB = new PdfDocument(readerB_merge)) { |
|
|
|
|
|
if (docB.getNumberOfPages() >= pagesToInsertB) { |
|
|
|
|
|
merger.merge(docB, 1, pagesToInsertB); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (totalPagesA > HSBC_REP_PAGE) { |
|
|
|
|
|
merger.merge(docA, HSBC_REP_PAGE + 1, totalPagesA); |
|
|
|
|
|
|
|
|
if (totalPagesA > repEndA) { |
|
|
|
|
|
merger.merge(docA, repEndA + 1, totalPagesA); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} else { |
|
|
} else { |
|
|
// Default: Copy all pages from docA |
|
|
|
|
|
merger.merge(docA, 1, totalPagesA); |
|
|
merger.merge(docA, 1, totalPagesA); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 4. Process PDF B (Only appended if IDA or FNA) |
|
|
|
|
|
if (pdfBFlattenedBytes != null) { |
|
|
|
|
|
if ("IDA".equals(formCode) || "FNA".equals(formCode)){ |
|
|
|
|
|
try (PdfReader readerB_merge = new PdfReader(new ByteArrayInputStream(pdfBFlattenedBytes)); |
|
|
|
|
|
PdfDocument docB = new PdfDocument(readerB_merge)) { |
|
|
|
|
|
|
|
|
|
|
|
// Copy ONLY page 1 from docB to append as the last page |
|
|
|
|
|
merger.merge(docB, 1, 1); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
mergedPdf.close(); |
|
|
|
|
|
|
|
|
mergedPdf.close(); |
|
|
return baos.toByteArray(); |
|
|
return baos.toByteArray(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public byte[] mergePdf2sItext7(String formCode, byte[] pdfABytes, byte[] pdfBBytes) throws IOException { |
|
|
|
|
|
|
|
|
/** Backward compatibility: use today as asOfDate. */ |
|
|
|
|
|
public byte[] mergePdfsItext7(String formCode, byte[] pdfABytes, byte[] pdfBBytes) throws IOException { |
|
|
|
|
|
return mergePdfsItext7(formCode, LocalDate.now(), pdfABytes, pdfBBytes); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Merge upload2 PDF using config from form_sig_page. |
|
|
|
|
|
*/ |
|
|
|
|
|
public byte[] mergePdf2sItext7(String formCode, LocalDate asOfDate, byte[] pdfABytes, byte[] pdfBBytes) throws IOException { |
|
|
|
|
|
Optional<FormSigPage> configOpt = formSigPageService.getUpload2Config(formCode, asOfDate); |
|
|
|
|
|
|
|
|
// UPDATED CONSTANTS FOR NEW REQUIREMENTS |
|
|
|
|
|
final int MLB03S_REP_START_A = 12; |
|
|
|
|
|
final int MLB03S_REP_END_A = 13; |
|
|
|
|
|
final int MLB03S_REP_COUNT_B = 2; |
|
|
|
|
|
final int SLAPP_REP_START_A = 19; |
|
|
|
|
|
final int SLAPP_REP_END_A = 20; |
|
|
|
|
|
final int SLAPP_REP_COUNT_B = 2; |
|
|
|
|
|
final int SLGII_REP_START_A = 15; |
|
|
|
|
|
final int SLGII_REP_END_A = 16; |
|
|
|
|
|
final int SLGII_REP_COUNT_B = 2; |
|
|
|
|
|
|
|
|
|
|
|
// --- STEP 1: Flatten PDF A and get the modified bytes --- |
|
|
|
|
|
byte[] pdfAFlattenedBytes; |
|
|
byte[] pdfAFlattenedBytes; |
|
|
try (PdfReader readerA = new PdfReader(new ByteArrayInputStream(pdfABytes)); |
|
|
try (PdfReader readerA = new PdfReader(new ByteArrayInputStream(pdfABytes)); |
|
|
ByteArrayOutputStream tempBaosA = new ByteArrayOutputStream(); |
|
|
ByteArrayOutputStream tempBaosA = new ByteArrayOutputStream(); |
|
|
PdfWriter tempWriterA = new PdfWriter(tempBaosA); |
|
|
PdfWriter tempWriterA = new PdfWriter(tempBaosA); |
|
|
PdfDocument docA_mod = new PdfDocument(readerA, tempWriterA)) { |
|
|
PdfDocument docA_mod = new PdfDocument(readerA, tempWriterA)) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flattenPdf(docA_mod); |
|
|
flattenPdf(docA_mod); |
|
|
docA_mod.close(); // IMPORTANT: Close to finalize writing to tempBaosA |
|
|
|
|
|
|
|
|
docA_mod.close(); |
|
|
pdfAFlattenedBytes = tempBaosA.toByteArray(); |
|
|
pdfAFlattenedBytes = tempBaosA.toByteArray(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// --- STEP 2: Flatten PDF B and get the modified bytes (if needed) --- |
|
|
|
|
|
byte[] pdfBFlattenedBytes = null; |
|
|
byte[] pdfBFlattenedBytes = null; |
|
|
if (pdfBBytes != null && pdfBBytes.length > 0) { |
|
|
if (pdfBBytes != null && pdfBBytes.length > 0) { |
|
|
try (PdfReader readerB = new PdfReader(new ByteArrayInputStream(pdfBBytes)); |
|
|
try (PdfReader readerB = new PdfReader(new ByteArrayInputStream(pdfBBytes)); |
|
|
ByteArrayOutputStream tempBaosB = new ByteArrayOutputStream(); |
|
|
ByteArrayOutputStream tempBaosB = new ByteArrayOutputStream(); |
|
|
PdfWriter tempWriterB = new PdfWriter(tempBaosB); |
|
|
PdfWriter tempWriterB = new PdfWriter(tempBaosB); |
|
|
PdfDocument docB_mod = new PdfDocument(readerB, tempWriterB)) { |
|
|
PdfDocument docB_mod = new PdfDocument(readerB, tempWriterB)) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flattenPdf(docB_mod); |
|
|
flattenPdf(docB_mod); |
|
|
docB_mod.close(); // IMPORTANT: Close to finalize writing to tempBaosB |
|
|
|
|
|
|
|
|
docB_mod.close(); |
|
|
pdfBFlattenedBytes = tempBaosB.toByteArray(); |
|
|
pdfBFlattenedBytes = tempBaosB.toByteArray(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// --- STEP 3: Perform the merge using the flattened bytes --- |
|
|
|
|
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
|
|
PdfWriter writer = new PdfWriter(baos); |
|
|
PdfWriter writer = new PdfWriter(baos); |
|
|
PdfDocument mergedPdf = new PdfDocument(writer); |
|
|
PdfDocument mergedPdf = new PdfDocument(writer); |
|
|
PdfReader readerA_merge = new PdfReader(new ByteArrayInputStream(pdfAFlattenedBytes)); |
|
|
PdfReader readerA_merge = new PdfReader(new ByteArrayInputStream(pdfAFlattenedBytes)); |
|
|
PdfDocument docA = new PdfDocument(readerA_merge)) { |
|
|
|
|
|
|
|
|
PdfDocument docA = new PdfDocument(readerA_merge)) { |
|
|
|
|
|
|
|
|
// ⭐️ FIX: PdfMerger is NOT AutoCloseable, so we instantiate it here. |
|
|
|
|
|
PdfMerger merger = new PdfMerger(mergedPdf); |
|
|
PdfMerger merger = new PdfMerger(mergedPdf); |
|
|
|
|
|
|
|
|
int totalPagesA = docA.getNumberOfPages(); |
|
|
int totalPagesA = docA.getNumberOfPages(); |
|
|
|
|
|
|
|
|
// --- Multi-page Replacement Forms (SLAPP, SLGII) --- |
|
|
|
|
|
int repStartA = -1; |
|
|
|
|
|
int repEndA = -1; |
|
|
|
|
|
int repCountB = -1; |
|
|
|
|
|
|
|
|
|
|
|
if ("SLAPP".equals(formCode)) { |
|
|
|
|
|
repStartA = SLAPP_REP_START_A; |
|
|
|
|
|
repEndA = SLAPP_REP_END_A; |
|
|
|
|
|
repCountB = SLAPP_REP_COUNT_B; |
|
|
|
|
|
} else if ("SLGII".equals(formCode)) { |
|
|
|
|
|
repStartA = SLGII_REP_START_A; |
|
|
|
|
|
repEndA = SLGII_REP_END_A; |
|
|
|
|
|
repCountB = SLGII_REP_COUNT_B; |
|
|
|
|
|
} else if ("MLB03S".equals(formCode)) { |
|
|
|
|
|
repStartA = MLB03S_REP_START_A; |
|
|
|
|
|
repEndA = MLB03S_REP_END_A; |
|
|
|
|
|
repCountB = MLB03S_REP_COUNT_B; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (configOpt.isEmpty()) { |
|
|
|
|
|
merger.merge(docA, 1, totalPagesA); |
|
|
|
|
|
mergedPdf.close(); |
|
|
|
|
|
return baos.toByteArray(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (repStartA != -1 && totalPagesA >= repEndA) { |
|
|
|
|
|
|
|
|
|
|
|
// A. Copy pages 1 up to (repStartA - 1) |
|
|
|
|
|
|
|
|
FormSigPage cfg = configOpt.get(); |
|
|
|
|
|
int repStartA = cfg.getPageFrom(); |
|
|
|
|
|
int repEndA = cfg.getPageTo(); |
|
|
|
|
|
int repCountB = repEndA - repStartA + 1; |
|
|
|
|
|
|
|
|
|
|
|
if (totalPagesA >= repEndA) { |
|
|
if (repStartA > 1) { |
|
|
if (repStartA > 1) { |
|
|
merger.merge(docA, 1, repStartA - 1); |
|
|
merger.merge(docA, 1, repStartA - 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// B. Insert replacement pages from PDF B (if available) |
|
|
|
|
|
if (pdfBFlattenedBytes != null) { |
|
|
if (pdfBFlattenedBytes != null) { |
|
|
try (PdfReader readerB_merge = new PdfReader(new ByteArrayInputStream(pdfBFlattenedBytes)); |
|
|
try (PdfReader readerB_merge = new PdfReader(new ByteArrayInputStream(pdfBFlattenedBytes)); |
|
|
PdfDocument docB = new PdfDocument(readerB_merge)) { |
|
|
PdfDocument docB = new PdfDocument(readerB_merge)) { |
|
|
|
|
|
|
|
|
// Copy the required number of pages from docB starting from page 1 |
|
|
|
|
|
if (docB.getNumberOfPages() >= repCountB) { |
|
|
if (docB.getNumberOfPages() >= repCountB) { |
|
|
merger.merge(docB, 1, repCountB); |
|
|
merger.merge(docB, 1, repCountB); |
|
|
} else { |
|
|
|
|
|
System.err.println("PDF B too short for " + formCode + " replacement."); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// C. Copy pages (repEndA + 1) through the end |
|
|
|
|
|
if (totalPagesA > repEndA) { |
|
|
if (totalPagesA > repEndA) { |
|
|
merger.merge(docA, repEndA + 1, totalPagesA); |
|
|
merger.merge(docA, repEndA + 1, totalPagesA); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// --- Single Page Replacement Forms (MLB03S) --- |
|
|
|
|
|
/* |
|
|
|
|
|
} else if ("MLB03S".equals(formCode) && totalPagesA >= MLB03S_REP_PAGE_A) { |
|
|
|
|
|
|
|
|
|
|
|
// A. Copy pages 1 up to 11 (MLB03S_REP_PAGE_A - 1) |
|
|
|
|
|
if (MLB03S_REP_PAGE_A > 1) { |
|
|
|
|
|
merger.merge(docA, 1, MLB03S_REP_PAGE_A - 1); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// B. Insert replacement page from PDF B (if available) |
|
|
|
|
|
if (pdfBFlattenedBytes != null) { |
|
|
|
|
|
try (PdfReader readerB_merge = new PdfReader(new ByteArrayInputStream(pdfBFlattenedBytes)); |
|
|
|
|
|
PdfDocument docB = new PdfDocument(readerB_merge)) { |
|
|
|
|
|
|
|
|
|
|
|
// Copy ONLY page 1 from docB |
|
|
|
|
|
if (docB.getNumberOfPages() >= 1) { |
|
|
|
|
|
merger.merge(docB, 1, 1); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// C. Copy pages 13 through the end (MLB03S_REP_PAGE_A + 1) |
|
|
|
|
|
if (totalPagesA > MLB03S_REP_PAGE_A) { |
|
|
|
|
|
merger.merge(docA, MLB03S_REP_PAGE_A + 1, totalPagesA); |
|
|
|
|
|
} |
|
|
|
|
|
*/ |
|
|
|
|
|
} else { |
|
|
} else { |
|
|
// Default: Copy all pages from docA |
|
|
|
|
|
merger.merge(docA, 1, totalPagesA); |
|
|
merger.merge(docA, 1, totalPagesA); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
mergedPdf.close(); |
|
|
|
|
|
return baos.toByteArray(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mergedPdf.close(); |
|
|
|
|
|
return baos.toByteArray(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Backward compatibility: use today as asOfDate. */ |
|
|
|
|
|
public byte[] mergePdf2sItext7(String formCode, byte[] pdfABytes, byte[] pdfBBytes) throws IOException { |
|
|
|
|
|
return mergePdf2sItext7(formCode, LocalDate.now(), pdfABytes, pdfBBytes); |
|
|
|
|
|
} |
|
|
|
|
|
} |