У меня есть файл в формате PDF, который я пытаюсь заполнить содержимым "MyDocument". Все поля заполняются нормально, но проблема в том, что поля "Вычисляемые" в моем PDF файле не обновляются, а также не форматируются в других полях. Как заставить обработанные поля обновлять и форматировать, используя ITextSharp? (Мне все равно, получаю ли я ответ на С# или VB.NET)
VB.NET:
Public Shared Sub Serialize(ByVal stmt As MyDocument, ByVal file As FileInfo)
Dim reader As New PdfReader(TemplateFilePath.FullName)
Dim pdfStamper As New PdfStamper(reader, New FileStream(file.FullName, FileMode.Open))
Try
With itsDaDetailFields
.MoveFirst()
While Not .EOF
Dim pdfFieldName As String = NsT(Of String)(!PDFFieldName, Nothing)
If Not String.IsNullOrEmpty(pdfFieldName) Then
Dim value As String = NsT(Of String)(stmt.GetValueFromPDFField(pdfFieldName), Nothing)
If Not String.IsNullOrEmpty(value) Then
pdfStamper.AcroFields.SetField(pdfFieldName, value)
End If
End If
.MoveNext()
End While
End With
Finally
pdfStamper.FormFlattening = False
reader.Close()
pdfStamper.Close()
End Try
End Sub
С#:
public static void Serialize(MyDocument stmt, FileInfo file)
{
PdfReader reader = new PdfReader(TemplateFilePath.FullName);
PdfStamper pdfStamper = new PdfStamper(reader, new FileStream(file.FullName, FileMode.Open));
try {
var _with1 = itsDaDetailFields;
_with1.MoveFirst();
while (!_with1.EOF) {
string pdfFieldName = NsT<string>(_with1["PDFFieldName"], null);
if (!string.IsNullOrEmpty(pdfFieldName)) {
string value = NsT<string>(stmt.GetValueFromPDFField(pdfFieldName), null);
if (!string.IsNullOrEmpty(value)) {
pdfStamper.AcroFields.SetField(pdfFieldName, value);
}
}
_with1.MoveNext();
}
} finally {
pdfStamper.FormFlattening = false;
reader.Close();
pdfStamper.Close();
}
}
Итак, я понял, как это сделать в .NET на основе следующего сообщения с помощью iText (java-версия ITextSharp - процедура немного отличается для .net). Не стесняйтесь прочитать следующий поток для полного объяснения и обсуждения той же проблемы в iText:
Есть два способа сделать это:
(1) Укажите отображаемое значение, например:
pdfStamper.AcroFields.SetField(pdfFieldName, value, <formatted value>)
как в:
pdfStamper.AcroFields.SetField(pdfFieldName, 1000, "1,000")
Это не было оптимальным для меня, потому что я не мог вычислить программу из моего PDF файла, текстовые поля которого форматировали их содержимое в каком формате. У некоторых были несколько разные форматы (у некоторых было 2 десятичных разряда, у некоторых было 0, у некоторых было много), поэтому, если вы можете отслеживать, как текстовое поле форматирует свои данные или все они делают то же самое, тогда это может сработать. Это также не устранило проблему с вычисленными полями, а просто затруднило проблему форматирования.
(2) Предоставьте javascript для "DIRTY" значения, чтобы оно форматировалось и вычислялось:
Мой код превратился во что-то вроде следующего, поскольку мне нужно было только форматировать числовые значения, но это можно расширить, чтобы обрабатывать другие типы (см. обсуждение ниже).
Dim reader As New PdfReader(TemplateFilePath.FullName)
Dim pdfStamper As New PdfStamper(reader, New FileStream(file.FullName, FileMode.Open))
With pdfStamper.AcroFields
If IsNumeric(value) Then
Dim js As String = String.Format("var f = this.getField('{0}'); f.value = 1 * f.value;", pdfFieldName)
pdfStamper.JavaScript = js
End If
.SetField(pdfFieldName, value)
End With
reader.Close()
pdfStamper.Close()
Итак, фокус в том, что вам нужно использовать JavaScript, чтобы получить значение грязным, тогда Reader будет применять форматирование. Вы можете обобщить это больше и обрабатывать больше типов значений на основе полного решения, представленного ниже (извините, что оно находится в java, но может быть адаптировано к .net):
import java.io.IOException;
import java.util.ArrayList;
import com.lowagie.text.pdf.PRStream;
import com.lowagie.text.pdf.PdfDictionary;
import com.lowagie.text.pdf.PdfName;
import com.lowagie.text.pdf.PdfObject;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfString;
import com.lowagie.text.pdf.AcroFields.Item;
public class AcroFieldJSScanner {
protected ArrayList<String> functions = null;
public void getFieldFunctions(Item item) throws IOException{
PdfDictionary dict;
for (int i = 0; i < item.size(); i++) {
dict = item.getMerged(i);
scanPdfDictionary(dict);
// dict = item.getWidget(i);
//
// scanPdfDictionary(dict);
}
}
protected void scanPdfDictionary(PdfDictionary dict) throws IOException{
PdfObject objJS = null;
String func = null;
objJS = dict.get(PdfName.JS);
if (dict.get(PdfName.S) != null && objJS != null && objJS.isString()){
PdfString strJS = (PdfString)objJS;
if (functions == null){
functions = new ArrayList<String>();
}
func = strJS.toString();
functions.add(func);
}else if (dict.get(PdfName.S) != null && objJS != null){
for(Object obj : dict.getKeys()){
PdfName pdfName = (PdfName)obj;
PdfObject pdfObj = dict.get(pdfName);
if (pdfObj.isIndirect()){
PdfObject pdfIndirectObject = PdfReader.getPdfObject(pdfObj);
func = new String(PdfReader.getStreamBytes((PRStream)pdfIndirectObject));
if (functions == null){
functions = new ArrayList<String>();
}
functions.add(func);
}else{
scanPdfObject(pdfObj);
}
}
}else{
for(Object obj : dict.getKeys()){
PdfName pdfName = (PdfName)obj;
PdfObject pdfObj = dict.get(pdfName);
scanPdfObject(pdfObj);
}
}
}
protected void scanPdfObject(PdfObject parentPdfObject) throws IOException{
if (parentPdfObject.isDictionary()){
scanPdfDictionary((PdfDictionary)parentPdfObject);
}else if (parentPdfObject.isIndirect()){
PdfObject pdfObject = PdfReader.getPdfObject(parentPdfObject);
scanPdfObject(pdfObject);
}
}
public ArrayList<String> getFunctions() {
return functions;
}
public String toString(){
StringBuilder sb = null;
if (getFunctions() != null){
sb = new StringBuilder();
for (int i =0; i< getFunctions().size();i++) {
sb.append(getFunctions().get(i)).append("\n");
}
}else{
return "No functions found";
}
return sb.toString();
}
}
И тогда, если вы знаете сценарии javascript, которые будет вызывать Adobe (используя приведенный выше код), вы знаете, какой тип данных является таким, чтобы вы могли "DIRTY" данных. Ниже приведены некоторые типы данных adobe и javascript, которые находятся за этими типами данных:
public String getFieldFormat(Item item){
PdfDictionary aa = (PdfDictionary) item.getMerged(0).get(PdfName.AA);
if (null != aa)
{
PdfDictionary f = (PdfDictionary)PdfReader.getPdfObject(aa.get(PdfName.F));
if (null != f)
{
PdfString js = (PdfString)PdfReader.getPdfObject(f.get(PdfName.JS));
if (null != js)
{
String sScriptName = js.toString();
if (sScriptName.contains("AFNumber_Format"))
System.out.println("Format : Number");
else if (sScriptName.contains("AFDate_Format"))
System.out.println("Format : Date");
else if (sScriptName.contains("AFTime_Format"))
System.out.println("Format : Time");
else if (sScriptName.contains("AFSpecial_Format"))
System.out.println("Format : Special");
else if (sScriptName.contains("AFPercent_Format"))
System.out.println("Format : Percent");
else
System.out.println("Format : Custom");;
System.out.println("JS: ");
System.out.println(js);
}
}
}
}
В моем случае я узнал, что:
Поэтому мне пришлось изменить javascript, чтобы на самом деле написать полное значение, а не просто загрязнять его. Например:
JS &= String.Format("var f = this.getField('{0}'); f.value = '{1}';",
FieldName.Key, NewFieldValue)
Я добавляю код javascript для каждого поля в строку JS, а затем я вызываю pdfStamper.Javascript = JS
в конце.