PDFBOX acroForm 已填充,但在 Acrobat 中打开时 reader 值消失
PDFBOX acroForm filled but when opened in Acrobat reader values disappears
我有 PDF 表格,我正在尝试用 PDFBOX 填写它。它有效,表格已填写,我用其他 reader 或浏览器打开,我可以看到值,但是当我尝试在 Adobe Reader 中打开时,值消失了,我尝试了所有可能的方法来找出原因但是值,不可见。
我有模板表单,我使用它来填充数据、重命名字段并将其合并到其他文档中,然后重做该过程直到所有表单都填满。
我不确定这是否与我的代码或 Adobe 有关 reader。
Link to PDF form I need to fill
这是我用来填充表单的代码。
abstract class AbstractPDFFormFiller<T> {
private val logger = LogManager.getLogger()
private val merge = PDFMergerUtility()
private val PAGE_SIZE = 12
fun fillForm(templatePath: String, data: List<T>, headerParam: Map<String, String>): PDDocument {
val chunks = getDataChunks(data)
val totalPages = chunks.size
if (totalPages == 1) {
val sourceDocument = getTemplate(templatePath)
val form = sourceDocument.documentCatalog.acroForm
fillHeader(form, headerParam, totalPages, totalPages, data)
fillData(form, data, totalPages)
return sourceDocument
} else {
val resultDocument = PDDocument()
chunks.forEachIndexed { currentPage, it ->
val sourceDocument = getTemplate(templatePath)
val form = sourceDocument.documentCatalog.acroForm
fillHeader(form, headerParam, currentPage, totalPages, it)
fillData(form, it, currentPage)
// mergePDFForm(resultDocument, sourceDocument)
sourceDocument.save("C:\Users\\Documents\Downloads\$currentPage.pdf")
sourceDocument.close()
}
mergeFromDisk(File("C:\Users\Documents\Downloads"),resultDocument)
return resultDocument
}
}
fun mergeFromDisk(folderPath:File, resultDoc:PDDocument){
folderPath.listFiles()?.forEach {
mergePDFForm(resultDoc, PDDocument.load(it))
}
}
private fun mergePDFForm(destination: PDDocument, source: PDDocument) {
try {
source.documentCatalog.acroForm.flatten()
merge.acroFormMergeMode = PDFMergerUtility.AcroFormMergeMode.JOIN_FORM_FIELDS_MODE
merge.appendDocument(destination, source)
merge.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly())
} catch (e: Exception) {
e.printStackTrace()
}
}
fun getTemplate(templatePath: String): PDDocument {
val s = javaClass.classLoader?.getResource(templatePath)?.openStream()
return PDDocument.load(s)
}
private fun getDataChunks(data: List<T>): List<List<T>> {
return data.chunked(PAGE_SIZE)
}
fun setValue(
form: PDAcroForm,
fullyQualifiedName: String,
value: String,
rename: Boolean = false,
reNameTo: String?
) {
val field = form.getField(fullyQualifiedName)
field.setValue(value)
if (rename)
renameField(
form = form,
fullyQualifiedName = fullyQualifiedName,
newName = "${field.fullyQualifiedName}_$reNameTo"
)
}
fun renameField(form: PDAcroForm, fullyQualifiedName: String, newName: String) {
val field = form.getField(fullyQualifiedName)
?: throw IllegalArgumentException("Field with $fullyQualifiedName not found")
if (field.actions != null && field.actions.f != null) field.actions.f = null
try {
field.partialName = newName
} catch (e: Exception) {
logger.fatal("Cannot rename to PDF form name {} to new name {}", fullyQualifiedName, newName)
}
}
abstract fun fillHeader(
form: PDAcroForm,
map: Map<String, String>,
currentPage: Int,
totalPage: Int,
data: List<T>
)
abstract fun fillData(form: PDAcroForm, data: List<T>, currentPage: Int)
}
class DiversionDataFormFiller : AbstractPDFFormFiller<DiversionData>() {
private val logger = LogManager.getLogger()
override fun fillHeader(
form: PDAcroForm,
map: Map<String, String>,
currentPage: Int,
totalPage: Int,
data: List<DiversionData>
) {
form.getField("Product Type").setValue(map["Product Type"])
form.getField("Type of Schedule").setValue(map["Type of Schedule"])
form.getField("LA Revenue Account Number").setValue(map["LA Revenue Account Number"])
form.getField("Company Name").setValue(map["Company Name"])
form.getField("Filling Period").setValue(map["Filling Period"])
form.getField("Page").setValue((currentPage + 1).toString())
form.getField("of").setValue(totalPage.toString())
form.getField("Total").setValue(data.stream().mapToDouble(DiversionData::quantity).sum().toString())
renameField(form, "Product Type", "Product Type_$currentPage")
renameField(form, "Type of Schedule", "Type of Schedule_$currentPage")
renameField(form, "LA Revenue Account Number", "LA Revenue Account Number_$currentPage")
renameField(form, "Company Name", "Company Name_$currentPage")
renameField(form, "Filling Period", "Filling Period_$currentPage")
renameField(form, "Total", "Total_$currentPage")
renameField(form, "Page", "Page_$currentPage")
renameField(form, "of", "of_$currentPage")
}
override fun fillData(form: PDAcroForm, data: List<DiversionData>, currentPage: Int) {
val fieldTree = form.fieldTree
data.forEachIndexed { i, element ->
fieldTree.forEach {
if (it.fieldType == "Tx") {
try {
if (it.fullyQualifiedName.startsWith("Date") && it.partialName == i.toString()) {
logger.info(
"renaming {} to {}, {}",
it.fullyQualifiedName,
"${it.fullyQualifiedName}_$currentPage",
it.partialName
)
/* it.setValue(Util.dateToStr(element.date, "MM/dd/yy"))
renameField(form, it.fullyQualifiedName, "${it.partialName}_$currentPage")*/
setValue(
form,
it.fullyQualifiedName,
Util.dateToStr(element.date, "MM/dd/yy"),
true,
"$currentPage"
)
} else if (it.fullyQualifiedName.startsWith("Name2") && it.partialName == i.toString()
) {
it.setValue(element.shipperTaxPayerNumber)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("Name") && it.partialName == i.toString()
) {
it.setValue(element.supplierTaxPayerNumber)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("Diversion Number") && it.partialName == i.toString()
) {
it.setValue(element.importNumber)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("FEIN2") && it.partialName == i.toString()
) {
it.setValue(element.shipperTaxPayerNumber)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("FEIN") && it.partialName == i.toString()
) {
it.setValue(element.supplierTaxPayerNumber)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("Mode") && it.partialName == i.toString()
) {
it.setValue("J")
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("Manifest") && it.partialName == i.toString()
) {
it.setValue(element.billOfLading)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("Doc. Number") && it.partialName == i.toString()
) {
it.setValue(element.billOfLading)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("Net gallons") && it.partialName == i.toString()
) {
it.setValue(element.quantity.toString())
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("New") && it.partialName == i.toString()
) {
it.setValue(element.revisedDestination)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
}
} catch (ex: IOException) {
ex.printStackTrace()
}
}
}
}
}
}
数据class定义
data class DiversionData(
val terminalIRSCode: String,
val fuelType: String,
val supplierTaxPayerNumber: String,
val shipperTaxPayerNumber: String,
val quantity: Double,
val originalDestination: String,
val revisedDestination: String,
val importNumber: String?,
val date: LocalDate,
val billOfLading: String,
)
测试填写表格
@Test
public void fillFormUsingNewKotlinClass() throws IOException {
List<DiversionData> diversionData = new ArrayList<>();
for (int i = 0; i < 20; i++) {
DiversionData d = new DiversionData(
"terminal_Code" + i,
"Regular" + i,
"Supplier tax" + i,
"shipper tax" + i,
1000 + i,
"TX",
"LA",
"0000" + i,
LocalDate.now().plusDays(i),
"123456" + i
);
diversionData.add(d);
}
//E:/repo/gasjobber-docker/gasjobber-api/src/main/resources/
String path = "templates/taxFormsPDF/LA/5402(7_06)F.pdf";
DiversionDataFormFiller filler = new DiversionDataFormFiller();
Map<String, String> param = new HashMap<>();
param.put("Product Type","065");
param.put("Type of Schedule","22");
param.put("LA Revenue Account Number","3264660001");
param.put("Company Name","Test CO.");
param.put("Filling Period","2020-12");
PDDocument document = filler.fillForm(path,diversionData,param);
document.save(new File("C:\Users\Documents\Downloads\testpdf.pdf"));
document.close();
}
页面 /AA/O
条目(“打开页面时应执行的操作”)具有以下内容:
if (!bReset)
{
this.resetForm();
bReset = true;
}
所以表格被重置了。
即使手动填写并保存、关闭和重新打开表单,也会发生这种情况。也许这是故意这样做的“formupack”的演示版本。
您可以像这样删除页面 /AA 条目来防止这种情况发生
document.getPage(0).setActions(null);
或者只删除 /O 条目
document.getPage(0).getActions().setO(null);
我有 PDF 表格,我正在尝试用 PDFBOX 填写它。它有效,表格已填写,我用其他 reader 或浏览器打开,我可以看到值,但是当我尝试在 Adobe Reader 中打开时,值消失了,我尝试了所有可能的方法来找出原因但是值,不可见。
我有模板表单,我使用它来填充数据、重命名字段并将其合并到其他文档中,然后重做该过程直到所有表单都填满。
我不确定这是否与我的代码或 Adobe 有关 reader。
Link to PDF form I need to fill
这是我用来填充表单的代码。
abstract class AbstractPDFFormFiller<T> {
private val logger = LogManager.getLogger()
private val merge = PDFMergerUtility()
private val PAGE_SIZE = 12
fun fillForm(templatePath: String, data: List<T>, headerParam: Map<String, String>): PDDocument {
val chunks = getDataChunks(data)
val totalPages = chunks.size
if (totalPages == 1) {
val sourceDocument = getTemplate(templatePath)
val form = sourceDocument.documentCatalog.acroForm
fillHeader(form, headerParam, totalPages, totalPages, data)
fillData(form, data, totalPages)
return sourceDocument
} else {
val resultDocument = PDDocument()
chunks.forEachIndexed { currentPage, it ->
val sourceDocument = getTemplate(templatePath)
val form = sourceDocument.documentCatalog.acroForm
fillHeader(form, headerParam, currentPage, totalPages, it)
fillData(form, it, currentPage)
// mergePDFForm(resultDocument, sourceDocument)
sourceDocument.save("C:\Users\\Documents\Downloads\$currentPage.pdf")
sourceDocument.close()
}
mergeFromDisk(File("C:\Users\Documents\Downloads"),resultDocument)
return resultDocument
}
}
fun mergeFromDisk(folderPath:File, resultDoc:PDDocument){
folderPath.listFiles()?.forEach {
mergePDFForm(resultDoc, PDDocument.load(it))
}
}
private fun mergePDFForm(destination: PDDocument, source: PDDocument) {
try {
source.documentCatalog.acroForm.flatten()
merge.acroFormMergeMode = PDFMergerUtility.AcroFormMergeMode.JOIN_FORM_FIELDS_MODE
merge.appendDocument(destination, source)
merge.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly())
} catch (e: Exception) {
e.printStackTrace()
}
}
fun getTemplate(templatePath: String): PDDocument {
val s = javaClass.classLoader?.getResource(templatePath)?.openStream()
return PDDocument.load(s)
}
private fun getDataChunks(data: List<T>): List<List<T>> {
return data.chunked(PAGE_SIZE)
}
fun setValue(
form: PDAcroForm,
fullyQualifiedName: String,
value: String,
rename: Boolean = false,
reNameTo: String?
) {
val field = form.getField(fullyQualifiedName)
field.setValue(value)
if (rename)
renameField(
form = form,
fullyQualifiedName = fullyQualifiedName,
newName = "${field.fullyQualifiedName}_$reNameTo"
)
}
fun renameField(form: PDAcroForm, fullyQualifiedName: String, newName: String) {
val field = form.getField(fullyQualifiedName)
?: throw IllegalArgumentException("Field with $fullyQualifiedName not found")
if (field.actions != null && field.actions.f != null) field.actions.f = null
try {
field.partialName = newName
} catch (e: Exception) {
logger.fatal("Cannot rename to PDF form name {} to new name {}", fullyQualifiedName, newName)
}
}
abstract fun fillHeader(
form: PDAcroForm,
map: Map<String, String>,
currentPage: Int,
totalPage: Int,
data: List<T>
)
abstract fun fillData(form: PDAcroForm, data: List<T>, currentPage: Int)
}
class DiversionDataFormFiller : AbstractPDFFormFiller<DiversionData>() {
private val logger = LogManager.getLogger()
override fun fillHeader(
form: PDAcroForm,
map: Map<String, String>,
currentPage: Int,
totalPage: Int,
data: List<DiversionData>
) {
form.getField("Product Type").setValue(map["Product Type"])
form.getField("Type of Schedule").setValue(map["Type of Schedule"])
form.getField("LA Revenue Account Number").setValue(map["LA Revenue Account Number"])
form.getField("Company Name").setValue(map["Company Name"])
form.getField("Filling Period").setValue(map["Filling Period"])
form.getField("Page").setValue((currentPage + 1).toString())
form.getField("of").setValue(totalPage.toString())
form.getField("Total").setValue(data.stream().mapToDouble(DiversionData::quantity).sum().toString())
renameField(form, "Product Type", "Product Type_$currentPage")
renameField(form, "Type of Schedule", "Type of Schedule_$currentPage")
renameField(form, "LA Revenue Account Number", "LA Revenue Account Number_$currentPage")
renameField(form, "Company Name", "Company Name_$currentPage")
renameField(form, "Filling Period", "Filling Period_$currentPage")
renameField(form, "Total", "Total_$currentPage")
renameField(form, "Page", "Page_$currentPage")
renameField(form, "of", "of_$currentPage")
}
override fun fillData(form: PDAcroForm, data: List<DiversionData>, currentPage: Int) {
val fieldTree = form.fieldTree
data.forEachIndexed { i, element ->
fieldTree.forEach {
if (it.fieldType == "Tx") {
try {
if (it.fullyQualifiedName.startsWith("Date") && it.partialName == i.toString()) {
logger.info(
"renaming {} to {}, {}",
it.fullyQualifiedName,
"${it.fullyQualifiedName}_$currentPage",
it.partialName
)
/* it.setValue(Util.dateToStr(element.date, "MM/dd/yy"))
renameField(form, it.fullyQualifiedName, "${it.partialName}_$currentPage")*/
setValue(
form,
it.fullyQualifiedName,
Util.dateToStr(element.date, "MM/dd/yy"),
true,
"$currentPage"
)
} else if (it.fullyQualifiedName.startsWith("Name2") && it.partialName == i.toString()
) {
it.setValue(element.shipperTaxPayerNumber)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("Name") && it.partialName == i.toString()
) {
it.setValue(element.supplierTaxPayerNumber)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("Diversion Number") && it.partialName == i.toString()
) {
it.setValue(element.importNumber)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("FEIN2") && it.partialName == i.toString()
) {
it.setValue(element.shipperTaxPayerNumber)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("FEIN") && it.partialName == i.toString()
) {
it.setValue(element.supplierTaxPayerNumber)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("Mode") && it.partialName == i.toString()
) {
it.setValue("J")
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("Manifest") && it.partialName == i.toString()
) {
it.setValue(element.billOfLading)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("Doc. Number") && it.partialName == i.toString()
) {
it.setValue(element.billOfLading)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("Net gallons") && it.partialName == i.toString()
) {
it.setValue(element.quantity.toString())
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
} else if (it.fullyQualifiedName.startsWith("New") && it.partialName == i.toString()
) {
it.setValue(element.revisedDestination)
renameField(form, it.fullyQualifiedName, "${it.fullyQualifiedName}_$currentPage")
}
} catch (ex: IOException) {
ex.printStackTrace()
}
}
}
}
}
}
数据class定义
data class DiversionData(
val terminalIRSCode: String,
val fuelType: String,
val supplierTaxPayerNumber: String,
val shipperTaxPayerNumber: String,
val quantity: Double,
val originalDestination: String,
val revisedDestination: String,
val importNumber: String?,
val date: LocalDate,
val billOfLading: String,
)
测试填写表格
@Test
public void fillFormUsingNewKotlinClass() throws IOException {
List<DiversionData> diversionData = new ArrayList<>();
for (int i = 0; i < 20; i++) {
DiversionData d = new DiversionData(
"terminal_Code" + i,
"Regular" + i,
"Supplier tax" + i,
"shipper tax" + i,
1000 + i,
"TX",
"LA",
"0000" + i,
LocalDate.now().plusDays(i),
"123456" + i
);
diversionData.add(d);
}
//E:/repo/gasjobber-docker/gasjobber-api/src/main/resources/
String path = "templates/taxFormsPDF/LA/5402(7_06)F.pdf";
DiversionDataFormFiller filler = new DiversionDataFormFiller();
Map<String, String> param = new HashMap<>();
param.put("Product Type","065");
param.put("Type of Schedule","22");
param.put("LA Revenue Account Number","3264660001");
param.put("Company Name","Test CO.");
param.put("Filling Period","2020-12");
PDDocument document = filler.fillForm(path,diversionData,param);
document.save(new File("C:\Users\Documents\Downloads\testpdf.pdf"));
document.close();
}
页面 /AA/O
条目(“打开页面时应执行的操作”)具有以下内容:
if (!bReset)
{
this.resetForm();
bReset = true;
}
所以表格被重置了。
即使手动填写并保存、关闭和重新打开表单,也会发生这种情况。也许这是故意这样做的“formupack”的演示版本。
您可以像这样删除页面 /AA 条目来防止这种情况发生
document.getPage(0).setActions(null);
或者只删除 /O 条目
document.getPage(0).getActions().setO(null);