Cannot close PDFStamper after Setfield - vb.net

The VB.Net code below works under v 5.3.4 but the .Close statement fails under v5.4.1 and v5.4.1. I can use the same code and switch .dlls to reproduce the problem.
Me.Text in the second line is a textbox containing the path to the file. The file was created with AcroBat 9.5 and it has two form fields named 'NAME' and 'PHONE'.
The itextsharp.dll from the core.zip is added as a reference to the project. At the form level: IMPORTS itextsharp.text and .text.pdf. Thanks!
Could someone verify this and report the bug? Or let me know what I'm doing wrong (besides using WinForms and VB.Net:)?
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
Dim newFile As String = Me.Text.Replace(".pdf", "_Out.pdf")
''//create w/overwrite copy of the template
Dim pdfReader As New PdfReader(pdfTemplate)
Dim pdfStamper As New PdfStamper(pdfReader, New FileStream(newFile, FileMode.Create))
pdfFormFields = pdfStamper.AcroFields
''//set form pdfFormFields ' field names are case sensitive
pdfFormFields.SetField("NAME", "Firstname Lastname")
pdfFormFields.SetField("PHONE", "805.555.1212")
''//report by reading values from completed PDF
Dim sTmp As String = "Completed: " + pdfFormFields.GetField("NAME") + " " + _
pdfFormFields.GetField("PHONE")
MessageBox.Show(sTmp, "Finished")
''//flatten the form to remove editting options, set it to false
''//to leave the form open to subsequent manual edits
pdfStamper.FormFlattening = False
''//close the pdf
pdfStamper.Close()
End Sub

Related

What is the proper way to save an existing file to a user's computer using a SaveFileDialog?

I have a vb.net windows app that creates a PDF. After creation, I want to prompt the user where they want to save the file. The default save folder is different than the folder of the created PDF. I get the SaveDialog box to come up with the default folder and file name that I want. If I choose "Save", I get a message saying that the file does not exist and none of the code below the ShowDialog is executed (I'm sure that I'm doing that part wrong as well).
Dim saveFileDialog1 As New SaveFileDialog
saveFileDialog1.InitialDirectory = MyDocsFolder
saveFileDialog1.FileName = "Report.pdf"
saveFileDialog1.Title = "Save Report"
saveFileDialog1.CheckFileExists = True
saveFileDialog1.CheckPathExists = True
saveFileDialog1.DefaultExt = "pdf"
saveFileDialog1.Filter = "All files (*.*)|*.*|All files (*.*)|*.*"
saveFileDialog1.FilterIndex = 2
saveFileDialog1.RestoreDirectory = True
saveFileDialog1.ShowDialog()
If saveFileDialog1.ShowDialog = DialogResult.OK Then
If saveFileDialog1.FileName() <> "" Then
Dim newStream As FileStream = File.Open(newFile, FileMode.Open)
Dim pdfStream As New FileStream(saveFileDialog1.FileName, FileMode.Create)
newStream.CopyTo(fs, FileMode.Append)
newStream.Close()
fs.Close()
End If
End If
you can do it like this:
Imports Microsoft.WindowsAPICodePack.Dialogs
Public NotInheritable Class Form1
Private Sub ButtonSave_Click(sender As Object, e As EventArgs) Handles ButtonSave.Click
Dim Path As String
Using SFD1 As New CommonSaveFileDialog
SFD1.Title = "Where should the file be saved?"
SFD1.Filters.Add(New CommonFileDialogFilter("PDF", ".pdf"))
SFD1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
If SFD1.ShowDialog() = CommonFileDialogResult.Ok Then
Path = SFD1.FileName
Else
Return
End If
End Using
Dim dest As IO.FileInfo
Using fs As IO.FileStream = IO.File.Create(Path & ".pdf")
dest = My.Computer.FileSystem.GetFileInfo(fs.Name)
End Using
End Sub
End Class
Please note that I am using a FileDialog that I downloaded from Visual Studios' own Nuget Package Manager. See image. You don't have to do that, but I prefer this FileDialog because it offers more options than the one that is already included.
So the user enters the file name; thus the path results. In my code example, this creates a blank PDF. It cannot be opened like this yet.
So that something is written in the PDF, you can download itext7 (also via NuGet).
Then, you write Imports iText.Kernel.Pdf,
Imports iText.Kernel.Utils and in your sub Dim pdfwriter As New PdfWriter(dest) with ‘dest’ from above.
I am so blind and stupid...
saveFileDialog1.CheckFileExists = True
Sorry for wasting anyone's time.

PDF fill in not merging correctly

We are using an asp.net website with iTextSharp.dll version 5.5.13
We can merge multiple PDF files into one using a stream and it works perfectly. However, when we use a PDF that was created in a "fill-in" function the new PDF file does not correctly merge into the other documents. It merges without the filled in values. However, if I open the filled in PDF that it creates the filled in data displays and prints fine.
I have tried merging the new "filled in" PDF at a later time but it still displays the template file as though the filled in data was missing.
Below code fills in the data
Dim strFileName As String = Path.GetFileNameWithoutExtension(strSourceFile)
Dim strOutPath As String = HttpContext.Current.Server.MapPath("~/Apps/Lifetime/OfficeDocs/Export/")
newFile = strOutPath & strFileName & " " & strRONumber & ".pdf"
If Not File.Exists(newFile) Then
Dim pdfReader As PdfReader = New PdfReader(strSourceFile)
Using pdfStamper As PdfStamper = New PdfStamper(pdfReader, New FileStream(newFile, FileMode.Create))
Dim pdfFormFields As AcroFields = pdfStamper.AcroFields
pdfFormFields.SetField("CUSTOMER NAME", strCustomer)
pdfFormFields.SetField("YR MK MODEL", strVehicle)
pdfFormFields.SetField("RO#", strRONumber)
pdfStamper.FormFlattening = False
pdfStamper.Dispose()
End Using
End If
Then code below here merges multiple PDF files/paths sent to it
Public Shared Sub MergePDFs(ByVal files As List(Of String), ByVal filename As String)
'Gets a list of full path files and merges into one memory stream
'and outputs it to a browser response.
Dim MemStream As New System.IO.MemoryStream
Dim doc As New iTextSharp.text.Document
Dim reader As iTextSharp.text.pdf.PdfReader
Dim numberOfPages As Integer
Dim currentPageNumber As Integer
Dim writer As iTextSharp.text.pdf.PdfWriter = iTextSharp.text.pdf.PdfWriter.GetInstance(doc, MemStream)
doc.Open()
Dim cb As iTextSharp.text.pdf.PdfContentByte = writer.DirectContent
Dim page As iTextSharp.text.pdf.PdfImportedPage
Dim strError As String = ""
For Each strfile As String In files
reader = New iTextSharp.text.pdf.PdfReader(strfile)
numberOfPages = reader.NumberOfPages
currentPageNumber = 0
Do While (currentPageNumber < numberOfPages)
currentPageNumber += 1
doc.SetPageSize(reader.GetPageSizeWithRotation(currentPageNumber))
doc.NewPage()
page = writer.GetImportedPage(reader, currentPageNumber)
cb.AddTemplate(page, 0, 0)
Loop
Next
doc.Close()
doc.Dispose()
If MemStream Is Nothing Then
HttpContext.Current.Response.Write("No Data is available for output")
Else
HttpContext.Current.Response.Clear()
HttpContext.Current.Response.ContentType = "application/pdf"
HttpContext.Current.Response.AppendHeader("Content-Disposition", "inline; filename=" + filename)
HttpContext.Current.Response.BinaryWrite(MemStream.ToArray)
HttpContext.Current.Response.OutputStream.Flush()
HttpContext.Current.Response.OutputStream.Close()
HttpContext.Current.Response.OutputStream.Dispose()
MemStream.Close()
MemStream.Dispose()
End If
End Sub
I expect the "filled in" PDF in the list of files to retain the filled in data but it does not. Even if I try to merge the filled in file later it still comes up missing the filled in data. If I print the filled in file it looks perfect.
PdfWriter.GetImportedPage only returns you a copy of the page contents. This does not include any annotations, in particular not the widget annotations of form fields on the page at hand.
To also copy the annotations of the source pages, use the iText PdfCopy class instead. This class is designed to copy pages including all annotations. Furthermore, it includes methods to copy all pages of a source document in one step.
You have to tell the PdfCopy object to merge fields, otherwise the document-wide form structure won't be built.
As an aside, your code creates many PdfReader objects but does not close them. That may increase your memory requirements substantially.
Thus:
Public Shared Sub MergePDFsImproved(ByVal files As List(Of String), ByVal filename As String)
Using mem As New MemoryStream()
Dim readers As New List(Of PdfReader)
Using doc As New Document
Dim copy As New PdfCopy(doc, mem)
copy.SetMergeFields()
doc.Open()
For Each strfile As String In files
Dim reader As New PdfReader(strfile)
copy.AddDocument(reader)
readers.Add(reader)
Next
End Using
For Each reader As PdfReader In readers
reader.Close()
Next
HttpContext.Current.Response.Clear()
HttpContext.Current.Response.ContentType = "application/pdf"
HttpContext.Current.Response.AppendHeader("Content-Disposition", "inline; filename=" + filename)
HttpContext.Current.Response.BinaryWrite(mem.ToArray)
HttpContext.Current.Response.OutputStream.Flush()
HttpContext.Current.Response.OutputStream.Close()
HttpContext.Current.Response.OutputStream.Dispose()
End Using
End Sub
Actually I'm not sure whether it is a good idea to Close and Dispose the response output stream here, that shouldn't be the responsibility of a PDF merging method.
This is a related answer for the Java version of iText; you may want to read it for additional information. Unfortunately many links in that answer meanwhile are dead.

Problem when filling out a PDF and printing the PDF

I use my application to fill and print out forms.
When the form is printed out all the text except the name of the customer is stretch black...
I have tried to print the saved PDF and the same result is happening.
When i open the filled out pdf and change the text, the file is printed out correctly..
The code i'm using to fill out the PDF and printing it is under... Is there something that i can change in my code so this dont happens?
Private Sub btnPrint_Click(sender As Object, e As EventArgs) Handles btnPrint.Click
Me.xmltopdf()
Me.Print()
End Sub
Private Sub xmltopdf()
Dim pdfTemp As String = My.Settings.SavePDFT ' ---> It's the original pdf form you want to fill
Dim newFile As String = My.Settings.SavePDFS & Me.TextBox1.Text & ".PDF" ' ---> It will generate new pdf that you have filled from your program
' ------ READING -------
Dim pdfReader As New PdfReader(pdfTemp)
' ------ WRITING -------
' If you don’t specify version and append flag (last 2 params) in below line then you may receive “Extended Features” error when you open generated PDF
Dim pdfStamper As New PdfStamper(pdfReader, New FileStream(newFile, FileMode.Create), "\6c", True)
Dim pdfFormFields As AcroFields = pdfStamper.AcroFields
' ------ SET YOUR FORM FIELDS ------
pdfFormFields.SetField("Field_1", TextBox1.Text)
pdfFormFields.SetField("Field_2", TextBox2.Text)
' There is more fields.. just removed them this.
pdfStamper.FormFlattening = False
' close the pdf
pdfStamper.Close()
' pdfReader.close() ---> DON"T EVER CLOSE READER IF YOU'RE GENERATING LOTS OF PDF FILES IN LOOP
End Sub
' Print PDF
Private Sub Print()
' Wait a bit so the PDF file is created before printing.
Threading.Thread.Sleep(2500)
Dim psi As New ProcessStartInfo
psi.UseShellExecute = True
psi.Verb = "print"
psi.WindowStyle = ProcessWindowStyle.Hidden
'psi.Arguments = PrintDialog1.PrinterSettings.PrinterName.ToString()
psi.FileName = My.Settings.SavePDFS & Me.Ordre_NummerTextBox.Text & ".PDF" ' Here specify a document to be printed
Process.Start(psi)
End Sub
This is the printet out PDF.
I made a new PDF File and have had no problems with it sinse.
The old PDF file was 25.8 MB large and the new one is only 140 kB.
Also i remember that the Old file is copied and change several times in the past.

Acrofields Autofilled with iTextSharp in vb.net Not Visible Until Clicked

I'm working on a vb.net app to fill preexisting pdf forms and I've run in to a frustrating problem. The code below puts the values into the given fields on the pdf form, but in order to see those values in Adobe Reader, the fields themselves have to be selected. I can't share the pdf itself, but from opening it in Acrobat, it seems like security/protection isn't the issue, though I do get a permissions error when I set FormFlattening to True.
Is there a step in the code below which I'm missing?
Imports System
Imports System.IO
Imports System.Xml
Imports iTextSharp
Imports iTextSharp.text
Imports iTextSharp.text.pdf
Imports iTextSharp.text.xml
Imports iTextSharp.pdfa
Imports System.Security
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim pdfTemp As String = "C:\ExampleTemplate.pdf"
Dim newFile As String = "C:\NewFile.Pdf"
Dim pdfReader As New PdfReader(pdfTemp)
Dim pdfStamper As New PdfStamper(pdfReader, New FileStream(newFile,_ FileMode.Create), "\6c", True)
Dim pdfFormFields As AcroFields = pdfStamper.AcroFields
pdfFormFields.SetField("Date", "03092014", "03092014")
pdfFormFields.SetField("Contract_No", "1234456", "1234456")
pdfFormFields.SetField("Buyer", "bar, foo", "bar, foo")
pdfFormFields.GenerateAppearances = True
pdfStamper.FormFlattening = True
pdfStamper.Close()
pdfReader.Close()
End Sub
End Class
So it's not 100% clear to me why this worked and my previous efforts didn't, but copying and pasting this code to initialize the PdfStamper
Dim pdfTemplate As String = "Path to fillable pdf"
Dim strFolder As String = "Path to destination Folder"
Dim newFile As String = strFolder & "Name of Completed Form"
Dim pdfReader As New PdfReader(pdfTemplate)
Dim pdfStamper As New PdfStamper(pdfReader, New FileStream(newFile, FileMode.Create))
Dim pdfFormFields As AcroFields = pdfStamper.AcroFields
`fields and values as in original question'
pdfStamper.FormFlattening = True
pdfStamper.Close()
from the tutorial here made the project work.
On a side note, it became clear to me that not all .pdf files with fillable fields are "forms", and that itextsharp requires that the file be a form. I realized this when, after applying the above code to two files successfully, the third failed, despite me knowing the names of the fields. To make it into a form, and thus recognizable to itextsharp, I opened it in acrobat and created a form. All the fields and their names were preserved so I saved it and it worked like a charm.

Open, Launch or Show a file for the user to read or write in vb.net

It sounds very simple but I have searched and cannot seem to find a way to open a log file which the user just created from my windows form app. The file exits I just want to open it after it is created.
I have a Dim path As String = TextBox1.Text and once the user names and clicks ok on the savefiledialog I have a msgbox that says "Done" and when you hit OK I have tried this
FileOpen(FreeFile, path, OpenMode.Input) but nothing happens. I just want it to open the log and show it to the user so they can edit or save it again or anything.
This is where I got the above code.
http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.filesystem.fileopen.aspx
Searching is difficult because everyone is trying to "Open" a file and process it during runtime. I am just trying to Show a file by Launching it like someone just double clicked it.
Here is the entire Export Button click Sub. It basically writes listbox items to file.
Private Sub btnExport_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExport.Click
Dim sfd As New SaveFileDialog
Dim path As String = TextBox1.Text
Dim arypath() As String = Split(TextBox1.Text, "\")
Dim pathDate As String
Dim foldername As String
foldername = arypath(arypath.Length - 1)
pathDate = Now.ToString("yyyy-MM-dd") & "_" & Now.ToString("hh;mm")
sfd.FileName = "FileScannerResults " & Chr(39) & foldername & Chr(39) & " " & pathDate
sfd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal)
sfd.Filter = "Text files (*.txt)|*.txt|CSV Files (*.csv)|*.csv"
sfd.ShowDialog()
path = sfd.FileName
Using SW As New IO.StreamWriter(path)
If CkbxFolder.Checked = True Then
SW.WriteLine("Folders")
For Each itm As String In ListBox1.Items
SW.WriteLine(itm)
Next
End If
If CkbxFiles.Checked = True Then
SW.WriteLine("Files")
For Each itm As String In ListBox2.Items
SW.WriteLine(itm)
Next
End If
End Using
MsgBox("Done...")
FileOpen(FreeFile, path, OpenMode.Input) 'Why can't I open a file for you...
End Sub
Do not use the old VB6 methods. They are still here for compatibility reason, the new code should use the more powerful methods in the System.IO namespace.
However, as said in comments, FileOpen don't show anything for you, just opens the file
You coud write
Using sr = new StreamReader(path)
Dim line = sr.ReadLine()
if !string.IsNullOrEmpty(line) Then
textBoxForLog.AppendText(line)
End If
End Using
or simply (if the file is not too big)
Dim myLogText = File.ReadAllText(path)
textBoxForLog.Text = myLogText
As alternative, you could ask the operating system to run the program associated with the file extension and show the file for you
Process.Start(path)
To get the same behavior as if the user double-clicked it, just use System.Diagnostics.Process, and pass the filename to it's Start method:
Process.Start(path)
This will open the file using whatever the default application is for that filename based on its extension, just like Explorer does when you double-click it.