# The normalized model Whatever version you fetch, iiisight hands you the same **v3-shaped, typed, frozen** model. It is a *consumer subset* of the Presentation API — the descriptive and structural properties a reader needs. Every collection field is a tuple that defaults to empty (never `None`), so traversal never trips on a missing list. ## The shape ```text Collection ─ items ─▶ Manifest | Collection | Reference Manifest ─ canvases ─▶ Canvas ─ structures ─▶ Range (table of contents) ─ annotations ─▶ AnnotationPage (manifest-level, non-painting) Canvas ─ items ─▶ AnnotationPage ─ items ─▶ Annotation ─ body ─▶ ContentResource ─ annotations ─▶ AnnotationPage (non-painting: comments, tags, …) ContentResource ─ service ─▶ ImageService ``` See the [API reference](../reference/api.md) for every field. ## Traversal ```python manifest = iiisight.normalize(document) for canvas in manifest.canvases: print(canvas.id, canvas.width, canvas.height, canvas.duration) # painting-annotation image bodies (derived, pure — no I/O) for image in canvas.images: print(image.id, image.format, image.width, image.height) # non-painting annotations (comments, captions, …) for page in canvas.annotations: for annotation in page.items: print(annotation.motivation, annotation.body, annotation.target) ``` `Canvas.images` and `Canvas.image_service` are computed properties over the parsed data — they never fetch. To load an image service's `info.json`, ask the client explicitly (see [Images & tiles](images.md)). ## Language maps IIIF strings are language maps (`{"en": ["Title"]}`). iiisight wraps them in {class}`~iiisight.language.LanguageMap`: ```python label = manifest.label str(label) # a display string (preferred/any language) label.text("en") # the English value(s), joined label.get("fr") # list[str] for French (empty list if absent) label.languages() # available language codes ``` Descriptive fields like `label`, `summary`, and `metadata[i].value` are all language maps. ## References vs. embedded resources Inside a collection or a range, an item may be a fully-embedded resource or a bare pointer. iiisight models the pointer as a {class}`~iiisight.Reference` (`id` / `type` / `label`). Fetch it with `client.resolve(reference)` or resolve a whole collection with `client.expand(...)`. ```python for item in collection.items: if isinstance(item, iiisight.Reference): manifest = await client.resolve(item) # sync: client.resolve(item) else: manifest = item # already embedded ``` ## Services Manifests, canvases, and image bodies can carry services (search, autocomplete, auth). These are parsed into {class}`~iiisight.Service` objects on a resource's `services` field, with the original JSON preserved on `service.raw` for specialized consumers. Image API services on a body stay typed as {class}`~iiisight.ImageService` on `ContentResource.service`. See [Search](search.md) and [Auth](auth.md) for the helpers that interpret them. ## Descriptive properties and the source document Beyond `label`/`summary`/`metadata`, every resource carries the descriptive properties the Presentation API defines: `provider` (a list of {class}`~iiisight.Agent`), `thumbnail` (a list of {class}`~iiisight.ContentResource`), `homepage` / `rendering` / `see_also` (lists of {class}`~iiisight.Link`), `part_of` (a list of {class}`~iiisight.Reference`), `required_statement` (a {class}`~iiisight.MetadataEntry`), `rights`, `behavior`, and `nav_date`. Each resource also keeps its **source document** on `.raw` — the JSON it was normalized from (v2 inputs are upgraded to v3 first, so `raw` is the v3-shaped document). This is for consumers that need the original verbatim (archiving, round-tripping a field the model doesn't surface): ```python manifest.provider[0].label # a parsed Agent's label manifest.raw["metadata"] # the untouched source metadata array ```