Question:
With PDFBox, how can I create a link annotation with "mouse over" color effect (aka rollover / mouse hover)?
It means that when I hover my mouse cursor over a link in a PDF file (without clicking it), the link changes to a different color. And if I mouse the cursor away, the link changes backs to the original color.
For example:
The effect that I am looking for is similar to the links at stackoverflow website. When you hover the mouse cursor over (without clicking) the "Ask Question" button, the link changes from grey to orange. When you move the cursor away, the color changes back to grey. See following picture for example: I want to achieve exactly the same effect in a PDF file.
What I have tried:
In PDF Reference Sixth Edition, it is described that:
the rollover appearance is used when the user moves the cursor into the annotation’s active area without pressing the mouse button"
and
[rollover appearance] are defined in an appearance dictionary, which in turn is the value of the AP entry in the annotation dictionary
Also,
In the PDFBox, there is a PDAppearanceDictionary
class, which has a setRolloverAppearance()
method.
This is the farthest I can get. I don't know how to use PDAppearanceDictionary
class (if this is indeed the right class to use) in conjunction with a PDAnnotationLink
class, in order to achieve my desired result.
I have tried finding examples on Google in vain.
In short
There was some uncertainty about whether or not such a rollover effect is possible. Using fairly current Adobe PDF viewers (Reader XI and Acrobat 9.5) for displaying, the desired rollover effect did not turn up for a link annotation. The effect did turn up, though, for a button widget (carrying the same URL action).
In detail
The test code feeds either a PDAnnotationLink
or a PDAnnotationWidget
customized as a pushbutton to a method which embeds the respective annotation in a document and adds normal and rollover appearances to it:
void createRollover(PDAnnotation annotation, String filename) throws IOException, COSVisitorException
{
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
List<PDAnnotation> annotations = page.getAnnotations();
float x = 100;
float y = 500;
String text = "PDFBox";
PDFont font = PDType1Font.HELVETICA_BOLD;
float textWidth = font.getStringWidth(text) / 1000 * 18;
PDPageContentStream contents = new PDPageContentStream(document, page);
contents.beginText();
contents.setFont(font, 18);
contents.moveTextPositionByAmount(x, y);
contents.drawString(text);
contents.endText();
contents.close();
PDAppearanceDictionary appearanceDictionary = new PDAppearanceDictionary();
PDAppearanceStream normal = createAppearanceStream(document, textWidth, font, "0.5 0.5 0.5 rg");
PDAppearanceStream rollover = createAppearanceStream(document, textWidth, font, "1 0.7 0.5 rg");
PDAppearanceStream down = createAppearanceStream(document, textWidth, font, "0 0 0 rg");
appearanceDictionary.setNormalAppearance(normal);
appearanceDictionary.setRolloverAppearance(rollover);
appearanceDictionary.setDownAppearance(down);
annotation.setAppearance(appearanceDictionary);
PDRectangle position = new PDRectangle();
position.setLowerLeftX(x);
position.setLowerLeftY(y - 5);
position.setUpperRightX(x + textWidth);
position.setUpperRightY(y + 20);
annotation.setRectangle(position);
annotations.add(annotation);
document.save(new File(RESULT_FOLDER, filename));
document.close();
}
In case of the PDAnnotationLink
:
In case of the pushbutton PDAnnotationWidget
:
Backgrounds:
The OP in his question and @Tilman in a comment referred to the PDF specification:
An annotation may define as many as three separate appearances: [...]
• The rollover appearance shall be used when the user moves the cursor into the annotation’s active area without pressing the mouse button. [...]
The normal, rollover, and down appearances shall be defined in an appearance dictionary, which in turn is the value of the AP entry in the annotation dictionary
and, therefore, thought:
So it should be possible
They did not consider, though, that the specification introduces the appearance dictionary as:
AP dictionary (Optional; PDF 1.2) An appearance dictionary specifying how the annotation shall be presented visually on the page (see 12.5.5, “Appearance Streams”). Individual annotation handlers may ignore this entry and provide their own appearances.
Thus, what at first glance seemed to be an unconditional requirement ("The rollover appearance shall be used when...") turns out to be ignorable if the annotation handler in a PDF viewer has its own ideas.
tl;dr: it is completely up to the PDF viewer in question to decide which appearance streams it uses and which it ignores and replaces in its own ways.
If making use of annotation appearance streams, one should always make sure that one also supplies the information most plausibly used if the given appearances are ignored, e.g. having regular page content beneath a link annotation.
It's important to understand that a "link" annotation in PDF simply represents a selectable area. It's a rectangle that may or may not have text under it, and is not tied to any specific text in any way ("hyperlinked" text just happens to be in the linked zone of the document). Acrobat and Reader have some "extra" features to "guess" at which text is used in links, and mark used links a different color, but from a PDF perspective a link is just a rectangle. You can give the link annotation itself a rollover effect, this has the effect of changing the appearance of the link rectangle itself. Examples include having the previously invisible rectangular outline appear when you mouseover, or having a visible rectangular outline change color. You can play around with these in Acrobat's link properties menu to get a better understanding.
However, that is the only type of rollover you will be able to achieve using link annotations. To reproduce what happens with web links, you will want to look into other workarounds. Examples include creating an Xobject form of the text with an alternate rollover appearance, creating the text as a image-based button with a rollover appearance, or even using Flash. I hope this helps explain what is and isn't possible with link annotations themselves!