Search code examples
itext7

iText7 flips text - bug?


Consider a 1-page PDF document named in.pdf that looks like this:

enter image description here

We execute the following code in C#:

using (var reader = new PdfReader(@"C:\<path>\in.pdf"))
using (var writer = new PdfWriter(@"C:\<path>\out.pdf"))
using (var pdfDoc = new PdfDocument(reader, writer))
{
    var page = pdfDoc.GetFirstPage();
    var pdfCanvas = new PdfCanvas(page.NewContentStreamBefore(), page.GetResources(), pdfDoc);

    using (var canvas = new Canvas(pdfCanvas, new Rectangle(400, 300)))
    {
        canvas.Add(new Paragraph("new text\r\nnew text")
            .SetFontColor(new DeviceRgb(255, 0, 0))
            .SetFontSize(50));
    }

    pdfCanvas.Release();
}

We get new text appearing under the original text, as expected:

enter image description here

Now we execute the same code with one change - we create PdfCanvas as follows:

var pdfCanvas = new PdfCanvas(page);

Result is still as expected - new text is above the original text:

enter image description here

Finally, we execute the same code with PdfCanvas created as follows:

var pdfCanvas = new PdfCanvas(page.NewContentStreamAfter(), page.GetResources(), pdfDoc);

New text is above the original text as expected, but it's flipped!

enter image description here

Is this a bug? Getting the same flipped output creating PdfCanvas like this:

var pdfCanvas = new PdfCanvas(page.GetFirstContentStream(), page.GetResources(), pdfDoc);
//OR
var pdfCanvas = new PdfCanvas(page.GetLastContentStream(), page.GetResources(), pdfDoc);

Using nuget package itext7 v7.1.15.


Solution

  • Is this a bug?

    No. It's matching your code and a feature of the PdfCanvas(PdfPage page) constructor.

    Each PDF content stream contains a sequence of instructions that change the current graphics state and/or draw something. If the original creator of the respective content stream did not take care to clean up the graphics state, instructions you add to it or to a following content stream of the same page are subject to those graphics state changes.

    In your case the original page content stream apparently at the end has a graphics state with the current transformation matrix set to a reflection. Depending on how you add your new content, your additions also are subject to that reflection transformation:

    • var pdfCanvas = new PdfCanvas(page.NewContentStreamBefore(), page.GetResources(), pdfDoc);
      

      Here you add a new content stream before all existing content streams. Thus, your additions to pdfCanvas are not subject to any graphics state changes and you get upright text.

    • var pdfCanvas = new PdfCanvas(page);
      

      The constructor you use here, PdfCanvas(PdfPage page), calls another constructor, PdfCanvas(PdfPage page, bool wrapOldContent), which for wrapOldContent == true wraps the existing content in an envelope of a save-graphics-state / restore-graphics-state instruction pair, to restore the original graphics state after the existing content. PdfCanvas(PdfPage page) uses a true value for wrapOldContent under certain conditions, in particular if you are manipulating an existing PDF and the page in question already has a non-empty content stream.

      In your case, therefore, the original content is wrapped in such an envelope and the graphics state is restored thereafter. So your additions are not subject to any graphics state changes and you get upright text.

    • var pdfCanvas = new PdfCanvas(page.NewContentStreamAfter(), page.GetResources(), pdfDoc);
      
      var pdfCanvas = new PdfCanvas(page.GetFirstContentStream(), page.GetResources(), pdfDoc);
      
      var pdfCanvas = new PdfCanvas(page.GetLastContentStream(), page.GetResources(), pdfDoc);
      

      The PdfCanvas constructors you use here do not add any such envelope. Thus, your additions are subject to graphics state changes in the original content and you get mirrored text.