I' ve been searching the Internet for 2 Weeks and found some interesting solutions for my Problem, but nothing seems to give me the answer.
My goal is to do the folowing:
I want to find a Text in a static PDF-File and replace this text with another text. I would like to keep the design of the content. Is it really that hard?
I found a way but I lost the whole information:
using (PdfReader reader = new PdfReader(path))
{
StringBuilder text = new StringBuilder();
for (int i = 1; i <= reader.NumberOfPages; i++)
{
text.Append(PdfTextExtractor.GetTextFromPage(reader, i));
text.Replace(txt_SuchenNach.Text, txt_ErsetzenMit.Text);
}
return text.ToString();
}
The second try I had was way better, but needs fields where I can change the text inside:
string fileNameExisting =path;
string fileNameNew = @"C:\TEST.pdf";
using (FileStream existingFileStream = new FileStream(fileNameExisting, FileMode.Open))
using (FileStream newFileStream = new FileStream(fileNameNew, FileMode.Create))
{
// PDF öffnen
PdfReader pdfReader = new PdfReader(existingFileStream);
PdfStamper stamper = new PdfStamper(pdfReader, newFileStream);
var form = stamper.AcroFields;
var fieldKeys = form.Fields.Keys;
foreach (string fieldKey in fieldKeys)
{
var value = pdfReader.AcroFields.GetField(fieldKey);
form.SetField(fieldKey, value.Replace(txt_SuchenNach.Text, txt_ErsetzenMit.Text));
}
// Textfeld unbearbeitbar machen (sieht aus wie normaler text)
stamper.FormFlattening = true;
stamper.Close();
pdfReader.Close();
}
This keeps the formatation of the rest of text and does only change my searched text. I need a solution for text which is NOT in a Textfield.
thanks for all your answers and your help.
The general issue is that text objects may use embedded fonts with specific glyphs assigned to specific letters. I.e. if you have a text object with some text like "abcdef" then the embedded font may contain glyphs for these ("abcdef" letters) only but not for other letters. So if you replace "abcdef" with "xyz" then the PDF will not display these "xyz" as no glyphs are available for these letters to be displayed.
So I would consider the following workflow:
I have worked on the same requirement and I am able to achieve this by the following steps.
Step1: Locating Source Pdf File and Destination file Path
Step2: Read Source Pdf file and Searching for the location of string that we want to replace
Step3: Replacing the string with new one.
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
using PDFExtraction;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace PDFReplaceTextUsingItextSharp
{
public partial class ExtractPdf : System.Web.UI.Page
{
static iTextSharp.text.pdf.PdfStamper stamper = null;
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Replace_Click(object sender, EventArgs e)
{
string ReplacingVariable = txtReplace.Text;
string sourceFile = "Source File Path";
string descFile = "Destination File Path";
PdfReader pReader = new PdfReader(sourceFile);
stamper = new iTextSharp.text.pdf.PdfStamper(pReader, new System.IO.FileStream(descFile, System.IO.FileMode.Create));
PDFTextGetter("ExistingVariableinPDF", ReplacingVariable , StringComparison.CurrentCultureIgnoreCase, sourceFile, descFile);
stamper.Close();
pReader.Close();
}
/// <summary>
/// This method is used to search for the location words in pdf and update it with the words given from replacingText variable
/// </summary>
/// <param name="pSearch">Searchable String</param>
/// <param name="replacingText">Replacing String</param>
/// <param name="SC">Case Ignorance</param>
/// <param name="SourceFile">Path of the source file</param>
/// <param name="DestinationFile">Path of the destination file</param>
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);
//Send some data contained in PdfContentByte, looks like the first is always cero for me and the second 100,
//but i'm not sure if this could change in some cases
strategy.UndercontentCharacterSpacing = (int)cb.CharacterSpacing;
strategy.UndercontentHorizontalScaling = (int)cb.HorizontalScaling;
//It's not really needed to get the text back, but we have to call this line ALWAYS,
//because it triggers the process that will get all chunks from PDF into our strategy Object
string currentText = PdfTextExtractor.GetTextFromPage(pReader, page, strategy);
//The real getter process starts in the following line
List<iTextSharp.text.Rectangle> MatchesFound = strategy.GetTextLocations(pSearch, SC);
//Set the fill color of the shapes, I don't use a border because it would make the rect bigger
//but maybe using a thin border could be a solution if you see the currect rect is not big enough to cover all the text it should cover
cb.SetColorFill(BaseColor.WHITE);
//MatchesFound contains all text with locations, so do whatever you want with it, this highlights them using PINK color:
foreach (iTextSharp.text.Rectangle rect in MatchesFound)
{
//width
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)
{
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With