iiif_mcp_docs / docs.md
Gabriel's picture
Create docs.md
ac831e9 verified
# IIIF APIs Reference (Image API 3.0 & Presentation API 3.0)
## Overview
The IIIF (International Image Interoperability Framework) consists of complementary APIs for delivering and presenting digital content:
- **Image API**: Web service for image manipulation and delivery via URI parameters
- **Presentation API**: JSON-LD format for describing structure, metadata, and presentation of compound digital objects
- **Content Search API**: Full-text search within IIIF resources
## IIIF Image API 3.0
## URI Syntax
### Base URI Template
```
{scheme}://{server}{/prefix}/{identifier}
```
### Image Request URI Template
```
{scheme}://{server}{/prefix}/{identifier}/{region}/{size}/{rotation}/{quality}.{format}
```
### Image Information Request URI Template
```
{scheme}://{server}{/prefix}/{identifier}/info.json
```
## URI Components
| Component | Description |
|-----------|-------------|
| scheme | HTTP or HTTPS protocol |
| server | Host server (may include port number) |
| prefix | Optional path to the service on the host server |
| identifier | Identifier of the requested image (ARK, URN, filename, etc.) |
**Important**: Special characters in identifiers MUST be URI encoded, including slashes (/).
## Image Request Parameters
Parameters must be specified in this exact order: region/size/rotation/quality.format
### Region Parameter
Defines the rectangular portion of the image to return.
| Form | Description | Example |
|------|-------------|---------|
| `full` | Return the complete image | `full` |
| `square` | Return square region (shorter dimension) | `square` |
| `x,y,w,h` | Pixel coordinates (x,y) and dimensions (w,h) | `125,15,120,140` |
| `pct:x,y,w,h` | Percentage coordinates and dimensions | `pct:41.6,7.5,40,70` |
### Size Parameter
Specifies dimensions for scaling the extracted region.
| Form | Description | Example |
|------|-------------|---------|
| `max` | Maximum size available (no upscaling) | `max` |
| `^max` | Maximum size (allows upscaling) | `^max` |
| `w,` | Scale to exact width | `150,` |
| `^w,` | Scale to exact width (allows upscaling) | `^200,` |
| `,h` | Scale to exact height | `,100` |
| `^,h` | Scale to exact height (allows upscaling) | `^,150` |
| `pct:n` | Scale to n percent (max 100%) | `pct:50` |
| `^pct:n` | Scale to n percent (allows upscaling) | `^pct:150` |
| `w,h` | Exact width and height (may distort) | `200,300` |
| `^w,h` | Exact width and height (allows upscaling) | `^250,400` |
| `!w,h` | Best fit within w,h (maintains aspect ratio) | `!200,300` |
### Rotation Parameter
Specifies mirroring and rotation to be applied to the image.
| Form | Description |
|------|-------------|
| `0` | No rotation |
| `90`, `180`, `270` | Clockwise rotation in degrees |
| `!0`, `!90`, etc. | Horizontal mirroring then rotation |
### Quality Parameter
Determines color characteristics of the image.
| Value | Description |
|-------|-------------|
| `default` | Server-determined default quality |
| `color` | Full color image |
| `gray` | Grayscale image |
| `bitonal` | Black and white image |
### Format Parameter
Specifies the output format.
Common formats: `jpg`, `png`, `gif`, `webp`, `tif`
---
## IIIF Presentation API 3.0
### Purpose
The Presentation API provides a standardized way to describe compound digital objects (books, manuscripts, albums, etc.) with their structure, metadata, and presentation instructions. It uses JSON-LD format to enable rich viewing experiences across different client applications.
### Core Resource Types
#### Collection
Ordered list of Manifests and/or other Collections for hierarchical organization.
```json
{
"@context": "http://iiif.io/api/presentation/3/context.json",
"id": "https://example.org/collection/books",
"type": "Collection",
"label": { "en": ["Book Collection"] },
"items": [
{
"id": "https://example.org/manifest/book1",
"type": "Manifest",
"label": { "en": ["Book 1"] }
}
]
}
```
#### Manifest
Describes a single compound object with its structure, metadata, and viewing instructions.
```json
{
"@context": "http://iiif.io/api/presentation/3/context.json",
"id": "https://example.org/manifest/book1",
"type": "Manifest",
"label": { "en": ["Example Book"] },
"summary": { "en": ["A description of this book"] },
"metadata": [
{
"label": { "en": ["Author"] },
"value": { "en": ["Jane Doe"] }
}
],
"items": [
{
"id": "https://example.org/canvas/page1",
"type": "Canvas",
"label": { "en": ["Page 1"] },
"height": 1000,
"width": 750,
"items": [
{
"id": "https://example.org/page/page1/annopage",
"type": "AnnotationPage",
"items": [
{
"id": "https://example.org/page/page1/anno",
"type": "Annotation",
"motivation": "painting",
"body": {
"id": "https://example.org/iiif/page1/full/max/0/default.jpg",
"type": "Image",
"format": "image/jpeg",
"service": [
{
"id": "https://example.org/iiif/page1",
"type": "ImageService3",
"profile": "level2"
}
]
},
"target": "https://example.org/canvas/page1"
}
]
}
]
}
]
}
```
#### Canvas
Virtual container representing a view of the object (page, image, time segment) with defined dimensions.
- **Spatial**: Uses `height` and `width` properties
- **Temporal**: Uses `duration` property
- **Mixed**: Can have all three dimensions
#### Range
Defines structural sections within an object (chapters, movements, articles).
```json
{
"id": "https://example.org/range/chapter1",
"type": "Range",
"label": { "en": ["Chapter 1"] },
"items": [
{ "id": "https://example.org/canvas/page1", "type": "Canvas" },
{ "id": "https://example.org/canvas/page2", "type": "Canvas" }
]
}
```
#### Annotation & Annotation Page
Content is associated with Canvases through Annotations collected in Annotation Pages.
- **Painting motivation**: Content that is part of the Canvas (images, video, audio)
- **Supplementing motivation**: Content derived from the Canvas (transcriptions, translations)
- **Other motivations**: Commentary, tags, etc.
### Essential Properties
#### Required Properties
- **id**: HTTP(S) URI identifier
- **type**: Resource class (Collection, Manifest, Canvas, etc.)
- **label**: Human-readable name (internationalized)
#### Key Descriptive Properties
- **metadata**: Array of label/value pairs for display
- **summary**: Brief description
- **thumbnail**: Representative image(s)
- **provider**: Institution/organization information
- **rights**: License or rights statement URI
- **requiredStatement**: Text that must be displayed (attribution)
#### Technical Properties
- **height/width**: Pixel dimensions for images, aspect ratio for Canvas
- **duration**: Time length in seconds
- **format**: MIME type (e.g., "image/jpeg")
- **profile**: Schema or functionality description
- **viewingDirection**: Display order (left-to-right, right-to-left, etc.)
- **behavior**: Presentation hints (paged, continuous, individuals, etc.)
#### Structural Properties
- **items**: Ordered list of child resources
- **structures**: Top-level Ranges for table of contents
- **annotations**: Commentary Annotation Pages
### Language Internationalization
Properties like `label`, `summary`, and `metadata` use language maps:
```json
{
"label": {
"en": ["English Title"],
"fr": ["Titre FranΓ§ais"],
"none": ["No Language"]
}
}
```
### Behavior Values
Control presentation and navigation:
| Behavior | Applies To | Description |
|----------|------------|-------------|
| `auto-advance` | Collections, Manifests, Canvases, Ranges | Automatically proceed to next item |
| `no-auto-advance` | Collections, Manifests, Canvases, Ranges | Do not auto-advance (default) |
| `paged` | Collections, Manifests, Ranges | Page-turning interface |
| `continuous` | Collections, Manifests, Ranges | Virtually stitch views together |
| `individuals` | Collections, Manifests, Ranges | Distinct objects/views (default) |
| `unordered` | Collections, Manifests, Ranges | No inherent order |
| `facing-pages` | Canvases | Canvas shows both parts of opening |
| `sequence` | Ranges | Alternative ordering of Canvases |
| `hidden` | Annotations, etc. | Not rendered by default |
### Viewing Directions
- `left-to-right` (default)
- `right-to-left`
- `top-to-bottom`
- `bottom-to-top`
---
## Practical Integration Examples
## Practical Integration Examples
### Image API + Presentation API Workflow
1. **Presentation API** provides structure and metadata via Manifests
2. **Image API** delivers actual image content with manipulation capabilities
3. **Annotations** link Image API resources to Canvas positions
```json
{
"id": "https://example.org/canvas/page1",
"type": "Canvas",
"height": 1000,
"width": 750,
"items": [{
"id": "https://example.org/annopage/page1",
"type": "AnnotationPage",
"items": [{
"id": "https://example.org/annotation/page1-image",
"type": "Annotation",
"motivation": "painting",
"body": {
"id": "https://example.org/iiif/page1/full/max/0/default.jpg",
"type": "Image",
"format": "image/jpeg",
"service": [{
"id": "https://example.org/iiif/page1",
"type": "ImageService3",
"profile": "level2"
}]
},
"target": "https://example.org/canvas/page1"
}]
}]
}
```
### Image API Examples
### General Examples
```
# Full image at maximum size, default quality, JPEG format
https://example.org/image-service/abcd1234/full/max/0/default.jpg
# Square region, scaled to 200px width
https://example.org/image-service/abcd1234/square/200,/0/default.jpg
# Specific region by pixels, 50% scale, 90Β° rotation
https://example.org/image-service/abcd1234/125,15,120,140/pct:50/90/color.png
# Region by percentage, best fit 300x200
https://example.org/image-service/abcd1234/pct:25,25,50,50/!300,200/0/gray.jpg
# Image information document
https://example.org/image-service/abcd1234/info.json
```
### Riksarkivet (Swedish National Archives) Examples
**IIIF Image API 3.0:**
```
# Base URL patterns
https://lbiiif.riksarkivet.se/{image-id}/{region}/{size}/{rotation}/{quality}.jpg
https://lbiiif.riksarkivet.se/v3/{image-id}/{region}/{size}/{rotation}/{quality}.jpg
# Full image examples
https://lbiiif.riksarkivet.se/arkis!R0001216_00005/full/max/0/default.jpg
# Square crop
https://lbiiif.riksarkivet.se/arkis!R0001216_00005/square/max/0/default.jpg
# Region by pixels (x=300, y=300, width=100, height=100)
https://lbiiif.riksarkivet.se/arkis!R0001216_00005/300,300,100,100/max/0/default.jpg
# Scale to fixed width (300px)
https://lbiiif.riksarkivet.se/arkis!R0001216_00005/full/300,/0/default.jpg
# Scale to fixed height (500px)
https://lbiiif.riksarkivet.se/arkis!R0001216_00005/full/,500/0/default.jpg
# Scale to exact dimensions (may distort aspect ratio)
https://lbiiif.riksarkivet.se/arkis!R0001216_00005/full/500,300/0/default.jpg
# 90-degree clockwise rotation
https://lbiiif.riksarkivet.se/arkis!R0001216_00005/full/max/90/default.jpg
# 90-degree rotation with horizontal flip
https://lbiiif.riksarkivet.se/arkis!R0001216_00005/full/max/!90/default.jpg
```
**IIIF Presentation API:**
```
# Manifest for a specific resource
https://lbiiif.riksarkivet.se/{identifier}/manifest
# Example manifest
https://lbiiif.riksarkivet.se/arkis!R0000480/manifest
# Collections (hierarchical organization)
https://lbiiif.riksarkivet.se/collection/riksarkivet
https://lbiiif.riksarkivet.se/collection/amnesomraden # Subject areas
https://lbiiif.riksarkivet.se/collection/tid # Time periods
# Collection by archive unit PID
https://lbiiif.riksarkivet.se/collection/arkiv/2Yz2ClKdn2VMeUXQcoZOO6
```
**IIIF Content Search:**
```
# Search within a volume
https://lbiiif.riksarkivet.se/search/arkis!40008511?q=kaffe
https://lbiiif.riksarkivet.se/search/arkis!A0068583?q=juveler
```
### Universal Examples (Any IIIF Server)
## Key Implementation Notes
- Parameters must be in exact order: region/size/rotation/quality.format
- All special characters in identifiers must be URI encoded
- Operations are applied in parameter order: extract region β†’ scale β†’ rotate β†’ adjust quality β†’ convert format
- Servers SHOULD support CORS on image responses
- If region extends beyond image bounds, crop at edges rather than adding empty space
- Zero-dimension regions should return 400 Bad Request
## Error Handling and Rate Limiting
Common HTTP status codes:
- `400 Bad Request` - Invalid parameters
- `403 Forbidden` - Missing rights marking or access denied
- `404 Not Found` - Image/resource not found
- `429 Too Many Requests` - Rate limit exceeded (check X-RateLimit-* headers)
- `501 Not Implemented` - Feature not supported
## CORS Configuration
For external applications, configure CORS headers:
```
# Request header
Origin: https://yourdomain.com
# Response header
Access-Control-Allow-Origin: https://yourdomain.com
```
**Reverse Proxy Setup (recommended for external services):**
Apache HTTP Server:
```apache
ProxyPass /iiif/ https://lbiiif.riksarkivet.se/
ProxyPassReverse /iiif/ https://lbiiif.riksarkivet.se/
Header always set Access-Control-Allow-Origin "*"
```
Nginx:
```nginx
location /iiif/ {
proxy_pass https://lbiiif.riksarkivet.se/;
add_header Access-Control-Allow-Origin "*";
}
```
## Programming Examples
### Python: Working with IIIF Manifests and Images
```python
import requests
import json
# Fetch and parse a IIIF Manifest
def get_manifest(manifest_url):
response = requests.get(manifest_url)
return response.json()
# Extract all image URLs from a manifest
def extract_image_urls(manifest):
image_urls = []
for canvas in manifest.get('items', []):
for annotation_page in canvas.get('items', []):
for annotation in annotation_page.get('items', []):
if annotation.get('motivation') == 'painting':
body = annotation.get('body', {})
if body.get('type') == 'Image':
image_urls.append(body.get('id'))
return image_urls
# Get image service base URL from image URL
def get_image_service_url(image_url):
# Remove the Image API parameters to get base service URL
# e.g., https://example.org/iiif/img1/full/max/0/default.jpg
# becomes https://example.org/iiif/img1
parts = image_url.split('/')
return '/'.join(parts[:-4]) # Remove /region/size/rotation/quality.format
# Generate thumbnail URL using Image API
def create_thumbnail_url(image_url, width=150, height=150):
service_url = get_image_service_url(image_url)
return f"{service_url}/full/!{width},{height}/0/default.jpg"
# Example usage
manifest_url = "https://lbiiif.riksarkivet.se/arkis!R0000480/manifest"
manifest = get_manifest(manifest_url)
images = extract_image_urls(manifest)
for img_url in images:
thumb_url = create_thumbnail_url(img_url)
print(f"Original: {img_url}")
print(f"Thumbnail: {thumb_url}")
```
### Python: Download Images from Riksarkivet Collection
```python
import requests
import sys
import json
import zipfile
import re
import os
URL_BASE = 'https://lbiiif.riksarkivet.se/collection/arkiv/'
def process_collection(collection, zip_file):
for item in collection.get('items', []):
res = requests.get(item.get('id'))
if res.status_code == 200:
item_json = res.json()
if item.get('type') == 'Collection':
process_collection(item_json, zip_file)
else:
process_manifest(item_json, zip_file)
def process_manifest(manifest, zip_file):
for canvas in manifest.get('items', []):
for annotation_page in canvas.get('items', []):
for annotation in annotation_page.get('items', []):
image_url = annotation.get('body', {}).get('id')
if image_url:
regex = re.compile('.*\!(.*)\/full.*')
result = regex.match(image_url)
if result:
image_id = result.group(1)
try:
file_name = f'{image_id}.jpg'
print(f'Processing {file_name}')
download_file(image_url, file_name)
zip_file.write(file_name, file_name)
os.remove(file_name)
except Exception as e:
print(e)
def download_file(url, file_name):
with requests.get(url, stream=True) as r:
r.raise_for_status()
with open(file_name, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
def main():
if len(sys.argv) > 1:
pid = sys.argv[1]
res = requests.get(f'{URL_BASE}{pid}')
if res.status_code == 200:
collection = res.json()
with zipfile.ZipFile(f'{pid}.zip', 'w') as zip_file:
process_collection(collection, zip_file)
else:
print('Usage: download.py <pid>')
if __name__ == "__main__":
main()
```
### JavaScript: IIIF Viewer Integration
```javascript
// Universal Viewer integration
const manifestUri = 'https://lbiiif.riksarkivet.se/arkis!R0000480/manifest';
const uv = new UV.createUV('#uv-container', {
manifestUri: manifestUri,
configUri: '/uv-config.json'
});
// Mirador viewer integration
const mirador = Mirador.viewer({
id: 'mirador-container',
windows: [{
manifestId: manifestUri,
canvasId: 'https://lbiiif.riksarkivet.se/arkis!R0000480/canvas/1'
}]
});
// OpenSeadragon with IIIF Image service
const viewer = OpenSeadragon({
id: 'osd-container',
tileSources: {
'@context': 'http://iiif.io/api/image/3/context.json',
'@id': 'https://lbiiif.riksarkivet.se/arkis!R0000480_00001',
'profile': 'level1'
}
});
// Fetch manifest and display metadata
async function displayManifest(manifestUrl) {
const response = await fetch(manifestUrl);
const manifest = await response.json();
// Display basic info
const title = manifest.label?.en?.[0] || manifest.label?.sv?.[0] || 'Untitled';
const summary = manifest.summary?.en?.[0] || manifest.summary?.sv?.[0] || '';
document.getElementById('title').textContent = title;
document.getElementById('summary').textContent = summary;
// Display metadata
const metadataContainer = document.getElementById('metadata');
manifest.metadata?.forEach(item => {
const label = item.label?.en?.[0] || item.label?.sv?.[0] || '';
const value = item.value?.en?.[0] || item.value?.sv?.[0] || '';
const div = document.createElement('div');
div.innerHTML = `<strong>${label}:</strong> ${value}`;
metadataContainer.appendChild(div);
});
}
```
### Finding Image IDs from Archive Units
1. Get archive unit PID from search interface
2. Fetch collection data: `https://lbiiif.riksarkivet.se/collection/arkiv/{pid}`
3. Extract manifest URLs from `items` array
4. Parse image IDs from manifest image URLs
### IIIF Presentation API Structure
**Collection Response Example:**
```json
{
"id": "https://lbiiif.riksarkivet.se/collection/arkiv/2Yz2ClKdn2VMeUXQcoZOO6",
"type": "Collection",
"label": { "sv": ["1 (1654-1721) - Handlingar rΓΆrande fΓΆrhandlingar..."] },
"summary": { "sv": ["Referenskod: SE/RA/2113/2113.2/1"] },
"items": [
{
"id": "https://lbiiif.riksarkivet.se/arkis!R0000480/manifest",
"type": "Manifest",
"label": { "sv": ["1 (1654-1721) - R0000480 - Handlingar..."] }
}
]
}
```
**Hierarchy:**
- **Collection** β†’ Contains other Collections or Manifests
- **Manifest** β†’ Contains Canvases (individual pages/images)
- **Canvas** β†’ Contains image annotations with Image API URLs
## Common Use Cases
### Image API
- **Thumbnails** - Quick generation of preview images at specific sizes
- **Deep zoom viewers** - Requesting tiles and regions for interactive viewing
- **Responsive images** - Serving appropriate sizes for different devices
- **Image analysis** - Extracting specific regions for processing
- **Print quality** - High-resolution downloads with rotation/cropping
### Presentation API
- **Digital libraries** - Organizing books, manuscripts, and documents
- **Museum collections** - Presenting artworks with metadata and context
- **Archives** - Hierarchical organization of historical materials
- **Educational platforms** - Structured learning materials with annotations
- **Cultural heritage** - Multi-language, multi-institutional content sharing
### Combined Workflows
- **Reading room interfaces** - Full manuscripts with page-turning and zoom
- **Annotation tools** - Transcription and commentary systems
- **Comparative viewing** - Side-by-side analysis of similar materials
- **Mobile apps** - Responsive cultural heritage exploration
- **Research platforms** - Advanced search and analysis tools
## Implementation Notes
### Content Delivery Best Practices
- Use Image API for dynamic image manipulation and delivery
- Use Presentation API for metadata, structure, and viewing instructions
- Combine both APIs for complete digital object presentation
- Implement proper caching strategies for performance
- Consider CDN deployment for global access
### Client Application Patterns
1. **Parse Manifest** β†’ Extract structure and metadata
2. **Render Canvas** β†’ Display individual views/pages
3. **Load Images** β†’ Use Image API services for content
4. **Handle Annotations** β†’ Process transcriptions, translations, comments
5. **Navigate Structure** β†’ Use Ranges for table of contents
6. **Apply Behaviors** β†’ Follow presentation hints (paging, auto-advance, etc.)
### Data Flow
```
Collection β†’ Manifest β†’ Canvas β†’ Annotation β†’ Image Service
↓ ↓ ↓ ↓ ↓
Hierarchy Metadata Viewport Association Pixels
```
## Implementation Variations
### Riksarkivet (Swedish National Archives) Specifics
**Technical Implementation:**
- **Compliance Level**: Image API Level 1 (basic features)
- **Supported Formats**: JPEG only (`.jpg`)
- **Supported Quality**: `default` only
- **Version Support**: Both IIIF 2.0 and 3.0 APIs
- **Content Types**:
- Images: `content-type: image/jpeg`
- Manifests: `content-type: application/json`
- **Access Rights**: Public digitized archives older than 110 years
- **Rate Limiting**: Enforced with `X-RateLimit-*` headers
**Content Organization:**
- Collections organized by subject (`amnesomraden`) and time (`tid`)
- Archive units identified by persistent IDs (PIDs)
- Manifests correspond to volumes or individual items
- Search integration with full-text OCR content
**API Endpoints:**
- Image API: `https://lbiiif.riksarkivet.se/`
- Collections: `https://lbiiif.riksarkivet.se/collection/`
- Search: `https://lbiiif.riksarkivet.se/search/`
### General IIIF Implementation Patterns
**Compliance Levels (Image API):**
- **Level 0**: Static images with limited manipulation
- **Level 1**: Basic resize, rotation, quality options
- **Level 2**: Full specification support with advanced features
**Common Institution Patterns:**
- **Museums**: Focus on high-quality images with rich metadata
- **Libraries**: Emphasis on text materials with OCR and search
- **Archives**: Hierarchical organization with finding aids
- **Universities**: Research-focused with annotation tools
**Supported IIIF APIs (Typical Implementations):**
- βœ… **Image API 3.0/2.0** - Core image delivery (universal)
- βœ… **Presentation API 3.0** - Metadata and structure (most institutions)
- βœ… **Content Search API 1.0** - Full-text search (text-heavy collections)
- ⚠️ **Authentication API 1.0** - Access control (restricted content)
- ⚠️ **Change Discovery API 1.0** - Update notifications (large institutions)
### Server Technology Stacks
**Popular IIIF Server Software:**
- **Cantaloupe** - Java-based image server (Image API)
- **IIPImage** - C++ high-performance server
- **Loris** - Python image server
- **RIIIF** - Ruby implementation
- **Omeka S** - Content management with IIIF support
- **Samvera/Hyrax** - Digital repository platform
- **DSpace** - Institutional repository with IIIF modules
**Cloud Solutions:**
- **IIIF Hosting** - Managed IIIF services
- **AWS/Azure/GCP** - Serverless IIIF implementations
- **ContentDM** - OCLC's hosted digital collections platform
## Integration with Viewers
### Popular IIIF-Compatible Viewers
**Full-Featured Viewers:**
- **Universal Viewer** - Complete presentation with navigation and metadata
- **Mirador** - Advanced viewer with workspace and comparison features
- **Tify** - Vue.js-based viewer for modern web applications
**Specialized Viewers:**
- **OpenSeadragon** - High-performance pan/zoom for images
- **Leaflet-IIIF** - Map-style tile viewer for large images
- **Annona** - Lightweight annotation-focused viewer
**Implementation Examples:**
```javascript
// Universal Viewer (comprehensive)
const manifestUri = 'https://lbiiif.riksarkivet.se/arkis!R0000480/manifest';
const uv = new UV.createUV('#uv', { manifestUri: manifestUri });
// Mirador (research-focused)
const mirador = Mirador.viewer({
id: 'mirador',
windows: [{ manifestId: manifestUri }],
workspaceControlPanel: { enabled: true }
});
// OpenSeadragon (image-focused)
const viewer = OpenSeadragon({
id: 'osd',
tileSources: 'https://example.org/iiif/image1/info.json'
});
```
### Mobile and Responsive Considerations
- Progressive image loading for bandwidth optimization
- Touch-friendly zoom and pan interactions
- Adaptive metadata display for small screens
- Offline capability for downloaded content
- Platform-specific apps (iOS/Android) using IIIF APIs