Introduction
Historical maps and old manuscripts contain characters oriented in various directions. This tool uses polygon annotations to accurately mark up tilted characters and leverages the tilt information to retrieve rotation-corrected images via the IIIF Image API.
How Tilt Calculation Works
Vertex Ordering Rules
When creating polygon annotations, vertices are specified in the following order:
- Top-left -> 2. Bottom-left -> 3. Bottom-right -> 4. Top-right
By following this counterclockwise order, the tilt angle can be uniquely calculated.
Top-left(1) ─────────── Top-right(4)
│ │
│ Character area │
│ │
Bottom-left(2) ─────── Bottom-right(3)
Please also refer to the following demo video.
https://youtu.be/P9srTeynXuk?si=mJO1yu3IhR0QFV-2
How the Angle Is Calculated
The tilt angle is calculated from the vector of the top edge (top-left to top-right):
// Extract vertices from SVG path
// M x1,y1 L x2,y2 L x3,y3 L x4,y4 Z
// Vertex 0 (top-left), Vertex 1 (bottom-left), Vertex 2 (bottom-right), Vertex 3 (top-right)
const dx = topRight.x - topLeft.x; // vertex3.x - vertex0.x
const dy = topRight.y - topLeft.y; // vertex3.y - vertex0.y
const angle = Math.atan2(dy, dx) * (180 / Math.PI);
// IIIF rotation parameter (clockwise is positive)
let iiifRotation = -angle;
if (iiifRotation 0) iiifRotation += 360;
Practical Examples
Example 1: Horizontal Text, Nearly Level
Annotation data:
{
"selector": [
{
"type": "FragmentSelector",
"value": "xywh=9030,15590,1231,244"
},
{
"type": "SvgSelector",
"value": "\"http://www.w3.org/2000/svg\">\"M 9034.61 15828.17 L 10261.06 15835.12 L 10262.08 15596.18 L 9030.33 15590.72 Z\" />"
}
]
}
Calculation:
Top-left: (9034.61, 15828.17)
Top-right: (9030.33, 15590.72)
dx = 9030.33 - 9034.61 = -4.28
dy = 15590.72 - 15828.17 = -237.45
angle = atan2(-237.45, -4.28) ≈ -91.0°
iiifRotation = 91°
Result:
- region:
9030,15590,1231,244 - rotation:
91

Example 2: Diagonally Tilted Characters
Annotation data:
{
"selector": [
{
"type": "FragmentSelector",
"value": "xywh=8843,18773,320,365"
},
{
"type": "SvgSelector",
"value": "\"http://www.w3.org/2000/svg\">\"M 9064.97 19139.05 L 9163.98 18836.97 L 8944.30 18773.62 L 8843.73 19059.54 Z\" />"
}
]
}
Calculation:
Top-left: (9064.97, 19139.05)
Top-right: (8843.73, 19059.54)
dx = 8843.73 - 9064.97 = -221.24
dy = 19059.54 - 19139.05 = -79.51
angle = atan2(-79.51, -221.24) ≈ -160.2°
iiifRotation = 160°
Result:
- region:
8843,18773,320,365 - rotation:
160

Retrieving Images via the IIIF Image API
URL Structure
The IIIF Image API URL structure:
{scheme}://{server}{/prefix}/{identifier}/{region}/{size}/{rotation}/{quality}.{format}
Image Without Rotation
https://example.org/iiif/image/8843,18773,320,365/,200/0/default.png
Tilt-Corrected Image
https://example.org/iiif/image/8843,18773,320,365/,200/160/default.png
Retrieving with a Transparent Background
To make the whitespace caused by rotation transparent, use the PNG format:
https://example.org/iiif/image/8843,18773,320,365/,200/160/default.png
Note: With the JPEG format, whitespace is filled with white.
Rotation Feature Support
The rotation feature of the IIIF Image API is not supported by all image servers. The IIIF Image API defines the following Compliance Levels:
| Level | Rotation Feature |
|---|---|
| Level 0 | Only 0 degrees supported |
| Level 1 | Only 0 degrees supported |
| Level 2 | 0, 90, 180, 270 degrees supported (arbitrary angles are optional) |
https://iiif.io/api/image/3.0/compliance/#33-rotation
https://iiif.io/api/image/2.1/compliance/#rotation

Many image servers operate at Level 1 or Level 2, and may not support arbitrary angle rotation. The support status can be checked in each image server’s info.json.
{
"profile": [
"http://iiif.io/api/image/2/level2.json",
{
"supports": ["rotationArbitrary"]
}
]
}
Arbitrary angle rotation is only possible when rotationArbitrary is included.
Viewing in the IIIF Image Viewer
Even if an image server does not support rotation, you can view rotation-corrected images using the IIIF Image Viewer. This viewer rotates the image in the browser, so it does not depend on the server’s rotation capabilities.
URL Parameters
| Parameter | Description | Example |
|---|---|---|
| iiif | IIIF Image API info.json URL | https://example.org/iiif/image/info.json |
| xywh | Cropping region (x,y,width,height) | 8843,18773,320,365 |
| rotation | Rotation angle (degrees) | 160 |
Usage Example
Best Practices for Creating Annotations
1. Follow the Vertex Order
Always specify vertices in the order “top-left -> bottom-left -> bottom-right -> top-right”. If this order is not followed, the tilt calculation will not work correctly.
2. Be Aware of Character Orientation
Create annotations so that the reading direction of the characters (the top edge) runs from top-left to top-right.
3. Include Adequate Margins
Rather than fitting the region exactly to the characters, including a small margin improves readability.
Technical Background
Why Both FragmentSelector and SvgSelector Are Needed
- FragmentSelector: Provides the bounding box (xywh) for cropping images via the IIIF Image API
- SvgSelector: Provides precise vertex coordinates for tilt calculation
FragmentSelector alone loses the tilt information, while SvgSelector alone requires computing the bounding box. By retaining both, efficient processing is possible.
About IIIF Image API Rotation
The rotation parameter of the IIIF Image API:
- Clockwise is positive
- Range is 0 to 360
- Decimal values are allowed
The rotated image may be larger than the original rectangle. This is because the smallest axis-aligned rectangle that encloses the rotated rectangle is returned.