Question about authorId when making annotations

Which product are you using?
PDF.js Express Plus

PDF.js Express Version
8.1.0

Detailed description of issue
We are preparing to setup a real-time collaboration for annotations. However I am a bit confused because I don’t see how or where you would set a users id into an annotation. I only see annotationUser as a setting on WebViewer but this is how you set the authors name. In your example Real-time PDF collaboration I see a reference to an author id being used in the server annotation.authorId = data.val().authorId; but this isn’t the same thing as setting an authorId right when the webviewer loads.

Code snippet
Your example shows this:

async function onAnnotationCreated(data) {
    // Import the annotation based on xfdf command
    const [annotation] = await annotationManager.importAnnotCommand(data.val().xfdf);
    // Set a custom field authorId to be used in client-side permission check
    annotation.authorId = data.val().authorId;
    annotationManager.redrawAnnotation(annotation);
 }

Guess I am confused because why can’t I simply set an author id directly on the webviewer like this?

WebViewer({
    path: '...'
    annotationUser: 'Andy H.',
    authorId: 'idNumber'
})

Am I needing to manually add an authorId field into the XML for the annotation?

Please let me know thanks!

Hello, I’m Ron, an automated tech support bot :robot:

While you wait for one of our customer support representatives to get back to you, please check out some of these documentation pages:

Guides:APIs:Forums:

Also I noticed the real-time example doesn’t discuss how to differentiate imported annotations. Example would be me and another person had a discussion which was stored in the database. I return a week later to import these annotations, I then don’t have any way of knowing which ones are mine other than the author name, which we would not want to use since someone could change their name in the future.

What would be your solution for this?

Hey!

You can set the current user by calling setCurrentUser. You can provide any string here so you could use their username, user id, etc. If you use the user Id, you can use setAnnotationDisplayAuthorMap to make sure their username appears in the viewer when creating annotations.

Thanks,
Logan

Hey Logan, okay I understand what you mean here. But how does this reference loading in saved annotations from multiple users?

Hey there,

I’m not sure what you mean by that, can you elaborate?

Yes, so if I open a document and import 20 saved annotation from 3 different authors, how do I determine which annotations are mine so that I may still edit them? When annotations are stored they only keep the author name in the xml, so when they are returned it is the only reference we have to use.

Here’s an example of a returned annotation with my own title

<underline color="#E44234" name="ff397c23-2b0c-2f00-8284-f76aadd89878" title="Andy Herman" subject="Underline"><contents>underlined text</contents></underline>

Here’s one with another users title

<underline color="#E44234" name="ff397c23-2b0c-2f00-8284-f76aadd89878" title="Eric Greggors" subject="Underline"><contents>underlined text</contents></underline>

So when these are returned and I need to determine which annotations are mine, I can only reference the titles title="Andy Herman" which is fine for a simple setup, but if I ever changed my own name to Andy Begman, then I wouldn’t be able to mark these annotation as mine to be able to edit them later.

Let me know if this clears it up.

Hey,

If everything is set up correctly, the exported annotation title should be the user ID and not the username. You can test using this code:

  // Here you should set the user ID, not the user name
  instance.Core.annotationManager.setCurrentUser('abcde');

  // This function maps a user id to a user name for display purposes
  instance.Core.annotationManager.setAnnotationDisplayAuthorMap((id) => {
    if(id === 'abcde') {
      return "Logan"
    }
  })

  instance.Core.annotationManager.addEventListener('annotationChanged', async (annots, action) => {
    // Every time an annotation is added, export it and console log it for debugging purposes
    if(action === 'add') {
      const xfdf = await instance.Core.annotationManager.exportAnnotations({ annotList: annots })
      console.log(xfdf)
    }
  })

You’ll see that the “title” property is now the user ID (in our case “abcde”) and not the user name

<?xml version="1.0" encoding="UTF-8" ?><xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve"><annots><square page="0" rect="219.590,500.250,501.030,667.260" color="#E44234" flags="print" name="f346b08b-f7b0-e731-325a-c6ceb9bca7e3" title="abcde" subject="Rectangle" date="D:20211216143453-08'00'" creationdate="D:20211216143453-08'00'"/></annots></xfdf>

Thanks,
Logan

Okay got it, I see what you mean.

Would this mean that I would need to keep track of all possible users to also set their names?

Example:

instance.Core.annotationManager.setAnnotationDisplayAuthorMap((id) => {
    if(id === 'abcde') {
      return "Logan"
    } else if (id === 'efght') {
      return "John"
    }
  })

We have hundreds of possible users so this would get a bit hairy.

Can I request to have authorId as an optional field added into annotations?

<highlight title="Andy Herman" authorId="2332323" ...><contents>text</contents></highlight>
WebViewer({
      path: '', // path to the PDF.js Express'lib' folder on your server
      annotationUser: "Andy Herman",
      annotationUserId: "2332323",

Hey there and happy new year!

Sorry for the delay here, most the team was on vacation for the holidays.

We cannot add additional attributes to XFDF items because that is against the PDF specification.

I’m talking with the team today to see how they recommend approaching this. The idea with setAnnotationDisplayAuthorMap is that you use it to look up the username based on the ID passed. A very basic example would be something like this:

  instance.Core.annotationManager.setAnnotationDisplayAuthorMap((id) => {
    return getUserNameFromId(id)
  })

where getUserNameFromId makes a call to your database or something to get the user name.

However, it seems that this API was poorly designed and does not allow asynchronous actions which means that is not possible.

I’ll get back to you today and let you know the best approach for this.

Logan

Thanks, we found a work around so no need to further help.

1 Like