Annotation Replies and Reviews

Annotation replies are a feature of PDF documents, and they enable users to have written discussions directly inside a document. As of PSPDFKit 7.5 for iOS, PSPDFKit provides convenient APIs for accessing replies in a document, as well as user interface components for viewing and editing replies.

Annotations may also have author-specific states associated with them, which allows users to review a proposed change to, for example, say whether they agree or disagree with the change.

PSPDFKit has built-in support to let users view, search, and add replies and reviews. There is also a model-level API for programmatic access so that you can build custom features. PSPDFKit implements annotation replies and state conforming to the PDF specification and is fully compatible with the same features in Adobe Acrobat Reader.

Terminology

This is the terminology we use:

  • Reply — an annotation with an in-reply-to annotation set.
  • Text reply — a reply where the author-specific state is unspecified.
  • State reply or state annotation — a reply where the author-specific state is specified.
  • Review — a state reply where the author-specific state is in the review state model (category).
  • Comments — the list containing an annotation and all of its text replies. This means the first comment displays the contents of the parent annotation. This is mostly a user-facing term.

Licensing

Annotation replies are a separate component in your PSPDFKit license. Without this feature included in your license, your app will not be able to view, search, or add annotation replies. Please contact our sales team to add this feature to your license.

Third-Party Compatibility

Replies were introduced in the PDF 1.6 specification and are fully compatible with Adobe Acrobat. Other PDF viewers might not be able to show all replies or simply fail to display status information.

Screenshot of comments in Adobe Acrobat

User Interface

Users can tap on an annotation, then tap Comments in the menu. This will present a PSPDFNoteAnnotationViewController, which shows all the comments on the annotation, as well as any reviews on those comments. Users can both add and edit the comments and add reviews.

Screenshot of comments

Just like with other annotations, users can edit comments even if they did not create them.

If there are replies to replies, PSPDFKit will flatten the nested conversation into a single list sorted by date.

Disabling the Replies and Reviews UI

If your license includes annotation replies, but you want to disable letting the user view and add replies and reviews, you can define a document features source and disable the replies and reviews feature:

Copy
1
2
3
4
5
6
7
8
class MyFeaturesSource: NSObject, PSPDFDocumentFeaturesSource {

    var features: PSPDFDocumentFeatures?

    var canShowAnnotationReplies: Bool {
        return false
    }
}
Copy
1
2
3
4
5
6
7
8
@interface MyFeaturesSource : NSObject <PSPDFDocumentFeaturesSource>
@end
@implementation MyFeaturesSource
@synthesize features = _features;
- (BOOL)canShowAnnotationReplies {
    return NO;
}
@end

Add your features source to the document:

1
2
let document: PSPDFDocument = ...
document.features.add([MyFeaturesSource()])
1
2
PSPDFDocument *document = ...
[document.features addSources:@[[[MyFeaturesSource alloc] init]]];

Model API

Replies to an annotation are themselves annotations. A reply is linked to its parent by means of the inReplyToAnnotation property. In the PDF, this is modeled with the IRT entry in the annotation dictionary. There is no direct connection in the reverse direction, i.e. between an annotation and its replies. Instead, all the annotations on the same page must be searched. However, if you use the user interface provided by PSPDFKit, this is all taken care of.

The author-specific state associated with an annotation is not specified in the annotation itself, but rather in a separate text annotation that refers to the original annotation by means of the inReplyToAnnotation property. Each time a user changes the state, a new note annotation is created and appended to the linked list of state changes. To find the complete set of states associated with an annotation, it is necessary to repeatedly search all the annotations on the same page in order to follow the reverse of the inReplyToAnnotation property. Again, this is already handled in PSPDFKit’s user interface.

Since replies are annotations, they are included when querying the annotations on a page. You can check if an annotation is a reply using the isReply property. Doing this does not require the replies feature in the license.

If the license feature is enabled, you can both read and set the annotation a reply replies to using the inReplyToAnnotation property. To add a text reply:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
guard let document = originalAnnotation.document else {
    return
}

let textReply = PSPDFNoteAnnotation(contents: "text of the comment")

// Replies must be on the same page as their inReplyToAnnotation.
// For multi-provider documents, PSPDFKit will fix up the page index when the annotation is added to the document.
textReply.pageIndex = originalAnnotation.absolutePageIndex

// originalAnnotation must have already been added to the document.
textReply.inReplyTo = originalAnnotation

document.add([textReply], options: [.userCreated(true)])
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (originalAnnotation.document == nil) {
    return
}

PSPDFNoteAnnotation *textReply = [[PSPDFNoteAnnotation alloc] initWithContents:@"text of the comment"];

// Replies must be on the same page as their inReplyToAnnotation.
// For multi-provider documents, PSPDFKit will fix up the page index when the annotation is added to the document.
textReply.pageIndex = originalAnnotation.absolutePageIndex;

// originalAnnotation must have already been added to the document.
textReply.inReplyToAnnotation = originalAnnotation;

[originalAnnotation.document addAnnotations:@[textReply] options:@{ PSPDFAnnotationOptionUserCreatedKey: @YES }];

You can read and set the author-specific state with the authorStateModel and authorState properties on PSPDFNoteAnnotation. If these properties are PSPDFAnnotationAuthorStateModelUnspecified and PSPDFAnnotationAuthorStateUnspecified, then the annotation is not a state reply. These properties should be kept consistent. To add a state reply:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
guard let document = originalAnnotation.document else {
    return
}

let stateReply = PSPDFNoteAnnotation()

stateReply.authorStateModel = .review
stateReply.authorState = .accepted

// Replies must be on the same page as their inReplyToAnnotation.
// For multi-provider documents, PSPDFKit will fix up the page index when the annotation is added to the document.
stateReply.pageIndex = originalAnnotation.absolutePageIndex

// originalAnnotation must have already been added to the document.
stateReply.inReplyTo = originalAnnotation

document.add([stateReply], options: [.userCreated(true)])
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (originalAnnotation.document == nil) {
    return
}

PSPDFNoteAnnotation *stateReply = [[PSPDFNoteAnnotation alloc] init];

stateReply.authorStateModel = PSPDFAnnotationAuthorStateModelReview;
stateReply.authorState = PSPDFAnnotationAuthorStateAccepted;

// Replies must be on the same page as their inReplyToAnnotation.
// For multi-provider documents, PSPDFKit will fix up the page index when the annotation is added to the document.
stateReply.pageIndex = originalAnnotation.absolutePageIndex;

// originalAnnotation must have already been added to the document.
stateReply.inReplyToAnnotation = originalAnnotation;

[originalAnnotation.document addAnnotations:@[stateReply] options:@{ PSPDFAnnotationOptionUserCreatedKey: @YES }];