使用Itextsharp替换pdf文件中的文本

时间:2017-05-16 18:43:57

标签: c# itext

尝试使用以前创建的代码替换pdf格式的文本。但是这条线用红色&my; myLocationTextExtractionStrategy strategy = new myLocationTextExtractionStrategy();' 。我是否需要创建一些自定义策略或者我可以使用一些库?

class Program
{
    static iTextSharp.text.pdf.PdfStamper stamper = null;
    static void Main(string[] args)
    {
        var oldFile = @"C:\oldFile.pdf";
        var newFile = @"C:\newFile.pdf";

        string replacingVariable = "Test";
        PdfReader pReader = new PdfReader(oldFile);
        stamper = new iTextSharp.text.pdf.PdfStamper(pReader, new System.IO.FileStream(newFile, System.IO.FileMode.Create));
        PDFTextGetter("ExistingVariableinPDF", replacingVariable, StringComparison.CurrentCultureIgnoreCase, oldFile, newFile);
        stamper.Close();
        pReader.Close();
    }

    public static void PDFTextGetter(string pSearch, string replacingText, StringComparison SC, string SourceFile, string DestinationFile)
    {
        try
        {
            iTextSharp.text.pdf.PdfContentByte cb = null;
            iTextSharp.text.pdf.PdfContentByte cb2 = null;
            iTextSharp.text.pdf.PdfWriter writer = null;
            iTextSharp.text.pdf.BaseFont bf = null;

            if (System.IO.File.Exists(SourceFile))
            {
                PdfReader pReader = new PdfReader(SourceFile);
                for (int page = 1; page <= pReader.NumberOfPages; page++)
                {
                    myLocationTextExtractionStrategy strategy = new myLocationTextExtractionStrategy();
                    cb = stamper.GetOverContent(page);
                    cb2 = stamper.GetOverContent(page);
                    strategy.UndercontentCharacterSpacing = (int)cb.CharacterSpacing;
                    strategy.UndercontentHorizontalScaling = (int)cb.HorizontalScaling;
                    string currentText = PdfTextExtractor.GetTextFromPage(pReader, page, strategy);

                    List<iTextSharp.text.Rectangle> MatchesFound = strategy.GetTextLocations(pSearch, SC);
                    cb.SetColorFill(BaseColor.WHITE);
                    foreach (iTextSharp.text.Rectangle rect in MatchesFound)
                    {
                        cb.Rectangle(rect.Left, rect.Bottom, 60, rect.Height);
                        cb.Fill();
                        cb2.SetColorFill(BaseColor.BLACK);
                        bf = BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
                        cb2.SetFontAndSize(bf, 9);
                        cb2.BeginText();
                        cb2.ShowTextAligned(0, replacingText, rect.Left, rect.Bottom, 0);
                        cb2.EndText();
                        cb2.Fill();
                    }
                }
            }
        }
        catch (Exception ex)
        {
        }
    }
}

2 个答案:

答案 0 :(得分:1)

您的来源中使用的myLocationTextExtractionStrategy类似乎是从example Project中共享的问题Jcis中的his answer堆栈溢出用户"how to highlight a text or word in a pdf file using iTextsharp?"中获取的。

他用VB.Net编写了它:

Public Class myLocationTextExtractionStrategy
    Implements ITextExtractionStrategy

    '* set to true for debugging 

    Private _UndercontentCharacterSpacing = 0
    Private _UndercontentHorizontalScaling = 0
    Private ThisPdfDocFonts As SortedList(Of String, DocumentFont)

    Public Shared DUMP_STATE As Boolean = False

    '* a summary of all found text 

    Private locationalResult As New List(Of TextChunk)()

    '*
    '         * Creates a new text extraction renderer.
    '         

    Public Sub New()
        ThisPdfDocFonts = New SortedList(Of String, DocumentFont)
    End Sub

    '*
    '         * @see com.itextpdf.text.pdf.parser.RenderListener#beginTextBlock()
    '         

    Public Overridable Sub BeginTextBlock() Implements ITextExtractionStrategy.BeginTextBlock
    End Sub

    '*
    '         * @see com.itextpdf.text.pdf.parser.RenderListener#endTextBlock()
    '         

    Public Overridable Sub EndTextBlock() Implements ITextExtractionStrategy.EndTextBlock
    End Sub

    '*
    '         * @param str
    '         * @return true if the string starts with a space character, false if the string is empty or starts with a non-space character
    '         

    Private Function StartsWithSpace(ByVal str As [String]) As Boolean
        If str.Length = 0 Then
            Return False
        End If
        Return str(0) = " "c
    End Function

    '*
    '         * @param str
    '         * @return true if the string ends with a space character, false if the string is empty or ends with a non-space character
    '         

    Private Function EndsWithSpace(ByVal str As [String]) As Boolean
        If str.Length = 0 Then
            Return False
        End If
        Return str(str.Length - 1) = " "c
    End Function

    Public Property UndercontentCharacterSpacing
        Get
            Return _UndercontentCharacterSpacing
        End Get
        Set(ByVal value)
            _UndercontentCharacterSpacing = value
        End Set
    End Property

    Public Property UndercontentHorizontalScaling
        Get
            Return _UndercontentHorizontalScaling
        End Get
        Set(ByVal value)
            _UndercontentHorizontalScaling = value
        End Set
    End Property

    Public Overridable Function GetResultantText() As [String] Implements ITextExtractionStrategy.GetResultantText

        If DUMP_STATE Then
            DumpState()
        End If

        locationalResult.Sort()

        Dim sb As New StringBuilder()
        Dim lastChunk As TextChunk = Nothing

        For Each chunk As TextChunk In locationalResult

            If lastChunk Is Nothing Then
                sb.Append(chunk.text)
            Else
                If chunk.SameLine(lastChunk) Then
                    Dim dist As Single = chunk.DistanceFromEndOf(lastChunk)
                    If dist < -chunk.charSpaceWidth Then
                        sb.Append(" "c)
                        ' we only insert a blank space if the trailing character of the previous string wasn't a space, and the leading character of the current string isn't a space
                    ElseIf dist > chunk.charSpaceWidth / 2.0F AndAlso Not StartsWithSpace(chunk.text) AndAlso Not EndsWithSpace(lastChunk.text) Then
                        sb.Append(" "c)
                    End If

                    sb.Append(chunk.text)
                Else
                    sb.Append(ControlChars.Lf)
                    sb.Append(chunk.text)
                End If
            End If
            lastChunk = chunk
        Next

        Return sb.ToString()

    End Function

    Public Function GetTextLocations(ByVal pSearchString As String, ByVal pStrComp As System.StringComparison) As List(Of iTextSharp.text.Rectangle)
        Dim FoundMatches As New List(Of iTextSharp.text.Rectangle)
        Dim sb As New StringBuilder()
        Dim ThisLineChunks As List(Of TextChunk) = New List(Of TextChunk)
        Dim bStart As Boolean, bEnd As Boolean
        Dim FirstChunk As TextChunk = Nothing, LastChunk As TextChunk = Nothing
        Dim sTextInUsedChunks As String = vbNullString

        For Each chunk As TextChunk In locationalResult

            If ThisLineChunks.Count > 0 AndAlso Not chunk.SameLine(ThisLineChunks.Last) Then
                If sb.ToString.IndexOf(pSearchString, pStrComp) > -1 Then
                    Dim sLine As String = sb.ToString

                    'Check how many times the Search String is present in this line:
                    Dim iCount As Integer = 0
                    Dim lPos As Integer
                    lPos = sLine.IndexOf(pSearchString, 0, pStrComp)
                    Do While lPos > -1
                        iCount += 1
                        If lPos + pSearchString.Length > sLine.Length Then Exit Do Else lPos = lPos + pSearchString.Length
                        lPos = sLine.IndexOf(pSearchString, lPos, pStrComp)
                    Loop

                    'Process each match found in this Text line:
                    Dim curPos As Integer = 0
                    For i As Integer = 1 To iCount
                        Dim sCurrentText As String, iFromChar As Integer, iToChar As Integer

                        iFromChar = sLine.IndexOf(pSearchString, curPos, pStrComp)
                        curPos = iFromChar
                        iToChar = iFromChar + pSearchString.Length - 1
                        sCurrentText = vbNullString
                        sTextInUsedChunks = vbNullString
                        FirstChunk = Nothing
                        LastChunk = Nothing

                        'Get first and last Chunks corresponding to this match found, from all Chunks in this line
                        For Each chk As TextChunk In ThisLineChunks
                            sCurrentText = sCurrentText & chk.text

                            'Check if we entered the part where we had found a matching String then get this Chunk (First Chunk)
                            If Not bStart AndAlso sCurrentText.Length - 1 >= iFromChar Then
                                FirstChunk = chk
                                bStart = True
                            End If

                            'Keep getting Text from Chunks while we are in the part where the matching String had been found
                            If bStart And Not bEnd Then
                                sTextInUsedChunks = sTextInUsedChunks & chk.text
                            End If

                            'If we get out the matching String part then get this Chunk (last Chunk)
                            If Not bEnd AndAlso sCurrentText.Length - 1 >= iToChar Then
                                LastChunk = chk
                                bEnd = True
                            End If

                            'If we already have first and last Chunks enclosing the Text where our String pSearchString has been found 
                            'then it's time to get the rectangle, GetRectangleFromText Function below this Function, there we extract the pSearchString locations
                            If bStart And bEnd Then
                                FoundMatches.Add(GetRectangleFromText(FirstChunk, LastChunk, pSearchString, sTextInUsedChunks, iFromChar, iToChar, pStrComp))
                                curPos = curPos + pSearchString.Length
                                bStart = False : bEnd = False
                                Exit For
                            End If
                        Next
                    Next
                End If
                sb.Clear()
                ThisLineChunks.Clear()
            End If
            ThisLineChunks.Add(chunk)
            sb.Append(chunk.text)
        Next

        Return FoundMatches
    End Function

    Private Function GetRectangleFromText(ByVal FirstChunk As TextChunk, ByVal LastChunk As TextChunk, ByVal pSearchString As String, _
                               ByVal sTextinChunks As String, ByVal iFromChar As Integer, ByVal iToChar As Integer, ByVal pStrComp As System.StringComparison) As iTextSharp.text.Rectangle

        'There are cases where Chunk contains extra text at begining and end, we don't want this text locations, we need to extract the pSearchString location inside
        'for these cases we need to crop this String (left and Right), and measure this excedent at left and right, at this point we don't have any direct way to make a
        'Transformation from text space points to User Space units, the matrix for making this transformation is not accesible from here, so for these special cases when
        'the String needs to be cropped (Left/Right) We'll interpolate between the width from Text in Chunk (we have this value in User Space units), then i'll measure Text corresponding
        'to the same String but in Text Space units, finally from the relation betweeenthese 2 values I get the TransformationValue I need to use for all cases

        'Text Width in User Space Units
        Dim LineRealWidth As Single = LastChunk.PosRight - FirstChunk.PosLeft

        'Text Width in Text Units
        Dim LineTextWidth As Single = GetStringWidth(sTextinChunks, LastChunk.curFontSize, _
                                                     LastChunk.charSpaceWidth, _
                                                     ThisPdfDocFonts.Values.ElementAt(LastChunk.FontIndex))
        'TransformationValue value for Interpolation
        Dim TransformationValue As Single = LineRealWidth / LineTextWidth

        'In the worst case, we'll need to crop left and right:
        Dim iStart As Integer = sTextinChunks.IndexOf(pSearchString, pStrComp)

        Dim iEnd As Integer = iStart + pSearchString.Length - 1

        Dim sLeft As String
        If iStart = 0 Then sLeft = vbNullString Else sLeft = sTextinChunks.Substring(0, iStart)

        Dim sRight As String
        If iEnd = sTextinChunks.Length - 1 Then sRight = vbNullString Else sRight = sTextinChunks.Substring(iEnd + 1, sTextinChunks.Length - iEnd - 1)

        'Measure cropped Text at left:
        Dim LeftWidth As Single = 0
        If iStart > 0 Then
            LeftWidth = GetStringWidth(sLeft, LastChunk.curFontSize, _
                                              LastChunk.charSpaceWidth, _
                                              ThisPdfDocFonts.Values.ElementAt(LastChunk.FontIndex))
            LeftWidth = LeftWidth * TransformationValue
        End If

        'Measure cropped Text at right:
        Dim RightWidth As Single = 0
        If iEnd < sTextinChunks.Length - 1 Then
            RightWidth = GetStringWidth(sRight, LastChunk.curFontSize, _
                                                LastChunk.charSpaceWidth, _
                                                ThisPdfDocFonts.Values.ElementAt(LastChunk.FontIndex))
            RightWidth = RightWidth * TransformationValue
        End If

        'LeftWidth is the text width at left we need to exclude, FirstChunk.distParallelStart is the distance to left margin, both together will give us this LeftOffset
        Dim LeftOffset As Single = FirstChunk.distParallelStart + LeftWidth
        'RightWidth is the text width at right we need to exclude, FirstChunk.distParallelEnd is the distance to right margin, we substract RightWidth from distParallelEnd to get RightOffset
        Dim RightOffset As Single = LastChunk.distParallelEnd - RightWidth
        'Return this Rectangle
        Return New iTextSharp.text.Rectangle(LeftOffset, FirstChunk.PosBottom, RightOffset, FirstChunk.PosTop)

    End Function

    Private Function GetStringWidth(ByVal str As String, ByVal curFontSize As Single, ByVal pSingleSpaceWidth As Single, ByVal pFont As DocumentFont) As Single
        Dim chars() As Char = str.ToCharArray()
        Dim totalWidth As Single = 0
        Dim w As Single = 0

        For Each c As Char In chars
            w = pFont.GetWidth(c) / 1000
            totalWidth += (w * curFontSize + Me.UndercontentCharacterSpacing) * Me.UndercontentHorizontalScaling / 100
        Next

        Return totalWidth
    End Function

    Private Sub DumpState()
        For Each location As TextChunk In locationalResult
            location.PrintDiagnostics()
            Console.WriteLine()
        Next
    End Sub

    Public Overridable Sub RenderText(ByVal renderInfo As TextRenderInfo) Implements ITextExtractionStrategy.RenderText
        Dim segment As LineSegment = renderInfo.GetBaseline()
        Dim location As New TextChunk(renderInfo.GetText(), segment.GetStartPoint(), segment.GetEndPoint(), renderInfo.GetSingleSpaceWidth())

        With location

            'Chunk Location:
            Debug.Print(renderInfo.GetText)
            .PosLeft = renderInfo.GetDescentLine.GetStartPoint(Vector.I1)
            .PosRight = renderInfo.GetAscentLine.GetEndPoint(Vector.I1)
            .PosBottom = renderInfo.GetDescentLine.GetStartPoint(Vector.I2)
            .PosTop = renderInfo.GetAscentLine.GetEndPoint(Vector.I2)
            'Chunk Font Size: (Height)
            .curFontSize = .PosTop - segment.GetStartPoint()(Vector.I2)
            'Use Font name  and Size as Key in the SortedList
            Dim StrKey As String = renderInfo.GetFont.PostscriptFontName & .curFontSize.ToString
            'Add this font to ThisPdfDocFonts SortedList if it's not already present
            If Not ThisPdfDocFonts.ContainsKey(StrKey) Then ThisPdfDocFonts.Add(StrKey, renderInfo.GetFont)
            'Store the SortedList index in this Chunk, so we can get it later
            .FontIndex = ThisPdfDocFonts.IndexOfKey(StrKey)
        End With
        locationalResult.Add(location)
    End Sub

    '*
    '         * Represents a chunk of text, it's orientation, and location relative to the orientation vector
    '         

    Public Class TextChunk
        Implements IComparable(Of TextChunk)
        '* the text of the chunk 

        Friend text As [String]
        '* the starting location of the chunk 

        Friend startLocation As Vector
        '* the ending location of the chunk 

        Friend endLocation As Vector
        '* unit vector in the orientation of the chunk 

        Friend orientationVector As Vector
        '* the orientation as a scalar for quick sorting 

        Friend orientationMagnitude As Integer
        '* perpendicular distance to the orientation unit vector (i.e. the Y position in an unrotated coordinate system)
        '             * we round to the nearest integer to handle the fuzziness of comparing floats 

        Friend distPerpendicular As Integer
        '* distance of the start of the chunk parallel to the orientation unit vector (i.e. the X position in an unrotated coordinate system) 

        Friend distParallelStart As Single
        '* distance of the end of the chunk parallel to the orientation unit vector (i.e. the X position in an unrotated coordinate system) 

        Friend distParallelEnd As Single
        '* the width of a single space character in the font of the chunk 

        Friend charSpaceWidth As Single

        Private _PosLeft As Single

        Private _PosRight As Single

        Private _PosTop As Single

        Private _PosBottom As Single

        Private _curFontSize As Single

        Private _FontIndex As Integer

        Public Property FontIndex As Integer
            Get
                Return _FontIndex
            End Get
            Set(ByVal value As Integer)
                _FontIndex = value
            End Set
        End Property

        Public Property PosLeft As Single
            Get
                Return _PosLeft
            End Get
            Set(ByVal value As Single)
                _PosLeft = value
            End Set
        End Property

        Public Property PosRight As Single
            Get
                Return _PosRight
            End Get
            Set(ByVal value As Single)
                _PosRight = value
            End Set
        End Property

        Public Property PosTop As Single
            Get
                Return _PosTop
            End Get
            Set(ByVal value As Single)
                _PosTop = value
            End Set
        End Property

        Public Property PosBottom As Single
            Get
                Return _PosBottom
            End Get
            Set(ByVal value As Single)
                _PosBottom = value
            End Set
        End Property

        Public Property curFontSize As Single
            Get
                Return _curFontSize
            End Get
            Set(ByVal value As Single)
                _curFontSize = value
            End Set
        End Property

        Public Sub New(ByVal str As [String], ByVal startLocation As Vector, ByVal endLocation As Vector, ByVal charSpaceWidth As Single)
            Me.text = str
            Me.startLocation = startLocation
            Me.endLocation = endLocation
            Me.charSpaceWidth = charSpaceWidth

            Dim oVector As Vector = endLocation.Subtract(startLocation)
            If oVector.Length = 0 Then
                oVector = New Vector(1, 0, 0)
            End If
            orientationVector = oVector.Normalize()
            orientationMagnitude = CInt(Math.Truncate(Math.Atan2(orientationVector(Vector.I2), orientationVector(Vector.I1)) * 1000))

            Dim origin As New Vector(0, 0, 1)
            distPerpendicular = CInt((startLocation.Subtract(origin)).Cross(orientationVector)(Vector.I3))

            distParallelStart = orientationVector.Dot(startLocation)
            distParallelEnd = orientationVector.Dot(endLocation)
        End Sub

        Public Sub PrintDiagnostics()
            Console.WriteLine("Text (@" & Convert.ToString(startLocation) & " -> " & Convert.ToString(endLocation) & "): " & text)
            Console.WriteLine("orientationMagnitude: " & orientationMagnitude)
            Console.WriteLine("distPerpendicular: " & distPerpendicular)
            Console.WriteLine("distParallel: " & distParallelStart)
        End Sub

        '*
        '             * @param as the location to compare to
        '             * @return true is this location is on the the same line as the other
        '             

        Public Function SameLine(ByVal a As TextChunk) As Boolean
            If orientationMagnitude <> a.orientationMagnitude Then
                Return False
            End If
            If distPerpendicular <> a.distPerpendicular Then
                Return False
            End If
            Return True
        End Function

        '*
        '             * Computes the distance between the end of 'other' and the beginning of this chunk
        '             * in the direction of this chunk's orientation vector.  Note that it's a bad idea
        '             * to call this for chunks that aren't on the same line and orientation, but we don't
        '             * explicitly check for that condition for performance reasons.
        '             * @param other
        '             * @return the number of spaces between the end of 'other' and the beginning of this chunk
        '             

        Public Function DistanceFromEndOf(ByVal other As TextChunk) As Single
            Dim distance As Single = distParallelStart - other.distParallelEnd
            Return distance
        End Function

        '*
        '             * Compares based on orientation, perpendicular distance, then parallel distance
        '             * @see java.lang.Comparable#compareTo(java.lang.Object)
        '             

        Public Function CompareTo(ByVal rhs As TextChunk) As Integer Implements System.IComparable(Of TextChunk).CompareTo
            If Me Is rhs Then
                Return 0
            End If
            ' not really needed, but just in case
            Dim rslt As Integer
            rslt = CompareInts(orientationMagnitude, rhs.orientationMagnitude)
            If rslt <> 0 Then
                Return rslt
            End If

            rslt = CompareInts(distPerpendicular, rhs.distPerpendicular)
            If rslt <> 0 Then
                Return rslt
            End If

            ' note: it's never safe to check floating point numbers for equality, and if two chunks
            ' are truly right on top of each other, which one comes first or second just doesn't matter
            ' so we arbitrarily choose this way.
            rslt = If(distParallelStart < rhs.distParallelStart, -1, 1)

            Return rslt
        End Function

        '*
        '             *
        '             * @param int1
        '             * @param int2
        '             * @return comparison of the two integers
        '             

        Private Shared Function CompareInts(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
            Return If(int1 = int2, 0, If(int1 < int2, -1, 1))
        End Function


    End Class

    '*
    '         * no-op method - this renderer isn't interested in image events
    '         * @see com.itextpdf.text.pdf.parser.RenderListener#renderImage(com.itextpdf.text.pdf.parser.ImageRenderInfo)
    '         * @since 5.0.1
    '         

    Public Sub RenderImage(ByVal renderInfo As ImageRenderInfo) Implements IRenderListener.RenderImage
        ' do nothing
    End Sub
End Class

堆栈溢出用户Boris声称已在his answer中解决了同一问题中的小问题。

在另一个问题的背景下,我发现了另一个问题,授予this answer;在这个答案的背景下,我也快速移植到C#,您可以找到该答案的附录。

答案 1 :(得分:0)

嗨代码替换文本工作正常,我已经在c#中翻译了myLocationTextExtractionStrategy类的vb.net中的代码。 就是这样:

private class myLocationTextExtractionStrategy : LocationTextExtractionStrategy
{

  public float UndercontentCharacterSpacing { get; set; }
  public float UndercontentHorizontalScaling { get; set; }
  private SortedList<string, DocumentFont> ThisPdfDocFonts = new SortedList<string, DocumentFont>();
  private List<TextChunk> locationalResult = new List<TextChunk>();

  private bool StartsWithSpace(String inText)
  {
    if (string.IsNullOrEmpty(inText))
    {
      return false;
    }
    if (inText.StartsWith(" "))
    {
      return true;
    }
    return false;
  }

  private bool EndsWithSpace(String inText)
  {
    if (string.IsNullOrEmpty(inText))
    {
      return false;
    }
    if (inText.EndsWith(" "))
    {
      return true;
    }
    return false;
  }

  public override string GetResultantText()
  {
    locationalResult.Sort();

    StringBuilder sb = new StringBuilder();
    TextChunk lastChunk = null;

    foreach (var chunk in locationalResult)
    {
      if (lastChunk == null)
      {
        sb.Append(chunk.text);
      }
      else
      {
        if (chunk.SameLine(lastChunk))
        {
          float dist = chunk.DistanceFromEndOf(lastChunk);
          if (dist < -chunk.charSpaceWidth)
          {
            sb.Append(" ");
          }
          else if (dist > chunk.charSpaceWidth / 2.0F && !StartsWithSpace(chunk.text) && !EndsWithSpace(lastChunk.text))
          {
            sb.Append(" ");
          }
          sb.Append(chunk.text);
        }
        else
        {
          sb.Append("\n");
          sb.Append(chunk.text);
        }
      }
      lastChunk = chunk;
    }
    return sb.ToString();
  }

  public List<iTextSharp.text.Rectangle> GetTextLocations(string pSearchString, System.StringComparison pStrComp)
  {
    List<iTextSharp.text.Rectangle> FoundMatches = new List<iTextSharp.text.Rectangle>();
    StringBuilder sb = new StringBuilder();
    List<TextChunk> ThisLineChunks = new List<TextChunk>();
    bool bStart = false;
    bool bEnd = false;
    TextChunk FirstChunk = null;
    TextChunk LastChunk = null;
    string sTextInUsedChunks = null;

    foreach (var chunk in locationalResult)
    {
      if (ThisLineChunks.Count > 0 && !chunk.SameLine(ThisLineChunks.Last()))
      {
        if (sb.ToString().IndexOf(pSearchString, pStrComp) > -1)
        {
          string sLine = sb.ToString();

          int iCount = 0;
          int lPos = 0;
          lPos = sLine.IndexOf(pSearchString, 0, pStrComp);
          while (lPos > -1)
          {
            iCount++;
            if (lPos + pSearchString.Length > sLine.Length)
            {
              break;
            }
            else
            {
              lPos = lPos + pSearchString.Length;
            }
            lPos = sLine.IndexOf(pSearchString, lPos, pStrComp);
          }

          int curPos = 0;
          for (int i = 1; i <= iCount; i++)
          {
            string sCurrentText;
            int iFromChar;
            int iToChar;

            iFromChar = sLine.IndexOf(pSearchString, curPos, pStrComp);
            curPos = iFromChar;
            iToChar = iFromChar + pSearchString.Length - 1;
            sCurrentText = null;
            sTextInUsedChunks = null;
            FirstChunk = null;
            LastChunk = null;

            foreach (var chk in ThisLineChunks)
            {
              sCurrentText = sCurrentText + chk.text;

              if (!bStart && sCurrentText.Length - 1 >= iFromChar)
              {
                FirstChunk = chk;
                bStart = true;
              }

              if (bStart && !bEnd)
              {
                sTextInUsedChunks = sTextInUsedChunks + chk.text;
              }

              if (!bEnd && sCurrentText.Length - 1 >= iToChar)
              {
                LastChunk = chk;
                bEnd = true;
              }
              if (bStart && bEnd)
              {
                FoundMatches.Add(GetRectangleFromText(FirstChunk, LastChunk, pSearchString, sTextInUsedChunks, iFromChar, iToChar, pStrComp));
                curPos = curPos + pSearchString.Length;
                bStart = false;
                bEnd = false;
                break;
              }
            }
          }
        }
        sb.Clear();
        ThisLineChunks.Clear();
      }
      ThisLineChunks.Add(chunk);
      sb.Append(chunk.text);
    }
    return FoundMatches;
  }

  private iTextSharp.text.Rectangle GetRectangleFromText(TextChunk FirstChunk, TextChunk LastChunk, string pSearchString,
    string sTextinChunks, int iFromChar, int iToChar, System.StringComparison pStrComp)
  {
    float LineRealWidth = LastChunk.PosRight - FirstChunk.PosLeft;

    float LineTextWidth = GetStringWidth(sTextinChunks, LastChunk.curFontSize,
                                                 LastChunk.charSpaceWidth,
                                                 ThisPdfDocFonts.ElementAt(LastChunk.FontIndex).Value);

    float TransformationValue = LineRealWidth / LineTextWidth;

    int iStart = sTextinChunks.IndexOf(pSearchString, pStrComp);

    int iEnd = iStart + pSearchString.Length - 1;

    string sLeft;
    if (iStart == 0)
    {
      sLeft = null;
    }
    else
    {
      sLeft = sTextinChunks.Substring(0, iStart);
    }

    string sRight;
    if (iEnd == sTextinChunks.Length - 1)
    {
      sRight = null;
    }
    else
    {
      sRight = sTextinChunks.Substring(iEnd + 1, sTextinChunks.Length - iEnd - 1);
    }

    float LeftWidth = 0;
    if (iStart > 0)
    {
      LeftWidth = GetStringWidth(sLeft, LastChunk.curFontSize,
                                        LastChunk.charSpaceWidth,
                                        ThisPdfDocFonts.Values.ElementAt(LastChunk.FontIndex));
      LeftWidth = LeftWidth * TransformationValue;
    }

    float RightWidth = 0;
    if (iEnd < sTextinChunks.Length - 1)
    {
      RightWidth = GetStringWidth(sRight, LastChunk.curFontSize,
                                          LastChunk.charSpaceWidth,
                                          ThisPdfDocFonts.Values.ElementAt(LastChunk.FontIndex));
      RightWidth = RightWidth * TransformationValue;
    }

    float LeftOffset = FirstChunk.distParallelStart + LeftWidth;
    float RightOffset = LastChunk.distParallelEnd - RightWidth;
    return new iTextSharp.text.Rectangle(LeftOffset, FirstChunk.PosBottom, RightOffset, FirstChunk.PosTop);
  }


  private float GetStringWidth(string str, float curFontSize, float pSingleSpaceWidth, DocumentFont pFont)
  {

    char[] chars = str.ToCharArray();
    float totalWidth = 0;
    float w = 0;

    foreach (Char c in chars)
    {
      w = pFont.GetWidth(c) / 1000;
      totalWidth += (w * curFontSize + this.UndercontentCharacterSpacing) * this.UndercontentHorizontalScaling / 100;
    }

    return totalWidth;
  }

  public override void RenderText(TextRenderInfo renderInfo)
  {
    LineSegment segment = renderInfo.GetBaseline();
    TextChunk location = new TextChunk(renderInfo.GetText(), segment.GetStartPoint(), segment.GetEndPoint(), renderInfo.GetSingleSpaceWidth());

    location.PosLeft = renderInfo.GetDescentLine().GetStartPoint()[Vector.I1];
    location.PosRight = renderInfo.GetAscentLine().GetEndPoint()[Vector.I1];
    location.PosBottom = renderInfo.GetDescentLine().GetStartPoint()[Vector.I2];
    location.PosTop = renderInfo.GetAscentLine().GetEndPoint()[Vector.I2];
    location.curFontSize = location.PosTop - segment.GetStartPoint()[Vector.I2];

    string StrKey = renderInfo.GetFont().PostscriptFontName + location.curFontSize.ToString();
    if (!ThisPdfDocFonts.ContainsKey(StrKey))
    {
      ThisPdfDocFonts.Add(StrKey, renderInfo.GetFont());
    }
    location.FontIndex = ThisPdfDocFonts.IndexOfKey(StrKey);
    locationalResult.Add(location);
  }


  private class TextChunk : IComparable<TextChunk>
  {
    public string text { get; set; }
    public Vector startLocation { get; set; }
    public Vector endLocation { get; set; }
    public Vector orientationVector { get; set; }
    public int orientationMagnitude { get; set; }
    public int distPerpendicular { get; set; }
    public float distParallelStart { get; set; }
    public float distParallelEnd { get; set; }
    public float charSpaceWidth { get; set; }
    public float PosLeft { get; set; }
    public float PosRight { get; set; }
    public float PosTop { get; set; }
    public float PosBottom { get; set; }
    public float curFontSize { get; set; }
    public int FontIndex { get; set; }


    public TextChunk(string str, Vector startLocation, Vector endLocation, float charSpaceWidth)
    {
      this.text = str;
      this.startLocation = startLocation;
      this.endLocation = endLocation;
      this.charSpaceWidth = charSpaceWidth;

      Vector oVector = endLocation.Subtract(startLocation);
      if (oVector.Length == 0)
      {
        oVector = new Vector(1, 0, 0);
      }
      orientationVector = oVector.Normalize();
      orientationMagnitude = (int)(Math.Truncate(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);
    }

    public bool SameLine(TextChunk a)
    {
      if (orientationMagnitude != a.orientationMagnitude)
      {
        return false;
      }
      if (distPerpendicular != a.distPerpendicular)
      {
        return false;
      }
      return true;
    }

    public float DistanceFromEndOf(TextChunk other)
    {
      float distance = distParallelStart - other.distParallelEnd;
      return distance;
    }

    int IComparable<TextChunk>.CompareTo(TextChunk rhs)
    {
      if (this == rhs)
      {
        return 0;
      }

      int rslt;
      rslt = orientationMagnitude.CompareTo(rhs.orientationMagnitude);
      if (rslt != 0)
      {
        return rslt;
      }

      rslt = distPerpendicular.CompareTo(rhs.distPerpendicular);
      if (rslt != 0)
      {
        return rslt;
      }
      rslt = (distParallelStart < rhs.distParallelStart ? -1 : 1);

      return rslt;
    }
  }

}