How to edit text in a PDF using Foxit PDF SDK 5.3?
Editing text in your PDFs is a quite tricky functionality offered by Foxit PDF SDK. Please note that the editing is different than filling and changing form field values.
This article will show you how to find a text object inside your PDF and replace the entire text object with a different text. We are updating the pdf_search sample with the editing functionality to achieve that.
IMPORTANT: Please note that the editing output won’t display correctly if you are using an Embedded Subset font. You need to use a fully embedded font so that the renderer recognizes all the characters in the text object.
First, let’s take a look at the sample PDF (edit_test.pdf):
This is the function for searching inside a PDF as you would find in your pdf_search sample:
//Do the search process in a PDF page.
void Search(string pdfFile, string inputFilesPath, string searchText)
{
CAutoDocument pdfDoc((inputFilesPath + "/" + pdfFile).c_str(), "rb");
if (!pdfDoc.GetFileHander())
return;
if (FSCRT_ERRCODE_SUCCESS != pdfDoc.LoadPDFDocument(NULL))
return;
FS_RESULT ret = FSCRT_ERRCODE_ERROR;
FS_INT32 pageCount = pdfDoc.GetPagesCount();
for (int i = 0; i < pageCount; i++)
{
CAutoPDFPage pdfPage;
ret = pdfPage.LoadPDFPage(pdfDoc.GetDocument(), i);
if (FSCRT_ERRCODE_SUCCESS != ret)
return;
ret = pdfPage.ParsePage();
if (FSCRT_ERRCODE_SUCCESS != ret)
return;
ret = pdfPage.LoadTextPage();
if (FSCRT_ERRCODE_SUCCESS != ret)
return;
//Set searchPattern
FSCRT_BSTR searchPattern;
//Initialize const string to searchPattern.
FSCRT_BStr_Init(searchPattern);
FSCRT_BStr_Set(searchPattern, (char*)searchText.c_str(), (FS_DWORD)searchText.length());
FSDK_OutputLog("Start to search \"%s\":\r\n", searchText.c_str());
CAutoTextSearch textSearch(pdfPage.GetPDFTextPage(), searchPattern, 0, 0);
if (!textSearch.m_textSearch)
{
FSCRT_BStr_Clear(searchPattern);
return;
}
//Find next matched pattern, direction from page top to down.
//Here, the first match will be found if exists and searching process starts from here.
FS_BOOL isMatch = FALSE;
textSearch.FindNext(isMatch);
FS_INT32 iMatchCount = 0;//Used to record match count.
while (isMatch)
{
//If find one match, get its character range and rectangle of this match in PDF space.
iMatchCount++;
FSDK_OutputLog("Match time: %d\r\n", iMatchCount);
//Get the text selection handle.
CAutoTextSelection textSelection(textSearch.m_textSearch);
if (!textSelection.m_textSelection)
{
FSCRT_BStr_Clear(searchPattern);
return;
}
After that point, you need to add the code to get the text object in the page:
// get pageobjects
FSCRT_PAGE page = pdfPage.GetPDFPage();
FSPDF_PAGEOBJECTS pageObjs;
FS_RESULT ret = FSPDF_Page_GetPageObjects(page, pageObjs);
The search function will go on, count the pieces of the text box and get the text box rectangle coordinates. It will loop through all the text objects in the page:
//Count pieces within text selection.
FS_INT32 iPieceCount = 0;
textSelection.CountPieces(iPieceCount);
//Get some information one piece by one piece.
for (FS_INT32 i=0; i<iPieceCount; i++)
{
FS_INT32 iStartChar = 0;
FS_INT32 iCountChar = 0;
FSCRT_RECTF selRect;
//Get char range of specific piece: index of start character and cout of characters in a piece.
textSelection.GetPieceCharRange(i, iStartChar, iCountChar);
//Get rectangle of specific piece.
textSelection.GetPieceRect(i, selRect);
FSDK_OutputLog("\tStart character index: %d\r\n\tRectangle(in PDF space):\r\n\tLeft:%.2f Top:%.2f Right:%.2f Bottom:%.2f\r\n", iStartChar, selRect.left, selRect.top, selRect.right, selRect.bottom);
Now, within the loop, the program will create a new page object and get the text object with the coordinates defined by the GetPieceRect function selRect parameters:
FSPDF_PAGEOBJECT pageObj;
ret = FSPDF_PageObjects_GetObjectAtPos(page, pageObjs, FSPDF_PAGEOBJECT_TEXT, selRect.left,selRect.top, 10, pageObj);
After the page object was found, the program will declare the Byte string variable unicodeStr, call the InitConstString and SetUnicodeString functions to set the new String value (“New Text”) to the text in the sample PDF and replace it:
if(ret == FSCRT_ERRCODE_SUCCESS)
{
FSCRT_BSTR unicodeStr;
FSCRT_BStr_InitConstString(unicodeStr, "New Text");
FSPDF_TextObject_SetUnicodeString(page, pageObj, unicodeStr);
}
}
FSPDF_PageObjects_GenerateContents(page, pageObjs);
//Find the next match. If no match, this loop will be finished.
textSearch.FindNext(isMatch);
}
Finally, go to the main method at the end of the sample and set the word you want to search in your text by adding it to the searchText parameter inside the Search function. Our program will search for the word “Foxit” inside the text object:
//Get path of input file folder for this demo.
string inputFilesPath = FSDK_GetInputFilesFolder("pdfsearch");
void* folder = FSDK_OpenFolder(inputFilesPath.c_str(), FALSE);
if (folder)
{
FSDK_OutputLog("\r\nEnumerate files in folder input_files/pdfsearch: ");
BrowerFolder(folder, inputFilesPath, outputFilesPath, "Foxit");
//Close access to specific folder.
FSDK_CloseFolder(folder);
}
else
{
string inputFilesPath = FSDK_GetFixFolder();
string pdffile = "edit_test.pdf";
Search(pdffile, inputFilesPath, "Foxit");
}
if (gsdkLogFile)
FSCRT_File_Release(gsdkLogFile);
//Finalize PDF module.
FSDK_PDFModule_Finalize();
//Finalize SDK library.
FSDK_FinalizeLibrary();
//Close log file.
FSDK_CloseLog();
return 0;
}
Run the program and this is how the sample PDF will look:
This is the entire code for the Search function and the main method:
//Do the search process in a PDF page.
void Search(string pdfFile, string inputFilesPath, string searchText)
{
CAutoDocument pdfDoc((inputFilesPath + "/" + pdfFile).c_str(), "rb");
if (!pdfDoc.GetFileHander())
return;
if (FSCRT_ERRCODE_SUCCESS != pdfDoc.LoadPDFDocument(NULL))
return;
FS_RESULT ret = FSCRT_ERRCODE_ERROR;
FS_INT32 pageCount = pdfDoc.GetPagesCount();
for (int i = 0; i < pageCount; i++)
{
CAutoPDFPage pdfPage;
ret = pdfPage.LoadPDFPage(pdfDoc.GetDocument(), i);
if (FSCRT_ERRCODE_SUCCESS != ret)
return;
ret = pdfPage.ParsePage();
if (FSCRT_ERRCODE_SUCCESS != ret)
return;
ret = pdfPage.LoadTextPage();
if (FSCRT_ERRCODE_SUCCESS != ret)
return;
//Set searchPattern
FSCRT_BSTR searchPattern;
//Initialize const string to searchPattern.
FSCRT_BStr_Init(searchPattern);
FSCRT_BStr_Set(searchPattern, (char*)searchText.c_str(), (FS_DWORD)searchText.length());
FSDK_OutputLog("Start to search \"%s\":\r\n", searchText.c_str());
CAutoTextSearch textSearch(pdfPage.GetPDFTextPage(), searchPattern, 0, 0);
if (!textSearch.m_textSearch)
{
FSCRT_BStr_Clear(searchPattern);
return;
}
//Find next matched pattern, direction from page top to down.
//Here, the first match will be found if exists and searching process starts from here.
FS_BOOL isMatch = FALSE;
textSearch.FindNext(isMatch);
FS_INT32 iMatchCount = 0;//Used to record match count.
while (isMatch)
{
//If find one match, get its character range and rectangle of this match in PDF space.
iMatchCount++;
FSDK_OutputLog("Match time: %d\r\n", iMatchCount);
//Get the text selection handle.
CAutoTextSelection textSelection(textSearch.m_textSearch);
if (!textSelection.m_textSelection)
{
FSCRT_BStr_Clear(searchPattern);
return;
}
// get pageobjects
FSCRT_PAGE page = pdfPage.GetPDFPage();
FSPDF_PAGEOBJECTS pageObjs;
FS_RESULT ret = FSPDF_Page_GetPageObjects(page, pageObjs);
//Count pieces within text selection.
FS_INT32 iPieceCount = 0;
textSelection.CountPieces(iPieceCount);
//Get some information one piece by one piece.
for (FS_INT32 i=0; i<iPieceCount; i++)
{
FS_INT32 iStartChar = 0;
FS_INT32 iCountChar = 0;
FSCRT_RECTF selRect;
//Get char range of specific piece: index of start character and cout of characters in a piece.
textSelection.GetPieceCharRange(i, iStartChar, iCountChar);
//Get rectangle of specific piece.
textSelection.GetPieceRect(i, selRect);
FSDK_OutputLog("\tStart character index: %d\r\n\tRectangle(in PDF space):\r\n\tLeft:%.2f Top:%.2f Right:%.2f Bottom:%.2f\r\n", iStartChar, selRect.left, selRect.top, selRect.right, selRect.bottom);
FSPDF_PAGEOBJECT pageObj;
ret = FSPDF_PageObjects_GetObjectAtPos(page, pageObjs, FSPDF_PAGEOBJECT_TEXT, selRect.left,selRect.top, 10, pageObj);
if(ret == FSCRT_ERRCODE_SUCCESS)
{
FSCRT_BSTR unicodeStr;
FSCRT_BStr_InitConstString(unicodeStr, "New Text");
FSPDF_TextObject_SetUnicodeString(page, pageObj, unicodeStr);
}
}
FSPDF_PageObjects_GenerateContents(page, pageObjs);
//Find the next match. If no match, this loop will be finished.
textSearch.FindNext(isMatch);
}
if (0 == iMatchCount)
FSDK_OutputLog("\tNo match pattern.\r\n");
else
FSDK_OutputLog("All match count: %d\r\n\r\n", iMatchCount);
FSCRT_BStr_Clear(searchPattern);
}
string outputFile = inputFilesPath + "/out2.pdf";
FSDK_SavePDFFile(pdfDoc.GetDocument(), outputFile.c_str());
FSDK_OutputLog("Succeed to search.\r\n");
}
void BrowerFolder(void* folder, string inputFilesPath, string outputFilesPath, string searchText)
{
do
{
FS_BOOL isDir = FALSE;
string strFile = FSDK_GetNextFile(folder, isDir);
if(!isDir strFile.size() > 0 && FSDK_CompareExtName(strFile.c_str(), ".pdf"))
{
Search(strFile, inputFilesPath, searchText);
}
if (strFile.size() < 1) break;
} while (1);
}
int main(int argc, char* argv[])
{
FSDK_SetExecuteFilePath(argc, argv);
string logPath = FSDK_GetLogFile("pdfsearch");
//Open log file.
FSDK_OpenLog(logPath.c_str());
FSDK_OutputLog("Foxit PDF SDK example: pdfsearch\r\n");
FSDK_OutputLog("\tInput from: input_files folder\r\n");
FSDK_OutputLog("\tOutput to: output_files/pdfsearch\r\n\r\n");
string outputFilesPath = FSDK_GetOutputFilesFolder("pdfsearch");
//Initialize SDK library, or no method of SDK can be used.
bool bRet = FSDK_InitializeLibray(true);
if (!bRet)
{
FSDK_CloseLog();
return -1;
}
//Initialize PDF module, or no method of PDF module can be used.
if(!FSDK_PDFModule_Initialize()){
FSDK_FinalizeLibrary();
FSDK_CloseLog();
return -1;
}
FSCRT_FILE gsdkLogFile = NULL;
bRet = FSDK_SetLibraryLogFile(outputFilesPath + "/gsdkLog.txt", gsdkLogFile);
if (!bRet)
{
FSDK_PDFModule_Finalize();
FSDK_FinalizeLibrary();
FSDK_CloseLog();
return -1;
}
//Get path of input file folder for this demo.
string inputFilesPath = FSDK_GetInputFilesFolder("pdfsearch");
void* folder = FSDK_OpenFolder(inputFilesPath.c_str(), FALSE);
if (folder)
{
FSDK_OutputLog("\r\nEnumerate files in folder input_files/pdfsearch: ");
BrowerFolder(folder, inputFilesPath, outputFilesPath, "Foxit");
//Close access to specific folder.
FSDK_CloseFolder(folder);
}
else
{
string inputFilesPath = FSDK_GetFixFolder();
string pdffile = "edit_test.pdf";
Search(pdffile, inputFilesPath, "Foxit");
}
if (gsdkLogFile)
FSCRT_File_Release(gsdkLogFile);
//Finalize PDF module.
FSDK_PDFModule_Finalize();
//Finalize SDK library.
FSDK_FinalizeLibrary();
//Close log file.
FSDK_CloseLog();
return 0;
}
Note: This article refers to a deprecated version of a Foxit Product. If you are still using Foxit PDF SDK 5.3 or older, please refer to your download package documents for Developer Guide and API Reference.
Get a trial version of the new Foxit PDF SDK and see our latest generation SDK’s brand new features!
Updated on October 4, 2017