rahularyansharma
rahularyansharma

Reputation: 10775

cannot access closed file itextsharp

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

Answers (1)

mkl
mkl

Reputation: 95963

Why closed?

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.

The attempted re-use

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)

The task

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

Related Questions