Search code examples
c#wpftextitextpositioning

iTextSharp (C#): Add text (over everything) at position


I'm trying to use iTextSharp in a WPF project to add text to existing PDFs over everything else and at a given point. Basically the digital equivalent of writing on a paper document

I've tried multiple methods and nothing seems to actually write the text to the new PDF. My code is messy from testing various things (left in so I know what I've tried - yes, I'll be clearing it out after resolution), so I'll try to get all of the important lines.

var stream = new FileStream(targetFilePath, FileMode.Create);
var doc = new iTextSharp.text.Document();
var target = new PdfCopy(doc, stream);

doc.Open();

var baseFont = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, false);

foreach (var vm in Pages) // Cycles through pages in WPF object...
{
    var srcDoc = vm.DocTmpFName;

    if (Path.GetExtension(srcDoc).ToUpperInvariant() == ".PDF")
    {
        var reader = new iTextSharp.text.pdf.PdfReader(srcDoc);
        var page = reader.GetPageN(vm.Number);
        var stamper = new PdfStamper(reader, stream);

        target.AddPage(target.GetImportedPage(reader, vm.Number));

        page.Put(PdfName.ROTATE, new PdfNumber(vm.Rotation % 360f));

        // Inside this 'foreach' loop is what isn't working.  The rest works fine.
        foreach(var str in vm.Strings) // str is a container class (just a string and two 'double' vars for positioning)
        {
            // This isn't writing to new PDF even though it gets run (verified with breakpoints)
            var contentByte = stamper.GetOverContent(vm.Number);

            contentByte.BeginText();
            contentByte.SetTextMatrix((float)str.X, (float)str.Y);
            contentByte.SetFontAndSize(baseFont, 72f); //Making it stupid-big so I don't miss it
            contentByte.ShowText(str.String);
            contentByte.EndText();
        }

        target.FreeReader(reader);
        reader.Close();

    }
}

target.Close();
doc.Close();
stream.Close();

If you feel I've missed something let me know. I tried to keep the context of the problem area, so hopefully it's not too horrible of a hack-job.

- Edited to correct code -


Edit

I renamed some variables to make it easier to discern what they're for. Also, I've included the new fixed (albeit buggy) code that adds text at a position - for anyone stumbling across this later. =)

Working code after @mkl's answer:

var stream = new FileStream(targetFilePath, FileMode.Create);
var doc = new iTextSharp.text.Document();
var targetPdf = new PdfCopy(doc, stream);

doc.Open();

foreach (ViewModels.PageViewModel vm in document.Pages)
{
    var srcDocPath = FileIO.ToTempFileName(vm.DocName);

    // Copy pageDict from source...
    if (Path.GetExtension(srcDocPath).ToUpperInvariant() == ".PDF")
    {
        var srcReader = new iTextSharp.text.pdf.PdfReader(srcDocPath);
        var pageDict = srcReader.GetPageN(vm.Number);
        var importedPage = targetPdf.GetImportedPage(srcReader, vm.Number);
        var pageStamp = targetPdf.CreatePageStamp(importedPage);

        foreach(var str in vm.Strings)
        {
            ColumnText.ShowTextAligned(pageStamp.GetOverContent(),
                iTextSharp.text.Element.ALIGN_LEFT,
                new iTextSharp.text.Phrase(str.String),
                (float) str.X,
                // pdf-origin is bottom-left, not top-left
                (float)(importedPage.Height - str.Y),
                0);
        }

        pageDict.Put(PdfName.ROTATE, new PdfNumber(vm.Rotation % 360f));
        pageStamp.AlterContents();
        targetPdf.AddPage(importedPage);

        targetPdf.FreeReader(srcReader);
        srcReader.Close();
    }
}
targetPdf.Close();
doc.Close();
stream.Close();

Solution

  • Your code has a number of issues some of which @Übercoder already addressed in his answer.

    Furthermore you have a PdfCopy and multiple PdfStamper instances writing into the same stream. This will result in utter chaos in the stream output. If you want to stamp pages you copy, you should use a PdfCopy.PageStamp, e.g. like this:

    using (PdfCopy copy = new PdfCopy(document, OUTPUT_STREAM)) {
        document.Open();
        PdfReader reader1 = new PdfReader(r1);
        int n1 = reader1.NumberOfPages;
        PdfImportedPage page;
        PdfCopy.PageStamp stamp;
        for (int i = 0; i < n1; ) {
            page = copy.GetImportedPage(reader1, ++i);
            stamp = copy.CreatePageStamp(page);
            // CHANGE THE PAGE,
            // e.g. by manipulating stamp.GetOverContent()
            ...
            //
            stamp.AlterContents();
            copy.AddPage(page);
        }
    }
    

    (excerpt from the official iTextSharp sample ConcatenateStamp.cs)