Skip to main content
Known Participant
April 12, 2017
Question

Selection is extended with PDTextSelectGetBoundingRect

  • April 12, 2017
  • 9 replies
  • 1465 views

Hello,

I am trying to create highlight annotation for the selected text over multiple line.

For example, see in the below image:

This is my selected text. Now when I apply the annotation to this text, it looks like below:

That means selection is getting extended while applying the annotation.

Here is my code:

I have used

ACCB1 void ACCB2 createRequirement(char *sectionName) {

AVDoc avDoc = AVAppGetActiveDoc();

if (avDoc == NULL) {

  AVAlertNote("Please open the document first.");

  return;

}

PDDoc pdDoc = AVDocGetPDDoc(avDoc);

ASFixedRect boundingRect;

PDPage page = NULL;

PDAnnot annot, hilightAnnot;

LPCWSTR secName = convertCharArrayToLPCWSTR(sectionName);

LPCWSTR arg = L"value";

DWORD size = 1024;

LPCWSTR filePath = convertCharArrayToLPCWSTR(iniFilePath);

PDTextSelect pdtext =static_cast<PDTextSelect>(AVDocGetSelection(avDoc));

char reqId[2048];

char ident[2048];

int numPages = PDDocGetNumPages(pdDoc);

//Check if text is not selected

if (pdtext == NULL) {

  AVAlertNote("Please select the text first.");

  if (sectionName != NULL) {

  WritePrivateProfileString(_T(sectionName), _T("selectedText"), _T(""), _T(iniFilePath));

  }

  return;

}

if (sectionName != NULL) {

  //Create requirement from Reqtify

  GetPrivateProfileString(sectionName, "value", NULL, reqId, size, iniFilePath);

  strcpy(ident,reqId);

}

else {

  //Input box to create requirement manually

  createInputDialogBox(0, NULL, L"Requirement creation", L"Requirement ID:", L"REQ");

  if (TheText[0] == 0) {

  return;

  }

  wstring reqIdent(TheText); 

  string str(reqIdent.begin(), reqIdent.end()); 

  strcpy(ident, str.c_str());

}

//Get selected text

AVDocCopySelection(avDoc);

wchar_t* selectedText = getClipboardText();

if (selectedText == NULL) {

  AVAlertNote("Could not copy to clipboard.");

  return;

}

// Get the bounding box for the selection

PDTextSelectGetBoundingRect(pdtext, &boundingRect);

//Create a PDPage object

AVPageView currentPageView = AVDocGetPageView(avDoc);

ASInt32 pageNum = AVPageViewGetPageNum(currentPageView);

page = PDDocAcquirePage(pdDoc, pageNum);

PDColorValueRec yellow;

yellow.space = PDDeviceRGB;

yellow.value[0] = ASInt32ToFixed(1);

yellow.value[1] = ASInt32ToFixed(1);

yellow.value[2] = ASInt32ToFixed(0);

//Use the bbox to create a highight annotation QuadPoints

CosObj ArrayObj, RecObj;

CosDoc cd = PDDocGetCosDoc(pdDoc);

CosObj cosPage = PDPageGetCosObj(page);

ArrayObj = CosNewArray(cd, false, 8);

CosArrayPut(ArrayObj, 0, CosNewFixed(cd, false, boundingRect.right));

CosArrayPut(ArrayObj, 1, CosNewFixed(cd, false, boundingRect.bottom));

CosArrayPut(ArrayObj, 2, CosNewFixed(cd, false, boundingRect.left));

CosArrayPut(ArrayObj, 3, CosNewFixed(cd, false, boundingRect.bottom));

CosArrayPut(ArrayObj, 4, CosNewFixed(cd, false, boundingRect.right));

CosArrayPut(ArrayObj, 5, CosNewFixed(cd, false, boundingRect.top));

CosArrayPut(ArrayObj, 6, CosNewFixed(cd, false, boundingRect.left));

CosArrayPut(ArrayObj, 7, CosNewFixed(cd, false, boundingRect.top));

// for the Rect. Highlight annotations don't require, but API call to create annotation requires this key

RecObj = CosNewArray(cd, false, 4);

CosArrayPut(RecObj, 0, CosNewFixed(cd, false, boundingRect.left));

CosArrayPut(RecObj, 1, CosNewFixed(cd, false, boundingRect.right));

CosArrayPut(RecObj, 2, CosNewFixed(cd, false, boundingRect.bottom));

CosArrayPut(RecObj, 3, CosNewFixed(cd, false, boundingRect.top));

CosObj cosDict = CosNewDict(cd, true, 4);

CosDictPutKeyString(cosDict, "Subtype", CosNewNameFromString(cd, false, "Highlight"));

CosDictPutKeyString(cosDict, "QuadPoints", ArrayObj);

CosDictPutKeyString(cosDict, "Rect", RecObj);

annot = PDAnnotFromCosObj(cosDict);

hilightAnnot = CastToPDTextAnnot(annot);

//Open the annotation, set the text, and add it to a page

PDTextAnnotSetOpen(hilightAnnot, true);

PDTextAnnotSetContents(hilightAnnot, ident, strlen(ident));

//Fix 197842 starts

//Adds logged in Username as a Annotation title

char username[UNLEN + 1];

DWORD username_len = UNLEN + 1;

GetUserName(username, &username_len);

PDAnnotSetTitle(hilightAnnot, username, strlen(username));

//Fix 197842 ends

PDPageAddAnnot(page, -2, annot);

PDPageNotifyContentsDidChange(page);

PDAnnotSetColor(annot, &yellow);

AVPageViewDrawNow(currentPageView);

if (ASAtomFromString("Highlight") == PDAnnotGetSubtype(annot) && sectionName != NULL)

{

  std::wstring  text;

  std::wstring str;

  std::wstringstream stringStream;

  stringStream << selectedText;

  while (stringStream >> text)

  {

  str.append(text);

  str.append(L" ");

  }

  trim(str);

  LPCWSTR secName = convertCharArrayToLPCWSTR(sectionName);

  LPCWSTR arg = L"selectedText";

  LPCWSTR reqText = str.c_str();

  LPCWSTR filePath = convertCharArrayToLPCWSTR(iniFilePath);

  WritePrivateProfileStringW(secName, arg, reqText, filePath);

}

PDPageRelease(page);

}

I have used PDTextSelect to select the text and PDTextSelectGetBoundingRect to get the bounding rect of selected text.

Can you please tell me what is missing or wrong in this code?

Thank you in advance!!

This topic has been closed for replies.

9 replies

Legend
April 20, 2017

Oh, that isn't what I said. I did NOT say to make a list of quads per line.

You need to make ONE annotation. If the selection has three lines it needs THREE sets of quads. Your difficult task is to work out the coordinates of the three quads you must make, from the many quads from each word.

Participating Frequently
June 27, 2023

@kundan24352562 do you have example code to PDTextSelectEnumQuads? 

Legend
April 20, 2017

What set of quads is used in the example? It is time for detailed analysis of what you hope to copy.

Legend
April 20, 2017

You must write code that analyses the quads (one per word), and derives a new list of quads (one per line). Your first step is to look in detail at the input, so you can start to derive an algorithm, to make your output.

Known Participant
April 20, 2017

But even if I make a list of quads for per line and make annotation, then when that annotation is selected, selection border will appear for each line. I want the border as I mentioned below image:

Legend
April 20, 2017

Ah, this was not what I expected but I understand it. The selection gives you a quad for each word. But when you make a highlight in Acrobat I believe it stores a quad for each line.

You should examine existing annotations made by Acrobat to confirm this. (By examine, I mean look at PDF internals).

It it will be up to you to transform this information. Print out the selection quads and look for a pattern you can match. Do they have the same baseline? Or do you need to use more complex fuzzy logic? Interesting problem. 

Known Participant
April 20, 2017

>>Print out the selection quads and look for a pattern you can match. Do they have the same baseline?

I did understand this statement. How to print out the selection quads and which matching pattern do you mean?

Legend
April 19, 2017

Ah, I see the problem. You are not creating an array of 8 x "n" numbers. Rather you are creating an array of "n" arrays. If your two quads were [a b c d e f g h] and [p q r s t u v w], you create [ [a b c d e f g h]  [p q r s t u v w] ] but what is needed is [ a b c d e f g h p q r s t u v w ], just 16 numbers in 1 array.

Known Participant
April 20, 2017

Hello,

Thank you for this additional information.

Now I am able to create annotation. But the problem is when I select the annotation, selection border does not seem good.

Here is the snapshot when I select my highlighted annotation:

And here is my latest code:

ArrayObj = CosNewArray(cd, false, 8*quadCount);

RecObj = CosNewArray(cd, false, 4);

for (int i = 0; i < quadCount; i++) {

  boundingRect = rects;

  CosArrayPut(ArrayObj, index++, CosNewFixed(cd, false, boundingRect.right));

  CosArrayPut(ArrayObj, index++, CosNewFixed(cd, false, boundingRect.bottom));

  CosArrayPut(ArrayObj, index++, CosNewFixed(cd, false, boundingRect.left));

  CosArrayPut(ArrayObj, index++, CosNewFixed(cd, false, boundingRect.bottom));

  CosArrayPut(ArrayObj, index++, CosNewFixed(cd, false, boundingRect.right));

  CosArrayPut(ArrayObj, index++, CosNewFixed(cd, false, boundingRect.top));

  CosArrayPut(ArrayObj, index++, CosNewFixed(cd, false, boundingRect.left));

  CosArrayPut(ArrayObj, index++, CosNewFixed(cd, false, boundingRect.top));

}

ASFixedRect annotLoc = rects[quadCount-1];

CosArrayPut(RecObj, 0, CosNewFixed(cd, false, annotLoc.left));

CosArrayPut(RecObj, 1, CosNewFixed(cd, false, annotLoc.right));

CosArrayPut(RecObj, 2, CosNewFixed(cd, false, annotLoc.bottom));

CosArrayPut(RecObj, 3, CosNewFixed(cd, false, annotLoc.top));

CosObj cosDict = CosNewDict(cd, true, 2);

CosDictPutKeyString(cosDict, "Subtype", CosNewNameFromString(cd, false, "Highlight"));

CosDictPutKeyString(cosDict, "QuadPoints", ArrayObj);

CosDictPutKeyString(cosDict, "Rect", RecObj);

annot = PDAnnotFromCosObj(cosDict);

Selection border should look like below:

What am I missing or doing wrong here?

Thanks.

Legend
April 19, 2017

Did you read the specification of QuadPoints in the PDF Reference?

Known Participant
April 19, 2017

Hello,

Yes, I read the PDF reference.

According to them, I created 8 X n array of quads where n is the number of quads.

Here is my code:

arr = CosNewArray(cd, false, quadCount);

for (int i = 0; i < quadCount; i++) {

  ArrayObj = CosNewArray(cd, false, 8);

  boundingRect = rects;

  CosArrayPut(ArrayObj, 0, CosNewFixed(cd, false, boundingRect.right));

  CosArrayPut(ArrayObj, 1, CosNewFixed(cd, false, boundingRect.bottom));

  CosArrayPut(ArrayObj, 2, CosNewFixed(cd, false, boundingRect.left));

  CosArrayPut(ArrayObj, 3, CosNewFixed(cd, false, boundingRect.bottom));

  CosArrayPut(ArrayObj, 4, CosNewFixed(cd, false, boundingRect.right));

  CosArrayPut(ArrayObj, 5, CosNewFixed(cd, false, boundingRect.top));

  CosArrayPut(ArrayObj, 6, CosNewFixed(cd, false, boundingRect.left));

  CosArrayPut(ArrayObj, 7, CosNewFixed(cd, false, boundingRect.top));

  CosArrayPut(arr, i, ArrayObj);

}

CosObj cosDict = CosNewDict(cd, true, 4);

CosDictPutKeyString(cosDict, "Subtype", CosNewNameFromString(cd, false, "Highlight"));

CosDictPutKeyString(cosDict, "QuadPoints", arr);

annot = PDAnnotFromCosObj(cosDict);

But at line 28, I am receiving "Invalid annotation object" error.

Please help.

Legend
April 19, 2017

It sounds as if you are making a new annotation for each quad that is enumerated. No. Collect all of the quads and make ONE annotation with all of the quads. No other methods are needed.

Known Participant
April 19, 2017

OK. But how to put those quads in Cos object? And to create annotation we need a Cos Dictionary with three keys as below:

  1. CosDictPutKeyString(cosDict, "Subtype", CosNewNameFromString(cd, false, "Highlight")); 
  2. CosDictPutKeyString(cosDict, "QuadPoints", ArrayObj); 
  3. CosDictPutKeyString(cosDict, "Rect", RecObj); 

How can I map those quad with my code?

Legend
April 13, 2017

I believe there is a method to list or enumerate the quads for a PDTextSelect.

Known Participant
April 19, 2017

Hello,

As you said, I used PDTextSelectEnumQuads method to enumerate the quads and apply the annotation.

But this method returns quads to every word inside the selection text and hence, highlight annotation is getting applied to every word.

So now there are multiple annotations to selected text whereas my intention is to have only a single annotation.

How to merge these annotations into single one.

Any example or method names will be helpful.

Thanks.

Legend
April 12, 2017

You make only one quad point, the enclosing rectangle. The highlight correctly shows this. There is no magic to adjust the highlight to actual text. Normally there is a quad per selected line.

Known Participant
April 13, 2017

Okay. So, how can I get the quad for per selected line?