Clicking on highlighted word should draw a box annotation around it

PDF.js Express Version

Detailed description of issue
My script highlights a search word on the loaded PDF (see code snippet below). Is it possible to make highlighted words clickable and attach functionalities to it? See below for the expected behavior.

Expected behaviour
My desired functionality is that the user clicks on any highlighted word and a box annotation should appear around that clicked word. Please feel free to recommend any other way to make this happen for the user. I am open to using actual highlight annotations also instead of the search-highlight if that makes it possible to get this functionality.

Code snippet
We are using a script similar to the one below to add highlights to search strings (this was recommended by Logan):

WebViewer(…).then(instance => {
const {docViewer} = instance;

docViewer.on('documentLoaded', () => {
   docViewer.setSearchHighlightColors({
     searchResult: new Annotations.Color(0, 0, 255, 0.5),
     activeSearchResult: 'rgba(0, 255, 0, 0.5)'
   });

   instance.searchText('your text here')
})

})

Hey there!

Sorry for the delay, this one took me a little while to figure out.

To get this functionality you need to use our more advanced search API, since the searchText API just creates non-selectable highlights.

Here is the code I ended up writing to accomplish this use case:

Webviewer({
  ...options
}, document.getElementById('viewer')).then(instance => {

  const { docViewer, CoreControls, Annotations, annotManager } = instance;

  docViewer.on('documentLoaded', () => {
    const searchText = 'YOUR_SEARCH_TEXT';
    const mode = CoreControls.Search.Mode.PAGE_STOP | CoreControls.Search.Mode.HIGHLIGHT;
    const searchOptions = {
      fullSearch: true, // search full document
      onResult: result => {
        if (result.resultCode === CoreControls.Search.ResultCode.FOUND) {
          const textQuad = result.quads[0].getPoints();
          const annot = new Annotations.TextHighlightAnnotation();

          annot.X = textQuad.x1;
          annot.Width = textQuad.x2 - textQuad.x1;

          annot.Y = textQuad.y3;
          annot.Height = textQuad.y1 - textQuad.y3;

          annot.FillColor = new Annotations.Color(0, 0, 255, 0.5);
          annot.StrokeColor = new Annotations.Color(0, 0, 255, 0.5);

          annot.Quads = [textQuad];

          // add custom property so we can check in annotationSelected function
          annot.IsCustomSearch = true;

          annotManager.addAnnotation(annot);
          annotManager.drawAnnotationsFromList([annot])
        }
      }
    };

    // start the search
    docViewer.textSearchInit(searchText, mode, searchOptions);
  });

  // Listen for when our search annots are selected
  annotManager.on('annotationSelected', (annots) => {
    if (!annots) return;
    annots.forEach(annot => {

      // check that our custom 'IsCustomSearch' property is set
      if (annot instanceof Annotations.TextHighlightAnnotation && annot.IsCustomSearch) {
        const { X, Y, Width, Height } = annot;
        
        // Here you can use X, Y, Width, Height 
        // to create a new Rectangle Annotation! 
 
      }

    })
  })
});

What I am doing here is using the textSearchInit function, which gives us actual positions of the search results in the callback function.

Using those positions, I create highlight annotations. On each of these highlight annots, I add a custom property called “IsCustomSearch” (this could be named whatever you want). I do this so that in our annotationSelected handler, we can check if one of our custom highlight annots was selected.

In the annotationSelected handler, I check if the selected annotation is one of our custom highlights. If it is, I can use the position of that annotation to create a new rectangle annotation as per your spec.

Here are some APIs that might be useful for you as well.

  • Rectangle Annotation
  • Get all annotations on doc - This might be useful if you want to delete all your custom highlights at some point. You can loop over these, get the ones with our custom IsCustomSearch property, and delete them
  • Delete annotatation This will be useful for deleting the highlight and replacing it with the new rectangle annotation. Will also be useful for the previous point.

I hope this helps! Let me know if there are any more questions.

Thanks,
Logan

Hi Logan, you are a life saver!! Thanks so much for putting effort into this. I’ll talk to my team about this solution and will update here on our results soon.

Have a nice day,
Best,
Pratyush

1 Like