LAST REVISED: 06/11/03 20:51
ANNOTATION SERVER GUIDE
VERSION 1.0

An Annotation Server is a server-side HTTP application responsible for the storage and retrieval of a document's annotations. When a client-side Viewer loads a document associated with an Annotation Server, it will query the Server for the annotations to display. If the user subsequently edits and saves the annotations, the Viewer will send the edited annotations back to the Annotation Server. By implementing an Annotation Server, web sites can provide a shared annotation mechanism, whereby the annotations of one user can be immediately reviewed by other users. This document describes the API of the Annotation Server.

The specification described in this document is fully supported by Version 6.0 and higher of Cartesian's CPC Lite, CPC View pi, and CPC View ax client-side Viewers. Web sites that conform to this specification should interoperate with these client-side Viewers without problems.

In the documentation below, advanced topics are described in this smaller font size.


1. The Server API

The Annotation Server has an extremely simple API. It provides only two methods: one to load the annotations from the Server; and one to save the annotations back to the Server. The annotations are represented by an ASCII XML description, known as an Annotation Document, specified later in this document.

The complexity of the Annotation Server is entirely up to the implementor. At its simplest, the Annotation Server need do no more than store and retrieve the annotations from some persistent storage medium, such as a database or flat files. If the Annotation Server is expected to support concurrent editing of annotations, it would also need to provide some form of update concurrency control. A more robust Annotation Server might also provide features such as access control and selective annotation display (e.g., show me only Mary Smith's annotations or show me only annotations created in the last three days).


1.1. The Server URL

An Annotation Server is specified by providing a single URL, identifying the HTTP address of the Server. This URL is used for all requests from the Viewer to the Server.

To associate an Annotation Server with a document, set the document's annServer URL parameter to the URL of the Annotation Server. The Server URL can be either absolute or relative. If a relative URL is specified, it is resolved against the base URL of the document.

For example, the URL:

http://foo.com/doc.cpc?annServer=as.cgi/docId=123/userId=rene
specifies that the document at http://foo.com/doc.cpc should be displayed using the Annotation Server at http://foo.com/as.cgi/docId=123/userId=rene.

When an Annotation Server is specified, any local annotations that the user had associated with the document will not be displayed.

The specified Annotation Server URL is not augmented in any way by the Viewer. In particular, the Viewer does not add any information regarding the document associated with the annotations. It is expected that the Annotation Server URL already contains any such information that is required by the Server.

For the purposes of authentication, the Annotation Server URL might also encode the user's session ID or similar information.


1.2. Loading Annotations

When the Viewer loads a document with an associated Annotation Server, it issues an HTTP GET request to the Annotation Server URL. The Server should respond with the XML description of the document's annotations. In addition, the response header should specify a status of no-error (Status: 200).

If the document has no associated annotations, the Server should still respond with a valid (but empty) annotation XML description. If the Server fails to respond with a valid annotation XML description, an error will be presented to the user.


1.3. Storing Annotations

When the Viewer wants to save the annotations associated with a document, it issues an HTTP POST request to the Annotation Server URL. The XML description for the Annotation Document is sent as the data for the POST. The Server should save the annotations and respond with a response header specifying a status of no-error (Status: 200). Any other response status should be considered an error by the Viewer.

If the Server's response contains an <error> tag, the save operation was unsuccessful and the Viewer should notify the user of the error. If the error tag contains data, the data is an error message that should be displayed to the user by the Viewer. For example, the response:

<error>
A disk error occurred
</error>
means that the save operation failed and the Viewer should display the error message "A disk error occurred".

Any other data returned by the Server is ignored by the Viewer.


2. XML Description

A annotation document is described by an XML file. Currently, there is no DTD. To satisfy the well-formedness requirements for DTD-less XML, the entire meta-document should be enclosed in a <annotations> block:
<?xml version="1.0" ?>
<annotations>
    list of annotation pages
</annotations>


2.1. Empty Document

A simple Annotation Server has no need to understand either the syntax or the semantics of the XML Annotation Document. The Server merely stores the Annotation Document sent by the Viewer (via a PUT request), and returns the saved document when the Viewer requests it (via a GET request). In this situation, the Server does not need to interpret the Annotation Document in any way.

The only exception to this comes in the original creation of the document. The API requires the Server to always return a valid Annotation Document in response to a GET request. Hence, if there are no annotations for a particular document, the Server should return an empty Annotation Document. The following document will suffice:

<?xml version="1.0" ?>
<annotations/>


2.2. Pages

Each page in the Annotation Document is described by a <page> block. The contents of the block are a list of the annotations on the page. The page tag has a single required attribute: the page number of the page (num). Pages are numbered from one.
<page num="1">
    list of annotations
</page>

The individual pages within the annotations block do not need to occur in page order. The num attribute of the page tag should be used by the Viewer to determine the appropriate image page that the annotation page applies to.


2.3. Annotations

Each annotation is described by a separate XML block. The tag used to introduce the block identifies the class of the annotation. Annotation classes are described in the CPC View Programmer's Guide. The following table specifies the mapping of XML tags to annotation classes:

TagAnnotation Class
<Overlay>Box
<CircleOverlay>Oval
<LineOverlay>Line
<FreehandLineOverlay>FreehandLine
<TextOverlay>Text

2.3.1. Parameters

In addition to the annotation class, there are several annotation parameters that are used to affect the appearence and behavior of the annotation. The various annotation parameters are described in sections 7.4 through 7.10 of the CPC View Programmer's Guide. Each of these parameters can be specified using either XML tag attributes of the annotation tag or embedded tagged content within the annotation block. The two methods are equivalent (although certain types of data may be more naturally encoded using one or the other of the two methods).

For example, the text to display in a text annotation is controlled by the drawText parameter. This can be specified in either of two ways:

<TextOverlay drawText="Hello, world" />
or
<TextOverlay>
    <drawText>Hello, world</drawText>
</TextOverlay>
For the purposes of this example, annotation parameters other than drawText have been omitted.

2.3.2. Examples

The following XML fragment describes a circle annotation:
<CircleOverlay
  paperRect="149,2564,805,520"
  paperTransparent="1"
  createName="rene"
  createTime="1020567051"
  drawColor="ff6464"
  edgeColor="000000"
/>

The following XML fragment describes a text annotation:

<TextOverlay
  paperRect="134,494,200,1305"
  paperColor="ffff64"
  paperBorder="Button"
  createName="rene"
  createTime="1022295707"
  drawColor="ff0000"
  fontSize="36"
  fontFace="Arial"
  paperAutoSize="1"
>

    <drawText>Hello, world</drawText>

</TextOverlay>


2.4. Example

The following XML fragment describes a complete Annotation Document containing two annotated pages. The first page contains a line annotation and a text annotation. The second page contains a circle annotation.
<?xml version="1.0" ?>
<annotations>

<page num="1">
    <LineOverlay
      paperRect="179,2039,2380,100"
      createName="pmark"
      createTime="1020566515"
      drawColor="c00000"
      drawSize="4"
      lineType="diagnw"
    />

    <TextOverlay
      paperRect="134,494,200,1305"
      paperColor="ffff64"
      paperBorder="Button"
      createName="rene"
      createTime="1022295707"
      drawColor="ff0000"
      fontSize="36"
      fontFace="Arial"
      paperAutoSize="1"
      drawText="Hello, world"
    />

</page>

<page num="2">
    <CircleOverlay
      paperRect="149,2564,805,520"
      paperTransparent="1"
      createName="rene"
      createTime="1020567051"
      drawColor="ff6464"
      edgeColor="000000"
    />
</page>

</annotations>


3. Sample Server

This section contains a PERL implementation of a simple Annotation Server.

This section has been generated directly from the PERL source code found in the file annServer.cgi.



3.1. Annotation Documents

The simple Annotation Server stores Annotation Documents in flat files.

3.1.1. Root Directory

$AnnRoot is the name of the root directory in which the files are stored. This directory must be writable by the user that the web server runs under.

$AnnRoot = "/tmp/Annotations";

3.1.2. Document ID

Each document will be identified by a Document ID. For the purposes of this sample, the Document ID is simply a unique string.

Function: adFileName($id)
Return the filename of the Annotation Document with the Document ID $id.

sub adFileName { my ($id) = @_;
Remove unsafe characters from the ID
 $id =~ s/[^a-zA-Z0-9\.,_]/_/g;
Map it to a file in the annotation directory
 qq{$AnnRoot/$id.data};
}

3.1.3. Loading

To load an Annotation Document, we simply map the ID to a file and load the contents of the file.

Function: adLoad($id)
Return the annotation data for Document ID $id.

sub adLoad { my ($id) = @_;
If the file can not be opened, return an empty annotation document.
 if(!open(FILE, adFileName($id))) { 
  return qq{<?xml version="1.0" ?><annotations/>}; 
 }
Read the file in binary mode under a non-exclusive lock
 binmode(FILE);
 flock(FILE, 1);
 my $data = join('', <FILE>);
 flock(FILE, 8);
Close the file and return the data
 close(FILE);
 $data;
}

3.1.4. Storing

To store an Annotation Document, we simply map the ID to a file and save the data to the file.

Function: adStore($id, $xml)
Save $xml as the annotation data for the Document ID $id. Returns 1 if the operation is successful; otherwise undef.

sub adStore { my ($id, $xml) = @_;
If the file can not be opened for writing, return an error.
 if(!open(FILE, ">".adFileName($id))) { return undef; }
Write the file in binary mode under an exclusive lock
 binmode(FILE);
 flock(FILE, 2);
 print FILE $xml;
 flock(FILE, 8);
Close the file and return no error
 close(FILE);
 1;
}


3.2. CGI Interface

The following utilities are used to interface with the CGI host.

3.2.1. Parsing CGI Parameters

Per the CGI specification, the request URL parameters are supplied in $ENV{QUERY_STRING}.

Function: cgiParse()
Parse the CGI parameters and store the name-value pairs into the CGI hash.

sub cgiParse {
The name/value pairs are separated by '&'.
 foreach(split(/&/, $ENV{QUERY_STRING})) {
Convert plus's to spaces
  s/\+/ /g; 
Split into name and value. The two fields are separated by '='.
  my ($key, $val) = split(/=/,$_,2); # splits on the first =.
  
Convert hex escapes (%XX) to alphanumeric
  $key =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
  $val =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
 
Associate key and value
  $CGI{$key} = $val;
 }
}

3.2.2. Generating a Response

Function: cgiRespond($code, $mime, $text)
Generate the HTTP response to a request. The response specifies a status code of $code. The response data has a mime type of $mime. $text is the data to send with the response.

sub cgiRespond { my ($code, $mime, $text) = @_; 
 my $len = length($text);
 print qq{Content-Type: $mime
Content-Length: $len
Status: $code
$text
};
}


3.3. The Server

The server merely parses the CGI parameters and reads or writes the specified annotations, depending on whether the request is a GET or a POST.


The Document ID is specified as the ID CGI parameter. Parse the CGI parameters into the CGI hash
cgiParse();
If they did not specify an ID, return an error
if(!$CGI{ID}) { 
 cgiRespond(400, "text/plain", 
  "<error>There was no document specified</error>");
}
If this is not a POST, load the XML data
elsif($ENV{REQUEST_METHOD} ne "POST") { 
 cgiRespond(200, "text/xml", adLoad($CGI{ID}));
}
This is a POST operation. Store the XML data
else {
Get the data to store
 my $xml; 
 read(STDIN, $xml, $ENV{'CONTENT_LENGTH'}); 
Store the new annotation data
 my $worked = adStore($CGI{ID}, $xml);
 if(!$worked) { 
  cgiRespond(503, 
   qq{<error>Unable to update file</error>}); 
 }
 else { 
  cgiRespond(200, "text/plain", 
   "Update succeeded"); 
 }
}


1998-2002 Cartesian Products, Inc. Contact Cartesian