Reputation: 10775
I am trying to get all images pdf and write some text just above that image.I am using following code but getting error of "cannot access closed file"
private static void InsertTextToPdf(string sourceFileName, string newFileName)
{
var img = new ReadPDFContent.ImagePDF.MyImageRenderListener();
using (Stream pdfStream = new FileStream(sourceFileName, FileMode.Open))
using (Stream newpdfStream = new FileStream(newFileName, FileMode.Create, FileAccess.ReadWrite))
{
PdfReader pdfReader = new PdfReader(pdfStream);
PdfStamper pdfStamper = new PdfStamper(pdfReader, newpdfStream);
PdfContentByte pdfContentByte = pdfStamper.GetOverContent(1);
BaseFont baseFont = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1250, BaseFont.NOT_EMBEDDED);
pdfContentByte.SetColorFill(BaseColor.BLUE);
pdfContentByte.SetFontAndSize(baseFont, 8);
pdfContentByte.BeginText();
pdfContentByte.ShowTextAligned(PdfContentByte.ALIGN_CENTER, " ", 400, 600, 0);
Dictionary<string, System.Drawing.Image> images;
images = new Dictionary<string, System.Drawing.Image>();
using (var reader = new PdfReader(sourceFileName))
{
var parser = new iTextSharp.text.pdf.parser.PdfReaderContentParser(reader);
ImageRenderListener listener = null;
for (var i = 1; i <= reader.NumberOfPages; i++)
{
parser.ProcessContent(i, (listener = new ImageRenderListener()));
var index = 1;
if (listener.Images.Count > 0)
{
try
{
var image = iTextSharp.text.Image.GetInstance(pdfStream);
float xval = image.AbsoluteX;
float yval = image.AbsoluteY;
image.SetAbsolutePosition(xval, yval);
pdfContentByte.ShowText("rahul");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
pdfContentByte.EndText();
pdfStamper.Close();
}
}
Error is on the following line :
iTextSharp.text.Image.GetInstance(pdfStream);
and in debug what i found that actually file read ,seeek are set to false after pdfreader read the stream.
Upvotes: 0
Views: 4669
Reputation: 95963
It's quite simple: Whenever a PdfReader
is constructed from a Stream
, that stream is closed after being read to the end. Thus, after
PdfReader pdfReader = new PdfReader(pdfStream);
pdfStream
is closed.
Yes, this does contradict the source code comment which claims "The stream is read to the end but is not closed":
/**
* Reads and parses a PDF document.
* @param isp the <CODE>InputStream</CODE> containing the document. The stream is read to the
* end but is not closed
* @throws IOException on error
*/
public PdfReader(Stream isp) : this(isp, null) {
}
/**
* Reads and parses a PDF document.
* @param is the <CODE>InputStream</CODE> containing the document. The stream is read to the
* end but is not closed
* @param ownerPassword the password to read the document
* @throws IOException on error
*/
public PdfReader(Stream isp, byte[] ownerPassword) : this(
new RandomAccessSourceFactory().CreateSource(isp),
false,
ownerPassword,
null,
null,
false) {
}
But looking at the used RandomAccessSourceFactory
method CreateSource
overload one sees:
/**
* Creates a {@link RandomAccessSource} based on an {@link InputStream}. The full content of the InputStream is read into memory and used
* as the source for the {@link RandomAccessSource}
* @param is the stream to read from
* @return the newly created {@link RandomAccessSource}
*/
public IRandomAccessSource CreateSource(Stream inp) {
try {
return CreateSource(StreamUtil.InputStreamToArray(inp));
}
finally {
try {inp.Close();}catch{}
}
}
I.e. an enforced Close
.
That been said, if the file was not closed, you would merely have a different exception.
During
PdfReader pdfReader = new PdfReader(pdfStream);
the stream is read to the end, so here
var image = iTextSharp.text.Image.GetInstance(pdfStream);
there would be nothing to read from the stream.
And even if you did reset the stream to the start before Image.GetInstance
, you would get yet another exception: Either the stream content is a PDF or a bitmap image. If it is a PDF, Image.GetInstance
will fail; if it is a bitmap, new PdfReader
will fail (assuming you don't use one of those constructed files which are both accepted as a bitmap and as a PDF by means of meshing the formats)
Concerning your task to write some text above images in the file, you can do that by creating a custom render listener (instead of using an existing one which ignores the image coordinates), e.g. like this:
public class ImageEntitlingRenderListener : IRenderListener
{
BaseFont baseFont = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1250, BaseFont.NOT_EMBEDDED);
PdfStamper pdfStamper = null;
int page = 0;
public ImageEntitlingRenderListener(PdfStamper pdfStamper, int page)
{
this.pdfStamper = pdfStamper;
this.page = page;
}
public void RenderImage(ImageRenderInfo renderInfo)
{
Matrix ctm = renderInfo.GetImageCTM();
float xCenter = ctm[Matrix.I31] + 0.5F * ctm[Matrix.I11];
float yTop = ctm[Matrix.I32] + ctm[Matrix.I22];
PdfContentByte pdfContentByte = pdfStamper.GetOverContent(page);
pdfContentByte.SetColorFill(BaseColor.BLUE);
pdfContentByte.SetFontAndSize(baseFont, 8);
pdfContentByte.BeginText();
pdfContentByte.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "rahul", xCenter, yTop, 0);
pdfContentByte.EndText();
}
public void BeginTextBlock() { }
public void EndTextBlock() { }
public void RenderText(TextRenderInfo renderInfo) { }
}
You can use it like this:
private static void InsertTextToPdf(string sourceFileName, string newFileName)
{
using (Stream pdfStream = new FileStream(sourceFileName, FileMode.Open))
using (Stream newpdfStream = new FileStream(newFileName, FileMode.Create, FileAccess.ReadWrite))
{
PdfReader pdfReader = new PdfReader(pdfStream);
PdfStamper pdfStamper = new PdfStamper(pdfReader, newpdfStream);
var parser = new PdfReaderContentParser(pdfReader);
for (var i = 1; i <= pdfReader.NumberOfPages; i++)
{
parser.ProcessContent(i, (new ImageEntitlingRenderListener(pdfStamper, i)));
}
pdfStamper.Close();
pdfReader.Close();
}
}
Beware, there are some simplifying assumptions in the code. In particular it assumes that images are drawn upright in the PDF. You might want to improve the code for a more generic use.
Upvotes: 3