cro717
cro717

Reputation: 99

iTextSharp - Overwrite Text

In the code below, I am attempting to place a layer of text over existing text (the original existing text is "hidden" by writing it in white font; it is serving as a placeholder for this replacement process). The code below is very heavily reliant on code provided by Chris Haas in this post:

Getting Coordinates of string using ITextExtractionStrategy and LocationTextExtractionStrategy in Itextsharp

However, the "left" position for my text placement is fine, but the "bottom" is lower than the text I'm trying to overwrite (even though the font is the same). How can I alter this to get the correct coordinates of the text in the original document? The original document was created using tables for layout, so does that impact this "overwrite" process?

byte[] content;

string tmppath = @"C:\Junk\MSE_1.pdf";

using (MemoryStream output = new MemoryStream())
{
    PdfReader pdf_rdr = new PdfReader(tmppath);
    PdfStamper stamper = new PdfStamper(pdf_rdr, output);

    for (int i = 1; i < pdf_rdr.NumberOfPages; i++)
    {
        //Create an instance of our strategy
        var t = new MyLocationTextExtractionStrategy("stuff");

        if (t != null)
        {
            //Parse the page of the document above
            //(if the text was found)
            var ex = PdfTextExtractor.GetTextFromPage(pdf_rdr, i, t);

            //Loop through each chunk found
            foreach (var p in t.myPoints)
            {
                Console.WriteLine(string.Format("Found text {0} at {1} x {2} on page {3}", p.Text, p.Rect.Left, p.Rect.Bottom, i.ToString()));

                PdfContentByte pcb = stamper.GetOverContent(i);

                pcb.BeginText();

                try
                {
                    BaseFont bf = BaseFont.CreateFont(@"C:\Junk\FontFiles\georgia.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

                    pcb.SetFontAndSize(bf, 10);
                    pcb.SetTextMatrix(p.Rect.Left, p.Rect.Bottom);
                    //pcb.SetTextRise(3);
                    pcb.ShowText("STUFF");
                }
                finally
                {
                    pcb.EndText();
                }
            }
        }
    }

    // Set the flattening flag to true, as the editing is done
    stamper.FormFlattening = true;

    // close the pdf stamper
    stamper.Close();

    //close the PDF reader
    pdf_rdr.Close();

    //put the output into the byte array
    content = output.ToArray();
}

//write the content to a PDF file
using (FileStream fs = File.Create(@"C:\Junk\MSE_REPLACED.pdf"))
{
    fs.Write(content, 0, (int)content.Length);
    fs.Flush();
}

Upvotes: 0

Views: 1516

Answers (1)

mkl
mkl

Reputation: 96064

Chris' MyLocationTextExtractionStrategy only considers the descent line and the ascent line of the text pieces because it is interested in the area used by the string.

For your task, though, you need the base line because text drawing operations use the current position as base line start.

Thus, you should create a variant of Chris' class which manages the base line of the text instead of / in addition to its ascent and descent line.

PS: The OP asked in a comment

I'm afraid I'm not familiar with working with the base line. Do you have any examples you could share?

If you look at Chris' code you referred to, you'll see this RenderText implementation:

public override void RenderText(TextRenderInfo renderInfo) {
    base.RenderText(renderInfo);

    //Get the bounding box for the chunk of text
    var bottomLeft = renderInfo.GetDescentLine().GetStartPoint();
    var topRight = renderInfo.GetAscentLine().GetEndPoint();

    //Create a rectangle from it
    var rect = new iTextSharp.text.Rectangle(
                                            bottomLeft[Vector.I1],
                                            bottomLeft[Vector.I2],
                                            topRight[Vector.I1],
                                            topRight[Vector.I2]
                                            );

    //Add this to our main collection
    this.myPoints.Add(new RectAndText(rect, renderInfo.GetText()));
}

As you see he stores the rectangle with the lower left corner at the start of the descent line and the upper right corner being the end of the ascent line.

If you replace

    //Get the bounding box for the chunk of text
    var bottomLeft = renderInfo.GetDescentLine().GetStartPoint();
    var topRight = renderInfo.GetAscentLine().GetEndPoint();

by

    //Get the bounding box for the chunk of text above the baseline
    var bottomLeft = renderInfo.GetBaseline().GetStartPoint();
    var topRight = renderInfo.GetAscentLine().GetEndPoint();

your overwriting code should work just fine.

Upvotes: 2

Related Questions