0

Based on the suggestion in the comments here, I refactored my method to try to assign data to a MemoryStream outside of the iTextSharp.text.Document "using" (but within the MemoryStream using clause):

internal static HttpResponseMessage GeneratePDFOfReportFutures()
{
    HttpResponseMessage result = null;
    futureReports = GetAllFutureReports();
    try
    {
        using (var ms = new MemoryStream())
        {
            using (var doc = new Document(PageSize.A4.Rotate(), 25, 25, 25, 25)) // the "Rotate" makes it landscape orientation
            {
                using (PdfWriter.GetInstance(doc, ms))
                {
                    doc.Open();
                    . . .
                    // "int i" is for testing a quick exit of this long-running loop
                    int i = 0;
                    foreach (QueuedReports qr in futureReports)
                    {
                        var tblRow = new PdfPTable(6)
                        {
                            WidthPercentage = 80,
                            SpacingBefore = 4f
                        };
                        float[] tblRowWidths = new float[] { 220f, 180f, 220f, 160f, 160f, 340f };
                        tblRow.SetWidths(tblRowWidths);
                        tblRow.HorizontalAlignment = Element.ALIGN_LEFT;

                        var phraseRptName = new Phrase(qr.ReportName, helvetica9);
                        var cellRptName = GetCellForBorderedTable(phraseRptName, Element.ALIGN_CENTER, BaseColor.WHITE);
                        cellRptName.VerticalAlignment = Element.ALIGN_MIDDLE;
                        tblRow.AddCell(cellRptName);

                        . . .
                        tblRow.AddCell(cellRecipients);

                        doc.Add(tblRow);

                        i++;
                        if (i >= 12)
                        {
                            break;
                        }
                    } // foreach
                } // pdfWriter
            } // doc
            var bytes = ms.ToArray();
            result = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new ByteArrayContent(bytes)
            };
            result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
            {
                FileName = string.Format("{0}.pdf", "test")
            };
            result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        } // memoryStream               
    } // try
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    return result;
} // GeneratePDFOfReportFutures

With a breakpoint in the "break" line, I step (F10) and the following exception is caught prior to reaching the assignment to bytes:

System.ObjectDisposedException was caught HResult=-2146232798
Message=Cannot access a closed Stream. Source=mscorlib
ObjectName="" StackTrace: at System.IO.__Error.StreamIsClosed() at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count) at iTextSharp.text.pdf.OutputStreamCounter.Write(Byte[] buffer, Int32 offset, Int32 count) at iTextSharp.text.pdf.PdfIndirectObject.WriteTo(Stream os) at iTextSharp.text.pdf.PdfWriter.PdfBody.Write(PdfIndirectObject indirect, Int32 refNumber, Int32 generation) at iTextSharp.text.pdf.PdfWriter.PdfBody.Add(PdfObject objecta, Int32 refNumber, Int32 generation, Boolean inObjStm) at iTextSharp.text.pdf.PdfWriter.PdfBody.Add(PdfObject objecta, PdfIndirectReference refa, Boolean inObjStm) at iTextSharp.text.pdf.PdfWriter.PdfBody.Add(PdfObject objecta, PdfIndirectReference refa) at iTextSharp.text.pdf.PdfWriter.AddToBody(PdfObject objecta, PdfIndirectReference refa) at iTextSharp.text.pdf.Type1Font.WriteFont(PdfWriter writer, PdfIndirectReference piref, Object[] parms) at iTextSharp.text.pdf.FontDetails.WriteFont(PdfWriter writer) at iTextSharp.text.pdf.PdfWriter.AddSharedObjectsToBody() at iTextSharp.text.pdf.PdfWriter.Close() at iTextSharp.text.DocWriter.Dispose()

Since accessing of PdfWriter and/or Doc appeared to be causing the problem, it would seem I need to move the writing code inside not only the doc using, but also the pdfWriter using, so I did that, moving the code like so:

                . . .
                } // foreach
                var bytes = ms.ToArray();
                result = new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new ByteArrayContent(bytes)
                };
                result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                {
                    FileName = string.Format("{0}.pdf", "test")
                };
                result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            } // pdfWriter
        } // doc
    } // memoryStream               
} // try
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}
return result;

...but I still get precisely the same exception. However, there is a difference between what I'm seeing now and what's described in the last post in that the exception is not thrown until I F10 on this line:

} // pdfWriter

If I move the return line above (and still keep it below the catch block) like so:

    return result;
} // pdfWriter

...the exception is thrown on F10ing on the "return result" line.

Why is this failing, and how can I prevent it?

Community
  • 1
  • 1
B. Clay Shannon-B. Crow Raven
  • 8,547
  • 144
  • 472
  • 862
  • 1
    It looks like that pdfwriter has a Dispose() that does stuff. This is poor design (since it will also fire when the code in the using block throws an exception). It also makes things hard to debug, as you have found, since things seem to go wrong "for no reason". Having said that, its Dispose() should fire before that of doc or ms. – Zastai May 09 '16 at 21:35
  • 1
    Oh, and mark stack traces as code - that tends to make them easier to read. – Zastai May 09 '16 at 21:36
  • Okay, so what can I do about that or how can I work around that? PdfWriter is not my code, it's 3rd party code, as is Doc (iTextSharp). – B. Clay Shannon-B. Crow Raven May 09 '16 at 21:38
  • 1
    It's possible some of the operations on doc you do in that inner `using` are triggering processing that ends up closing `ms`. I'd say: try stepping through the code, keeping an eye on `ms`, in particular its internals (like a closed or disposed flag). That would at least allow you to find at what point the stream gets closed. – Zastai May 09 '16 at 21:42
  • Henk's suggestion prevents the exception, but I'm still not getting the file downloaded for some reason. Very similar code works a treat with Excel files (as shown in the linked post here: http://stackoverflow.com/questions/37123068/how-can-i-assign-the-contents-of-an-itextsharp-text-document-to-either-a-byte-ar – B. Clay Shannon-B. Crow Raven May 09 '16 at 21:47
  • 3
    Don't use `using` for `PdfWriter`. See http://stackoverflow.com/questions/37095802 Also: you have solved one problem, now you have another problem that has been solved by thousands of developers before you, so the answer is on SO somewhere. In any case: the other problem you mention is visible only in the comments and people how to click "see more comments" to see it. This means that the chance you'll get an answer is close to 0. You did right to post a new question http://stackoverflow.com/questions/37126589 but I would suggest you delete this question as it's no longer a question. – Bruno Lowagie May 10 '16 at 06:30

0 Answers0