Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement support for Popup annotations #6792

Merged
merged 1 commit into from Dec 26, 2015

Conversation

timvandermeij
Copy link
Contributor

Most code for Popup annotations is already present for Text annotations. This patch extracts the popup creation logic from the Text annotation code so it can be reused for Popup annotations.

Not only does this add support for Popup annotations, the Text annotation code is also considerably easier. If a Popup entry is available for a Text annotation, it will not be more than an image. The popup will be handled by the Popup annotation. However, it is also possible for Text annotations to not have a separate Popup annotation, in which case the Text annotation handles the popup creation itself.

@Snuffleupagus
Copy link
Collaborator

Would you be willing to review this patch?

I suppose I can try, but I won't have time until after Christmas.

However, it seems that this PR currently breaks most of the annotations in http://tug.ctan.org/tex-archive/macros/latex/contrib/pdfcomment/doc/pdfcomment.pdf, since nothing happens when they are hovered/clicked.

@timvandermeij
Copy link
Contributor Author

It seems that it is also possible for Text annotations to not have an associated Popup annotation (which is the case in that PDF file), in which case the Text annotation needs to provide the popup and not the Popup annotation. This case can be discovered by checking if the Text annotation has a Popup dictionary element. If so, the Popup annotation takes care of the popup and otherwise the Text annotation should do that. Thanks a lot, Adobe... :(

Issues to address:

  • Solve the ZapfDingbats test failure

    It turned out that the file is somewhat corrupt because the parent of the Popup annotation cannot be fetched; Adobe Acrobat ignores this invalid annotation, we now issue a console warning and ignore it too (see timvandermeij@991e501#diff-18ca06e2bd5be4c9132b51f78388b8f0R756).

  • Fix popup rendering for the tests

    parentItem in src/display/annotation_layer.js was null because in Node.js document is different than in the regular browser case. We now perform the query selector on the actual annotation div, which is also a bit faster as we do not have to traverse the entire document anymore.

  • Handle absence of Popup annotations

    The popup code has been factored out to let both Text and Popup annotations use it.

  • Wrapper width and word wrapping

    The wrapper width is now correct and word wrapping is applied for very long words that do not fit in the container. In the current implementation such a text would overflow the container, but not anymore with this patch.

warn('Popup annotation has an invalid parent annotation.');
return;
}
this.data.parentId = dict.map.Parent.num;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please note that you should not access members of a Dict by directly using map (it should be considered private), use e.g. Dict_get instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main problem with using Dict.get('Parent') is that the ID (num) is not in the returned object anymore (or at least I could not find it). Do you know of a better way of obtaining this (internal) annotation ID other than the method I used? I will happily use that then.

Edit: There is Dict.get('Parent').objId, but its value is 29R (string) instead of the correct 29 (integer). Something like parseInt(Dict.get('Parent').objId, 10); should yield the same result as my method, but I'm not sure if it's better because objId still seems like something private and it's more code. What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably want Dict_getRaw, since it looks like you want to access a Reference. And I don't think that you should use objId when you have access to a reference, since it's also somewhat private, and should probably not be relied on.

But the real question here is: if you have a Ref, why do you directly access just one of its properties? In practice, the gen is usually zero, but you cannot assume that that always holds, so I really think that you ought to use Ref_toString instead!

This leads me to look at the current code in annotation.js, and I thus think that https://github.com/mozilla/pdf.js/blob/master/src/core/annotation.js#L163 is actually wrong! That line really ought to read this.data.id = params.ref.toString();, which means that changing the current line to this.data.parentId = dict.getRaw('Parent').toString(); should then work just fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the new commit. Really nice find!

}

var parentItem = dict.get('Parent');
if (!parentItem) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to have two different cases for when the Parent entry isn't present, or is invalid?
Would it not suffice to just replace both of them with e.g.

...
var dict = parameters.dict;
var parentDict = dict.get('Parent');
if (!parentDict) {
  warn(...);
  return;
}
this.data.parentId = ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the new commit. Much easier indeed.

// Attach the event listeners and position the container. A parent
// element is only available when its type is supported.
var selector = '[data-annotation-id="' + this.data.parentId + '"]';
var parentItem = this.layer.querySelectorAll(selector)[0];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is querySelectorAll(...)[0] really necessary, couldn't you simply use querySelector instead, since those should be equivalent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the new commit.

@timvandermeij timvandermeij force-pushed the popup-annotation branch 3 times, most recently from 7a681ed to 9994138 Compare December 24, 2015 19:16
}

if (dict.has('C')) {
data.hasBgColor = true;
this.data.popup = !!dict.get('Popup');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this just contains a boolean, how about naming it data.hasPopup instead?
Also, wouldn't dict.has('Popup) work here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a linked test-case for this particular code-path (or if it's not too much trouble, create a reduced one)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dict.has('Popup') is indeed working just fine. Not sure why I did not use that in the first place...

I'll try to create a reduced test case, but otherwise I will certainly add a linked test case.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably still need this.data.hasPopup though, since you've got code in annotation_layer.js that seem to depend on that flag being present :)

But I really like the general direction this PR is going!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. Fixed in the new commit. I have managed to create a reduced test case, which is now also in the commit.

title: this.data.title,
contents: this.data.contents
};
var popupElement = new PopupElement(parameters);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I'm concerned, these kind of temporary variables doesn't really add much in terms of readability. You could just do:

var popupElement = new PopupElement({
  parentContainer: this.container,
  parentTrigger: image,
  color: this.data.color,
  title: this.data.title,
  contents: this.data.contents
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the new commit, also for the other place where I did this for this commit. Looks better that way.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just trying to find small things to make the total diff smaller ;)

@timvandermeij timvandermeij force-pushed the popup-annotation branch 8 times, most recently from 8897031 to c3ee03e Compare December 24, 2015 23:00
this.data.color = null;
}

this.data.popup = dict.has('Popup');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, but I'd still like this to be named this.data.hasPopup, given that it's a boolean.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the new commit. I had missed this before, so thanks for reminding me!

@timvandermeij timvandermeij force-pushed the popup-annotation branch 2 times, most recently from 7a9282d to a231e26 Compare December 25, 2015 12:16
Most code for Popup annotations is already present for Text annotations.
This patch extracts the popup creation logic from the Text annotation
code so it can be reused for Popup annotations.

Not only does this add support for Popup annotations, the Text
annotation code is also considerably easier. If a `Popup` entry is
available for a Text annotation, it will not be more than an image. The
popup will be handled by the Popup annotation. However, it is also
possible for Text annotations to not have a separate Popup annotation,
in which case the Text annotation handles the popup creation itself.
@timvandermeij
Copy link
Contributor Author

I forgot to mention how you can test the reference image rendering locally, though you might already know this. First make a backup of your current reference images, then simply remove the test/ref and test/tmp directories, clear the test manifest to only leave the two at https://github.com/mozilla/pdf.js/pull/6792/files#diff-b19c886c212474d3b37d6026a795a246R2619 and run node make botmakeref. The results can be inspected in test/ref.

Thank you for the helpful comments so far and merry Christmas! :-)

@timvandermeij
Copy link
Contributor Author

/botio-linux preview

@pdfjsbot
Copy link

From: Bot.io (Linux)


Received

Command cmd_preview from @timvandermeij received. Current queue size: 0

Live output at: http://107.21.233.14:8877/8d36eb7e61e5580/output.txt

@timvandermeij
Copy link
Contributor Author

/botio test

@pdfjsbot
Copy link

From: Bot.io (Linux)


Received

Command cmd_test from @timvandermeij received. Current queue size: 0

Live output at: http://107.21.233.14:8877/3edc7c48302c2c8/output.txt

@pdfjsbot
Copy link

From: Bot.io (Windows)


Received

Command cmd_test from @timvandermeij received. Current queue size: 0

Live output at: http://107.22.172.223:8877/0bad30f904f75a8/output.txt

@pdfjsbot
Copy link

From: Bot.io (Windows)


Success

Full output at http://107.22.172.223:8877/0bad30f904f75a8/output.txt

Total script time: 20.52 mins

  • Font tests: Passed
  • Unit tests: Passed
  • Regression tests: Passed

@pdfjsbot
Copy link

From: Bot.io (Linux)


Success

Full output at http://107.21.233.14:8877/3edc7c48302c2c8/output.txt

Total script time: 20.61 mins

  • Font tests: Passed
  • Unit tests: Passed
  • Regression tests: Passed

@Snuffleupagus
Copy link
Collaborator

Thank you for the patch!

/botio makeref

@pdfjsbot
Copy link

From: Bot.io (Windows)


Received

Command cmd_makeref from @Snuffleupagus received. Current queue size: 0

Live output at: http://107.22.172.223:8877/fc4dfd01b429a35/output.txt

@pdfjsbot
Copy link

From: Bot.io (Linux)


Received

Command cmd_makeref from @Snuffleupagus received. Current queue size: 0

Live output at: http://107.21.233.14:8877/1f892d746193e3e/output.txt

Snuffleupagus added a commit that referenced this pull request Dec 26, 2015
Implement support for Popup annotations
@Snuffleupagus Snuffleupagus merged commit cba8a87 into mozilla:master Dec 26, 2015
@pdfjsbot
Copy link

From: Bot.io (Windows)


Success

Full output at http://107.22.172.223:8877/fc4dfd01b429a35/output.txt

Total script time: 20.18 mins

  • Lint: Passed
  • Make references: Passed
  • Check references: Passed

@pdfjsbot
Copy link

From: Bot.io (Linux)


Success

Full output at http://107.21.233.14:8877/1f892d746193e3e/output.txt

Total script time: 20.51 mins

  • Lint: Passed
  • Make references: Passed
  • Check references: Passed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants