C# implements keyword signature and stamping through iTextSharp (by inserting stamped pictures in the content)

This function reads the PDF document information through iTextSharp, and searches for each page of the PDF file in a loop. As long as it meets the conditions in the entire PDF, it will be stamped. For example, only the last page needs to be stamped. Please remove the For loop in the method, and put

PdfContentByte contentByte = pdfStamper. GetUnderContent(i);
parser. ProcessContent<PdfLocation>(i, location);

changed to

// Get the number of document pages
int pdfPageSize = pdfReader.NumberOfPages;
//The last page of the document, if you need the first page, directly change pdfPageSize to 1
PdfContentByte contentByte = pdfStamper. GetUnderContent(pdfPageSize);
//Read the content of the last page of the document
parser.ProcessContent<PdfLocation>(pdfPageSize, location);

After these few sentences are changed, it can be stamped on the exact page

The following is the specific code implementation:

1. For specific implementation of stamping, refer to iTextSharp.text.pdf

 /// <summary>
    /// pdf signature and seal, in the form of pictures
    /// </summary>
    public class PDFSealHelper
    {<!-- -->
        /// <summary>
        /// The maximum width of the image
        /// </summary>
        private static float ReSizeMaxWidth = 50;
        /// <summary>
        /// The maximum height of the image
        /// </summary>
        private static float ReSizeMaxHeight = 50;
        /// <summary>
        /// Sign and seal (file stream and Base64 format image)
        /// </summary>
        /// <param name="bytePdf">A pdf file that needs to be signed and stamped as a byte array</param>
        /// <param name="outPdfPath">Output pdf path after signing and stamping</param>
        /// <param name="SignImgBase64">Base64 format image</param>
        /// <param name="SignKeyWord">keyword</param>
        public static void SignBase64Img(byte[] bytePdf, string outPdfPath, string SignImgBase64, string SignKeyWord)
        {<!-- -->
            System.IO.Stream outputPdfStream = new System.IO.FileStream(outPdfPath, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None);
            // Create a PdfReader object
            PdfReader pdfReader = new PdfReader(bytePdf);
            PdfStamper pdfStamper = new PdfStamper(pdfReader, outputPdfStream);
            {<!-- -->
                // Get document pages
                int pdfPageSize = pdfReader.NumberOfPages;
                for (int i = 1; i <= pdfPageSize; i ++ )
                {<!-- -->
                    //Get the current page
                    PdfContentByte contentByte = pdfStamper. GetUnderContent(i);
                    PdfLocation location = new PdfLocation();
                    iTextSharp.text.pdf.parser.PdfReaderContentParser parser = new iTextSharp.text.pdf.parser.PdfReaderContentParser(pdfReader);
                    parser. ProcessContent<PdfLocation>(i, location);
                    //Find the keywords of the current page
                    location.SearchKeywords(SignKeyWord);
                    if (location. TextLocationInfo. Count > 0)
                    {<!-- -->
                        //The coordinates are from the lower left corner to the top, and the lower left corner is (0,0) zero point
                        XTextInfo o = location. TextLocationInfo[0];
                        //Get the starting coordinates of the upper left corner of the keyword
                        var ltLocation = o.TopLeft.ToString().Split(',');//272.15,766.46,1
                        var leftX = float. Parse(ltLocation[0]);
                        var topY = float. Parse(ltLocation[1]);
                        //Get the end coordinates of the bottom right corner of the keyword
                        var rbLocation = o.BottomRight.ToString().Split(',');//305.15,755.46,1
                        var rightX = float. Parse(rbLocation[0]);
                        var bottomY = float. Parse(rbLocation[1]);
                        //Calculate the center point of the keyword
                        float x = (rightX - leftX) / 2 + leftX;
                        float y = (topY - bottomY) / 2 + bottomY;
                        var imageByByte = ConvertBase64ToImage(SignImgBase64);
                        //Create an image object
                        iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(imageByByte, System.Drawing.Imaging.ImageFormat.Jpeg);
                        //Set the specified size of the image
                        float expectWidth = image.Width;
                        float expectHeight = image.Height;
                        if (image.Width > image.Height & amp; & amp; image.Width > ReSizeMaxWidth)
                        {<!-- -->
                            expectWidth = ReSizeMaxWidth;
                            expectHeight = expectWidth * image.Height / image.Width;
                        }
                        else if (image. Height > image. Width & amp; & amp; image. Height > ReSizeMaxHeight)
                        {<!-- -->
                            expectHeight = ReSizeMaxHeight;
                            expectWidth = expectHeight * image.Width / image.Height;
                        }
                        //Set the specified size of the image
                        image.ScalePercent(40);
                        //image.ScaleToFit(expectWidth, expectHeight);
                        //The seal position is at the bottom right of the keyword
                        image.SetAbsolutePosition(x + (expectWidth / 2), y + (expectHeight / 2) - expectHeight);
                        contentByte. AddImage(image);
                    }
                }
                pdfStamper. Close();
                pdfReader. Close();
                System.Diagnostics.Process.Start(outPdfPath);
            }
        }
        /// <summary>
        /// Signature and seal (file path)
        /// </summary>
        /// <param name="pdfPath">The path of the pdf file to be signed and stamped</param>
        /// <param name="outPdfPath">Output pdf path after signing and stamping</param>
        /// <param name="SignImgPath">Signature image path</param>
        /// <param name="SignKeyWord">keyword</param>
        public static void SignFile(string pdfPath, string outPdfPath, string SignImgPath, string SignKeyWord)
        {<!-- -->
            //Generate pdf after creating stamp
            System.IO.Stream outputPdfStream = new System.IO.FileStream(outPdfPath, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None);
            // Create a PdfReader object
            PdfReader pdfReader = new PdfReader(pdfPath);
            PdfStamper pdfStamper = new PdfStamper(pdfReader, outputPdfStream);
            // Get document pages
            int pdfPageSize = pdfReader.NumberOfPages;
            // loop through each page
            for (int i = 1; i <= pdfPageSize; i ++ )
            {<!-- -->
                //Get the current page
                //GetUnderContent is added to the lower layer of the content
                //GetOverContent is added to the upper layer of the content
                PdfContentByte contentByte = pdfStamper. GetUnderContent(i);
                PdfLocation location = new PdfLocation();
                iTextSharp.text.pdf.parser.PdfReaderContentParser parser = new iTextSharp.text.pdf.parser.PdfReaderContentParser(pdfReader);
                parser. ProcessContent<PdfLocation>(i, location);
                //Find the keywords and their coordinates of the current page
                location.SearchKeywords(SignKeyWord);
                if (location. TextLocationInfo. Count > 0)
                {<!-- -->
                    XTextInfo o = location. TextLocationInfo[0];
                    //Get the starting coordinates of the upper left corner of the keyword
                    var ltLocation = o.TopLeft.ToString().Split(',');//272.15,766.46,1
                    var leftX = float. Parse(ltLocation[0]);
                    var topY = float. Parse(ltLocation[1]);
                    //Get the end coordinates of the bottom right corner of the keyword
                    var rbLocation = o.BottomRight.ToString().Split(',');//305.15,755.46,1
                    var rightX = float. Parse(rbLocation[0]);
                    var bottomY = float. Parse(rbLocation[1]);
                    //Calculate the center point of the keyword
                    float x = (rightX - leftX) / 2 + leftX;
                    float y = (topY - bottomY) / 2 + bottomY;
                    //Create an image object
                    iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(SignImgPath);
                    float expectWidth = image.Width;
                    float expectHeight = image.Height;
                    if (image.Width > image.Height & amp; & amp; image.Width > ReSizeMaxWidth)
                    {<!-- -->
                        expectWidth = ReSizeMaxWidth;
                        expectHeight = expectWidth * image.Height / image.Width;
                    }
                    else if (image. Height > image. Width & amp; & amp; image. Height > ReSizeMaxHeight)
                    {<!-- -->
                        expectHeight = ReSizeMaxHeight;
                        expectWidth = expectHeight * image.Width / image.Height;
                    }
                    //Set the specified size of the image
                    image.ScalePercent(40);
                    //image.ScaleToFit(expectWidth, expectHeight);
                    //The seal position is at the bottom right of the keyword
                    image.SetAbsolutePosition(x + (expectWidth / 2), y + (expectHeight / 2) - expectHeight);
                    contentByte. AddImage(image);
                }
            }
            pdfStamper. Close();
            pdfReader. Close();
            System.Diagnostics.Process.Start(outPdfPath);
        }

        /// <summary>
        /// Base64 to image
        /// </summary>
        /// <param name="base64String"></param>
        /// <returns></returns>
        private static System.Drawing.Image ConvertBase64ToImage(string base64String)
        {<!-- -->
            byte[] imageBytes = Convert.FromBase64String(base64String);
            System.Drawing.Bitmap bitmap = null;
            MemoryStream stream = null;
            stream = new MemoryStream(imageBytes);
            bitmap = new System.Drawing.Bitmap(stream);
            return bitmap;
        }
    }

2. Find the pdf keyword coordinate class, refer to iTextSharp.text.pdf.parser

 public class PdfLocation : LocationTextExtractionStrategy
    {<!-- -->
        /// <summary>
        /// Document text and coordinates
        /// </summary>
        public List<XTextChunk> LocationResult = new List<XTextChunk>();
        /// <summary>
        /// Keywords and their coordinates
        /// </summary>
        public List<XTextInfo> TextLocationInfo = new List<XTextInfo>();
        /// <summary>
        /// Rewrite the method of reading document text. The first step is to execute this method, which cannot be deleted.
        /// After deletion, the LocationResult data is empty, which affects the execution of the keyword coordinate search method
        /// </summary>
        /// <param name="renderInfo">text rendering information</param>
        public override void RenderText(TextRenderInfo renderInfo)
        {<!-- -->
            LineSegment segment = renderInfo. GetBaseline();
            XTextChunk location = new XTextChunk(renderInfo.GetText(), segment.GetStartPoint(), segment.GetEndPoint(), renderInfo.GetSingleSpaceWidth(), renderInfo.GetAscentLine(), renderInfo.GetDescentLine());
            LocationResult. Add(location);
        }
        /// <summary>
        /// Keyword coordinate lookup
        /// </summary>
        /// <param name="sKeyword">keyword</param>
        public void SearchKeywords(string sKeyword)
        {<!-- -->
            var keyWordLen = sKeyword. Length;
            var keyWordList = sKeyword. ToList();
            LocationResult. Sort();
            TextLocationInfo. Clear();
            XTextInfo lastXTextInfo = new XTextInfo();
            if (LocationResult != null & amp; & amp; LocationResult. Any())
            {<!-- -->
                for (int i = 0; i < LocationResult. Count; i ++ )
                {<!-- -->
                    //When the first word of the keyword matches, it loops to match the next few keywords
                    if (LocationResult[i].Text == keyWordList[0].ToString())
                    {<!-- -->
                        if (keyWordLen > 1)
                        {<!-- -->
                            for (int j = 0; j < keyWordList.Count; j++ )
                            {<!-- -->
                            //The next few characters are all in line with the keyword
                                if (LocationResult[i + j].Text == keyWordList[j].ToString())
                                {<!-- -->
                                    lastXTextInfo.appendText(LocationResult[i + j]);
                                }
                            }
                        }
                        else
                        {<!-- -->
                            lastXTextInfo.appendText(LocationResult[i]);
                        }
                        if (lastXTextInfo. Text. Contains(sKeyword))
                        {<!-- -->
                            TextLocationInfo. Add(lastXTextInfo);
                            break;
                        }
                    }
                }
            }
        }
    }
    /// <summary>
    /// text block
    /// </summary>
    public class XTextChunk : IComparable, ICloneable
    {<!-- -->
        #region field
        /// <summary>
        /// ascending line segment
        /// </summary>
        public LineSegment AscentLine {<!-- --> get; set; }
        /// <summary>
        /// Descending segment
        /// </summary>
        public LineSegment DecentLine {<!-- --> get; set; }
        /// <summary>
        /// direction
        /// </summary>
        public Vector OrientationVector {<!-- --> get; set; }
        /// <summary>
        /// text
        /// </summary>
        public string Text {<!-- --> get; set; }
        /// <summary>
        /// character width
        /// </summary>
        public float CharSpaceWidth {<!-- --> get; set; }
        /// <summary>
        /// Parallel distance start
        /// </summary>
        public float DistParallelStart {<!-- --> get; set; }
        /// <summary>
        /// End of parallel distance
        /// </summary>
        public float DistParallelEnd {<!-- --> get; set; }
        /// <summary>
        /// vertical distance
        /// </summary>
        public int DistPerpendicular {<!-- --> get; set; }
        /// <summary>
        /// Direction amplitude (coordinate angle)
        /// </summary>
        public int OrientationMagnitude {<!-- --> get; set; }
        /// <summary>
        /// start coordinates
        /// </summary>
        public Vector StartLocation {<!-- --> get; set; }
        /// <summary>
        /// end coordinates
        /// </summary>
        public Vector EndLocation {<!-- --> get; set; }
        #endregion

        /// <summary>
        /// The text block, its orientation, and its position relative to the orientation vector
        /// </summary>
        /// <param name="txt"></param>
        /// <param name="startLoc"></param>
        /// <param name="endLoc"></param>
        /// <param name="charSpaceWidth"></param>
        public XTextChunk(string txt, Vector startLoc, Vector endLoc, float charSpaceWidth, LineSegment ascentLine, LineSegment decentLine)
        {<!-- -->
            Text = txt;
            StartLocation = startLoc;
            EndLocation = endLoc;
            CharSpaceWidth = charSpaceWidth;
            AscentLine = ascentLine;
            DecentLine = decentLine;

            OrientationVector = EndLocation.Subtract(StartLocation).Normalize();
            OrientationMagnitude = (int)(Math.Atan2(OrientationVector[Vector.I2], OrientationVector[Vector.I1]) * 1000);

            Vector origin = new Vector(0, 0, 1);
            DistPerpendicular = (int)(StartLocation.Subtract(origin)).Cross(OrientationVector)[Vector.I3];

            DistParallelStart = OrientationVector. Dot(StartLocation);
            DistParallelEnd = OrientationVector. Dot(EndLocation);
        }
        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {<!-- -->
            XTextChunk copy = new XTextChunk(Text, StartLocation, EndLocation, CharSpaceWidth, AscentLine, DecentLine);
            return copy;
        }
        /// <summary>
        /// Compare according to direction, vertical distance and parallel distance
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public int CompareTo(object obj)
        {<!-- -->
            if (obj == null) {<!-- -->throw new Exception ("obj parameter is empty");}
            XTextChunk rhs = obj as XTextChunk;
            if (rhs != null)
            {<!-- -->
                if (this == rhs) {<!-- --> return 0; }
                int rslt = OrientationMagnitude - rhs. OrientationMagnitude;
                if (rslt != 0) {<!-- --> return rslt; }

                rslt = DistPerpendicular - rhs.DistPerpendicular;
                if (rslt != 0) {<!-- --> return rslt; }

                // NOTE: It is not safe to check for equality of floating point numbers, if two blocks
                //really on top of each other, it doesn't matter which one comes first or second
                // So we chose this arbitrarily.
                rslt = DistParallelStart < rhs.DistParallelStart ? -1 : 1;
                return rslt;
            }
            else
            {<!-- -->
                throw new Exception ("XTextChunk is empty");
            }
        }
    }
    /// <summary>
    /// Qualified text information
    /// </summary>
    public class XTextInfo
    {<!-- -->
        #region field
        /// <summary>
        /// Start coordinates of upper left corner
        /// </summary>
        public Vector TopLeft {<!-- --> get; set; }
        /// <summary>
        /// The end coordinates of the lower right corner
        /// </summary>
        public Vector BottomRight {<!-- --> get; set; }
        /// <summary>
        /// keywords
        /// </summary>
        public string Text {<!-- --> get; set; }
        #endregion

        #region constructor
        /// <summary>
        /// Construction method
        /// </summary>
        /// <param name="initialXTextChunk"></param>
        public XTextInfo(XTextChunk initialXTextChunk)
        {<!-- -->
            // rising line = AscentLine
            TopLeft = initialXTextChunk. AscentLine. GetStartPoint();
            BottomRight = initialXTextChunk.DecentLine.GetEndPoint();
            Text = initialXTextChunk. Text;
        }
        /// <summary>
        /// Construction method
        /// </summary>
        public XTextInfo() {<!-- --> }
        #endregion
        
        /// <summary>
        /// Add text
        /// </summary>
        /// <param name="additionalXTextChunk"></param>
        public void appendText(XTextChunk additionalXTextChunk)
        {<!-- -->
            //DecentLine= descending line
            BottomRight = additionalXTextChunk.DecentLine.GetEndPoint();
            TopLeft = additionalXTextChunk.AscentLine.GetStartPoint();
            Text + = additionalXTextChunk. Text;
        }
    }

Only personal records, don’t spray if you don’t like it, thank you

Reference article: .net uses iTextSharp.pdf to operate pdf files to find keywords, sign and seal