I've been stuck on this far longer than I care to admit. I'm trying to use iTextSharp to build a table that I can then insert into a PDF template (which is really just a PDF with a header and an acrofield that needs to be populated). I've currently got code that follows the same outline below:
Dim doc As New Document
Dim reader As PdfReader
Dim stamper As PdfStamper
Dim filestream As FileStream
Dim tempfile As String = My.Computer.FileSystem.GetTempFileName
filestream = New FileStream(tempfile, FileMode.Create)
PdfWriter.GetInstance(doc, filestream)
doc.Open()
generateTableForPdf(listOfData, numColumns, doc)
doc.Close()
reader = New PdfReader(pdfTemplatePath)
stamper = New PdfStamper(reader, filestream)
stamper.AcroFields.SetField("topmostSubform[0].Page1[0].headerField", "some text to go in the field")
The pdfTemplatePath is a private const that has the template's file path that I am trying to use and generateTableForPdf simply creates a dynamically sized table and puts it in doc (doc is passed by ref). I know the table is created because it displays when the page loads, however, the header in the template doesn't appear. I've looked around and found a couple of examples that use byte streams or memory streams, but my attempts at implementing those methods have failed more dramatically. Any help or guidance in better understanding how to go about this is greatly appreciated.
Related
I'm using itextsharp in vb.net to stamp some backgrounds (ie other single page pdfs) onto another pdf.
So I have a 5 page 'blank' pdf, on which I stamp page 1 with the first page from a file called page1.pdf, then I put the first page of page2.pdf as a background to page 2 etc etc.
It's worked fine so far, but I've come across a problem with stamping a particular pdf onto my 'blank' - the issue appears to be with a file I'll call 'page4.pdf' and it seems likely it's because page4.pdf has been designed as a fillable form.
When I stamp page4 on and open up the blank file in Adobe reader, I get the message:
There was an error processing a page. There was a problem reading this document (18)
Could anyone suggest a way I can stamp a pdf with a form pdf as the source without this issue?
Thanks!
Here's an extract from the code I'm using for stamping (it does other stuff and involves a loop to go through the pages of the pdf, but I've just put the actual stamp bit below to keep things simple):
Dim background As PdfContentByte
Dim page As PdfImportedPage = Nothing
Dim reader As PdfReader = New PdfReader(sourcepdf)
Dim stamper As New itextsharp.text.pdf.PdfStamper(reader, New System.IO.FileStream(outputpdf, System.IO.FileMode.Create))
Dim s_reader As New PdfReader(backfile)
page = stamper.GetImportedPage(s_reader, 1)
background.AddTemplate(page, 0, 0)
stamper.Close()
reader.Close()
s_reader.Close()
Here's the code I've been trying out to convert the form pdf to a 'non-form', which I was hoping would eliminate the stamping problem (no success so far):
Dim pdfReader As PdfReader = New PdfReader(inputpdf)
Dim pdfStamper As itextsharp.text.pdf.PdfStamper = New PdfStamper(pdfReader, New FileStream(outputpdf, FileMode.Create))
pdfStamper.AnnotationFlattening = True
pdfStamper.FreeTextFlattening = True
pdfStamper.FormFlattening = True
pdfStamper.Close()
Issue was nothing to do with the pdf being a form, but a problem that seems to crop up when I repeatedly stamp the same output file. Solution is just to explicity delete the former output file before re-running process.
I wish to append text to a file by PDFWriter without replacing each text.
If using streamwriter I can only add True behind but what about PDFWriter?
StreamWriter Code:
System.IO.StreamWriter( FILE_NAME, True)
My Current PDFWriter Code:
Dim pdfWrite As PdfWriter = PdfWriter.GetInstance(pdfDoc, New FileStream("C:\Users\New folder\" + Reader.GetString(10) + Reader.GetString(9) + ".pdf", FileMode.Append))
pdfDoc.Open()
pdfDoc.Add(New Paragraph(String.Format("{0}|{1}", Reader.GetString(9), Reader.GetString(17))))
pdfDoc.Close()
PdfWriter is a class for generating PDFs from scratch (yes, one can import contents from other documents but fundamentally it is for new content).
If you want to add new content (using iText high level API layout mechanisms) to an existing document, you should create the new content in a separate document (which you may hold in memory only by means of a MemoryStream), create PdfReader instances for the existing document and your new, intermediary one, and then combine them using PdfCopy.
If you want to add new content (using iText less high level APIs) to an existing document, you can use a PdfStamper.
As an aside, concerning:
PdfWriter.GetInstance(pdfDoc, New FileStream("C:\Users\New folder\" + Reader.GetString(10) + Reader.GetString(9) + ".pdf", FileMode.Append))
Using FileMode.Append is a bad idea, PDF is not a format in which you can simply concatenate files to get a merged result.
I'm creating a file on the fly on a WebAPI call, and sending that file back to the client.
I think I'm misunderstanding flush/close on a FileStream:
Dim path As String = tempFolder & "\" & fileName
Dim result As New HttpResponseMessage(HttpStatusCode.OK)
Dim stream As New FileStream(path, FileMode.Open)
With result
.Content = New StreamContent(stream)
.Content.Headers.ContentDisposition = New Headers.ContentDispositionHeaderValue("attachment")
.Content.Headers.ContentDisposition.FileName = fileName
.Content.Headers.ContentType = New Headers.MediaTypeHeaderValue("application/octet-stream")
.Content.Headers.ContentLength = stream.Length
End With
'stream.Flush()
'stream.Close()
'Directory.Delete(tempFolder, True)
Return result
You can see where I've commented things out above.
Questions:
Does the stream flush/close itself?
How can I delete the tempFolder after returning the result?
On top of all this, it would be great to know how to generate the file and send it to the user without writing it to the file system first. I'm confident this is possible, but I'm not sure how. I'd love to be able to understand how to do this, and solve my current problem.
Update:
I went ahead with accepted answer, and found it to be quite simple:
Dim ReturnStream As MemoryStream = New MemoryStream()
Dim WriteStream As StreamWriter = New StreamWriter(ReturnStream)
With WriteStream
.WriteLine("...")
End With
WriteStream.Flush()
WriteStream.Close()
Dim byteArray As Byte() = ReturnStream.ToArray()
ReturnStream.Flush()
ReturnStream.Close()
Then I was able to stream the content as bytearraycontent:
With result
.Content = New ByteArrayContent(byteArray)
...
End With
On top of all this, it would be great to know how to generate the file and send it to the user without writing it to the file system first. I'm confident this is possible, but I'm not sure how. I'd love to be able to understand how to do this, and solve my current problem.
To do the same thing without writing a file to disk, you might look into the MemoryStream class. As you'd guess, it streams data from memory like the FileStream does from a file. The two main steps would be:
Take your object in memory and instead of writing it to a file, you'd serialize it into a MemoryStream using a BinaryFormatter or other method (see that topic on another StackOverflow Q here: How to convert an object to a byte array in C#).
Pass the MemoryStream to the StreamContent method, exactly the same way you're passing the FileStream now.
Below is the code I am using to modify pdf metadata. The code works fine but I have one issue - instead of modifying the source file, it creates a new file in a different directory. My question is - is there a way to modify source document instead of creating a new document with necessary changes?
If File.Exists(strSource) Then
Dim pdfFileReader As New PdfReader(strSource)
psStamp = New PdfStamper(pdfFileReader, New FileStream(strDest, FileMode.Create))
Dim inf As New SortedDictionary(Of String, String)(StringComparer.Ordinal)
inf.Add("Title", "Modifying metadata")
inf.Add("Company", "My Company")
inf.Add("Author", "myself")
psStamp.MoreInfo = inf
psStamp.Close()
End If
I have tried modifying the line below:
psStamp = New PdfStamper(pdfFileReader, New FileStream(strDest, FileMode.Create))
to point to the source file but then I get an error because file is in use.
Can anyone help?
I'd like to modify the current document without creating a new one. One way to do that would be create a new doc in a different directory, then delete the source doc and then save the newly created doc with the old doc's name but I wanted to see if there is a more elegant solution. Would appreciate any help
I am trying to add a header or footer to pages within a pdf document. This is explained in the iTextInAction book as the correct way to add direct content to a page. However when I try to open this document in Adobe I get the following error, and have some issues printing as well. Any ideas?
Dim reader As PdfReader = Nothing
Dim stamper As PdfStamper = Nothing
Try
reader = New PdfReader(inputFile)
stamper = New PdfStamper(reader, New IO.FileStream(outputFile, IO.FileMode.Append))
Dim fontSz As Single = 10.0F
Dim font As New Font(font.FontFamily.HELVETICA, fontSz, 1, BaseColor.GRAY)
Dim chunk As New Chunk(headerText, font)
Dim rect As Rectangle = reader.GetPageSizeWithRotation(1)
Here I am just adjusting the size of the text to make sure it fits within the page boundaries
While chunk.GetWidthPoint() > rect.Width
fontSz -= 1.0F
font = New Font(font.FontFamily.HELVETICA, fontSz, 1, BaseColor.GRAY)
chunk = New Chunk(wm.ToString(), font)
End While
This is where I get the overcontent and add my text to it
For pageNo As Int32 = 1 To reader.NumberOfPages
Dim phrase As New Phrase(chunk)
Dim x As Single = (rect.Width / 2) - (phrase.Chunks(0).GetWidthPoint() / 2)
Dim y As Single = If(wm.WatermarkPosition = "Header", rect.Height - font.Size, 1.0F)
Dim canvas As PdfContentByte = stamper.GetOverContent(pageNo)
canvas.BeginText()
ColumnText.ShowTextAligned(canvas, Element.ALIGN_LEFT, phrase, x, y, 0)
canvas.EndText()
Next
Catch ex As iTextSharp.text.pdf.BadPasswordException
Throw New InvalidOperationException("Page extraction is not supported for this pdf document. It must be allowed in order to add a watermark.")
Finally
reader.Close()
stamper.Close()
End Try
You're problem is probably this line:
stamper = New PdfStamper(reader, New IO.FileStream(outputFile, IO.FileMode.Append))
You are telling .Net to write the contents to a file in append mode. If the file doesn't exist then it creates the file but subsequent writes go to the end producing a corrupt PDF. You should change this to IO.FileMode.Create
Also, while you're at it, I usually recommend being even more explicit with your FileStream creation and tell .Net (and thus Windows) what you further intend for the stream. In this case, you are only ever going to write to it to you can say FileAccess.Write and while you are writing to it you want to make sure no one else attempts to read from it (since it would be in an invalid state) so you can say FileShare.None
stamper = New PdfStamper(reader, New FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None))
(An aside, although using IO.FileMode.Create is absolutely valid it is really weird to see. Most people either spell it out as System.IO.FileMode.Create or they import System.IO and then just us FileMode.Create.)