Friday, September 25, 2015

qpdfview automatic highlight

I needed to highlight few pdf files. In windows there are many tools. In ubuntu, there are tools but they require huge downloads. But qpdfview is a good alternative with relatively small download size. It highlights based on rectangle rather than text. Problem is, you have to go to highlight mode (menu click or ctrl + A or hold down ctrl), highlight rectangle, deal with tool tip and then highlight appears. This is painful process if there are many highlights to be done on same page. So let's deal with these issues.

1. Get source code:
Go to the folder where source should be downloaded and
$apt-get source qpdfview

2. Get build dependencies:
$sudo apt-get build-dep qpdfview

3. Compile package:
Go inside source code and
$dpkg-buildpackage -uc -b 

Now we can just use make to build with changes.

All source code is in single sources folder with .cpp and .h files only.

Now, where should we start? I feel interface is always a good place to search required code. So when we go to highlight mode and  mark a rect, a menu appears with option to 'Add text' and 'Add highlight'. Note underscores on t and h, which means 'Ampersand' before t and h. So let's hit search

$grep -in '&highlight' *

gives just one result in pageitem.cpp. Let's open it.

Code here seems fairly easy to understand. 2 menu items are created, a menu is shown and based on user click corresponding function is called. Now, we don't want menu to be shown, so let's comment out menu.exec line and set action to be addHighLightAction. This should hide menu and directly go to highlight mode. Build and test confirms this behaviour. Task one finished.


Now, instead of holding ctrl always or pressing ctrl + A for each highlight, we would like it to be default mode. How can we accomplish this? Based on interface action, menu entry in edit -> add annotation seems good choice. Notice A has underscore, so let's search it

$grep -in '&add' *

Gives two results, one for annotation and one for bookmark. Annotation part is in mainwindow.cpp. Given line adds menu entry to variable m_addAnnotationModeAction, without call back function mentioned. Searching this addAnnotationModeAction on same file gives few menu ordering codes and one snippet where something called rubberBandMode is checked. Now this could be the highlight mode related code. We need to find how this rubberBandMode changes, and set it by default to AddAnnotationMode. Let's see if this rubberBandMode gives us anything.

$grep -in rubberBandMode *

This gives far too many results. Instead of scrolling through them, it may be useful to check at what point does this mode gets set when we press ctrl and start dragging. Since our addAnnotation function does not have anything to do with it, let's check parent of this function. A grep to check its parents gives too many results, so we can use cscope to narrow it down. So in cscope 6 lines for symbol addAnnotation are given, one of them in pageitem.cpp file itself. Let's check this line.

It is in mouse release function, but does not have anything useful relating to rubberBandMode. But wait, in geany, there is a function mousePressEvent just like mouseReleaseEvent. Let's see if there is anything useful here.
Oh yes, first part of this function deals exclusively with rubber band modes, we can also see this mode being set to annotation mode in some conditions. Since we want annotation mode to be default one, it should be set without any modifier key too. So lets add an else to this inner conditional block and set mode to annotation mode in it.

i.e.
        else
        {
            m_rubberBandMode = AddAnnotationMode;
        }


Build and check if it works, and indeed it finishes task 2.

So, now we can directly click and drag a rect and it shows a bulky text box and afterwards highlights it. This text box is worst of the distractions though and took me quite some time to figure out how to hide it.

Since it is shown immediately after button release, it would be natural to  think that mouseReleaseEvent or addAnnotation function should have code regarding this. Sadly nothing relevant is visible in mouseReleaseEvent. But in addAnnotation function a fuction showAnnotationOverlay is called, maybe this has something to do with this text box. In this function commenting out lines selectively leads to segmentation fault. So this is not the correct approach.

Let's look at functionality of text box. Text entered here is shown as tool tip to highlight when mouse cursor is put on it. This event is popularly called hover event. Thankfully pageitem.cpp has 3 functions regarding hover event, two of which are empty. In hoverMoveEvent, we can see a code block for handling annotations. In showText call, it takes annotation->contents(). This must be the text set by text box. Let's search where this text is being set.


Now, a look over class/struct/function names would indicate this is part of QT library. Searching for annotation class in QT reference would lead us to poppler library reference. Here two functions are mentioned regarding content of annotation: contents() and setContents(). Obviously our target now is setContents() calls. Grep gives us only one exact match in annotationwidget.cpp. Here it is inside a fuction called AnnotationWidget::on_textChanged. So AnnotationWidget must be the text box. Now in same file, geany shows a function for key press event for same widget. We know that pressing escape on this box makes it go away, so let's see if there any relevant code.

Well it calles hideOnEscape function, which might be the one we want. Inside this function, widget->hide() is the call to hide it. So now we know the code to make the box disappear. Question is, where to put this code? Where is the box created? Well we know that box is an QWidget so let's search for it in pageitem.cpp. Sadly it does not yield any useful results. But addAnnotation calls addHighlightAnnotation in pdfmodel.cpp and here QWidget search leads to createWidget function which must create text box in first place. Now let's add widget->hide() just before return and see if it works, and surprisingly that is a working solution.

This makes the highlight process pretty fast, although actual hacking required a week of tedious work.


No comments:

Post a Comment