2

Can you please tell me, what's wrong with my code? I'm using it to merge PDFs, I create a memory stream then output it to PDF. It works fine for me, but some users cannot download file in IE, or get network error in Chrome:

public static MemoryStream MergePdfForms(List<byte[]> files)
    {
        if (files.Count > 1)
        {
            PdfReader pdfFile;
            Document doc;
            PdfWriter pCopy;
            MemoryStream msOutput = new MemoryStream();

            pdfFile = new PdfReader(files[0]);

            doc = new Document();
            pCopy = new PdfSmartCopy(doc, msOutput);

            doc.Open();

            for (int k = 0; k < files.Count; k++)
            {
                pdfFile = new PdfReader(files[k]);
                for (int i = 1; i < pdfFile.NumberOfPages + 1; i++)
                {
                    ((PdfSmartCopy)pCopy).AddPage(pCopy.GetImportedPage(pdfFile, i));
                }
                pCopy.FreeReader(pdfFile);
            }

            pdfFile.Close();
            pCopy.Close();
            doc.Close();

            return msOutput;
        }
        else if (files.Count == 1)
        {
            return new MemoryStream(files[0]);
        }

        return null;
    }

After debugging, I've noticed that the memory stream msOutput has some errors:

enter image description here

What's causing it, and how to avoid it?

Thank you.

Bruno Lowagie
  • 75,994
  • 9
  • 109
  • 165
Peter
  • 121
  • 1
  • 12
  • Hard to find confirmation in their docs, but usually when you wrap a stream in an object then close that object it also closes the wrapped stream. `pCopy = new PdfSmartCopy(doc, msOutput);` wraps `msOutput` into `pCopy`, then you call `pCopy.Close()` which closes `msOutput` (probably), then you try and return that closed stream. – Quantic Nov 04 '16 at 16:09
  • What's confusing is that you say "some users" are having this issue. If it's a matter of `msOutput` disposal, then *all* the users would be having trouble. – Matias Cicero Nov 04 '16 at 16:13
  • Yes, this was confusing for me too. If I download file like this: Response.BinaryWrite(ms.ToArray();, it works fine for me, but some users have issues. Why - no idea. But if I output stream this way: ms.WriteTo(Response.OutputStream); I'm getting error: Cannot access a closed Stream. – Peter Nov 04 '16 at 16:37

1 Answers1

2

Set CloseStream to false, otherwise the output stream will be closed when you call pCopy.Close().

pCopy = new PdfSmartCopy(doc, msOutput) { CloseStream = false };

Explanation

I found no documentation over the Internet so I had to look at the source code directly.

Here's the declaration of the PdfSmartCopy class:

public class PdfSmartCopy : PdfCopy
{
    // ...

    public PdfSmartCopy(Document document, Stream os) : base(document, os) {
        // ...
    }

    // ...
}

Here is the declaration of the PdfCopy class:

public class PdfCopy : PdfWriter
{
    // ...

    public PdfCopy(Document document, Stream os) : base(new PdfDocument(), os)     {
        // ...
    }

    // ...
}

The declaration of the PdfWriter class:

public class PdfWriter : DocWriter, 
    IPdfViewerPreferences,
    IPdfEncryptionSettings,
    IPdfVersion,
    IPdfDocumentActions,
    IPdfPageActions,
    IPdfIsoConformance,
    IPdfRunDirection,
    IPdfAnnotations
{
    // ...

    protected PdfWriter(PdfDocument document, Stream os) : base(document, os) {
        // ...
    }

    // ...
}

And finally, the declaration of DocWriter class:

public abstract class DocWriter : IDocListener
{
    // ...

    // default value is true
    protected bool closeStream = true;

    public virtual bool CloseStream {
        get {
            return closeStream;
        }
        set {
            closeStream = value;
        }
    }

     protected DocWriter(Document document, Stream os)  
     {
        this.document = document;
        this.os = new OutputStreamCounter(os);
     }

     public virtual void Close() {
        open = false;
        os.Flush();
        if (closeStream) // <-- Take a look at this line
            os.Close();
     }

     // ...
}
Community
  • 1
  • 1
Matias Cicero
  • 25,439
  • 13
  • 82
  • 154
  • No documentation? Not even on developers.itextpdf.com? Are you sure? – Amedee Van Gasse Nov 04 '16 at 16:29
  • @AmedeeVanGasse I already looked over there. There's only a reference page for the Java API, not for the .NET wrapper. – Matias Cicero Nov 04 '16 at 16:31
  • You're right @AmedeeVanGasse Matias is talking bullshit. I just answered an almost identical question yesterday: http://stackoverflow.com/questions/40401695/ – Bruno Lowagie Nov 04 '16 at 16:31
  • @BrunoLowagie So my answer is bullshit or the fact that I did not find documentation is bullshit? – Matias Cicero Nov 04 '16 at 16:32
  • The Java and the .NET API are identical, so any documentation for the Java version is also valid for the .NET version. – Amedee Van Gasse Nov 04 '16 at 16:33
  • The answer isn't bullshit @MatiasCicero but your allegation that there is no documentation to be found on the internet is uncalled for, unnecessary, and insulting. In my comment, I returned the favor. I could prove you wrong with a single link. – Bruno Lowagie Nov 04 '16 at 16:34
  • @BrunoLowagie Please be my guest. I want to see where is this exact behavior documented. – Matias Cicero Nov 04 '16 at 16:35
  • You are right in general @AmedeeVanGasse, but not in this case. In Java, a `ByteArrayOutputStream` behaves the way one expects it to behave. When you close such a stream, the content is made final and you can get the `byte[]` stored in the stream. In C#, a `MemoryStream` seems to throw away all data once you close it. That's pretty annoying, and it makes a difference in the context of iText vs iTextSharp. – Bruno Lowagie Nov 04 '16 at 16:36
  • Oh, the joys of I/O implementation differences between Java and .NET. *sigh* We *do* have this difference documented, don't we? – Amedee Van Gasse Nov 04 '16 at 16:38
  • Where is it documented? Here: http://stackoverflow.com/a/40401817/1622493 and here http://stackoverflow.com/questions/6374169/itextsharp-is-producing-a-corrupt-pdf and here http://stackoverflow.com/questions/27188593/getting-pdfstamper-to-work-with-memorystreams-c-itextsharp (that's 3 links already, I hope that's sufficient for you) – Bruno Lowagie Nov 04 '16 at 16:38
  • 1
    @BrunoLowagie Ok, so I have to look at SO questions with completely different titles and find out that the answer is hidden between irrelevant information. Yes, that's what I call official documentation. – Matias Cicero Nov 04 '16 at 16:40
  • I'm sure it's also somewhere in the C# examples, @AmedeeVanGasse but GitLab is still down and I don't have any access to the C# examples as long as GitLab hasn't been restored. – Bruno Lowagie Nov 04 '16 at 16:41
  • Brilliant, Matias, it worked, thank you very much! I still see couple errors in the stream, like: + ReadTimeout 'msOutput.ReadTimeout' threw an exception of type 'System.InvalidOperationException' int {System.InvalidOperationException}, but they don't affect anything. – Peter Nov 04 '16 at 16:42
  • Er @MatiasCicero, are you saying that Stack Overflow isn't a major source of information for developers? – Bruno Lowagie Nov 04 '16 at 16:42
  • @BrunoLowagie Please don't change my words. It's one of the best sources of information out there. But it's not documentation. It's just a community forum (if you want to put it that way). Unless, of course, that we're talking about [The new Documentation section](http://stackoverflow.com/documentation), but I don't see `itextsharp` over there. – Matias Cicero Nov 04 '16 at 16:47
  • You said "I found no documentation over the Internet" and I was able to pull up three relevant Stack Overflow questions with titles involving merging and MemoryStream that mentioned the solution that was needed. You claim that they had completely different titles. So be it. If you want to be a troll, by all means be a troll. Have a nice day. – Bruno Lowagie Nov 04 '16 at 16:49
  • You may not understand why this matters to me, @MatiasCicero but I spend many days, weeks, even months writing documentation and answering questions. At the same time, many developers use iText or iTextSharp without giving anything in return. Developers who think "Hey it's on Stack Overflow, we can use that code." That's not true, all code on Stack Overflow is licensed code: CC-BY-SA (see http://www.slideshare.net/blowagie/open-source-an-introduction-to-ip-and-legal ) and many developers infringe that license. The same goes for the AGPL license that is used for iText(Sharp). – Bruno Lowagie Nov 04 '16 at 16:53
  • @BrunoLowagie "major source of information" is different than "documentation", where documentation is written by the company itself and is supported by the actual developers. StackOverflow answers are written by anyone and answers that are 100% hearsay can be top voted answers for years. You are touting [`Secondary Sources`](http://lib1.bmcc.cuny.edu/help/sources/) as if they are equivalent to a Primary Source where a primary source is, "Factual, not interpretive". Matias's answer is from a primary source (the source code). Docs are also primary sources. SO is not. – Quantic Nov 04 '16 at 16:54
  • @Quantic I have answered 1857 questions on Stack Overflow and I've turned a compilation of the best answers into a primary source, on the official web site ([FAQ](http://developers.itextpdf.com/frequently-asked-developer-questions)) as well as in an ebook ([The Best iText Questions on Stack Overflow](https://leanpub.com/itext_so)). If that isn't documentation, then what is? Those answers are from the original developer of iText (being me). Are you saying that what I write is hearsay? Wow, many trolls on Stack Overflow today. – Bruno Lowagie Nov 04 '16 at 16:58
  • I was going to write some more documentation, but since my work isn't appreciated anyway, I'm going to start my weekend. – Bruno Lowagie Nov 04 '16 at 17:00
  • @BrunoLowagie I can go ahead and edit my answer and change the source code I posted in any way I want. For instance, I can go ahead and change the `Close` method, I can remove the `os.Flush()` line. Any viewer would see my code and think I did my research, but the code (while it's valid) is not correct. Answers in SO can be edited freely by anyone, and at certain point, with no review at all. So, **no**, it's not documentation. – Matias Cicero Nov 04 '16 at 17:01
  • 1
    @BrunoLowagie "Those answers are from the original developer of iText (being me)." -- Very helpful information, yes, but until SO grants you a tag that says, "official developer of iText", then the average user (me) has no idea you are more authoritative then some random user (also me). If I want to know whether StreamReader [`Closes the underlying stream or not`](https://msdn.microsoft.com/en-us/library/system.io.streamreader.close(v=vs.110).aspx) then I go to the docs to find out. When SO decides to sell out then iText just lost their only source of usable "documentation", great. – Quantic Nov 04 '16 at 17:03