{"id":19640649,"url":"https://github.com/daniel-km/omeka-s-module-iiifserver","last_synced_at":"2026-02-26T11:02:11.457Z","repository":{"id":48523512,"uuid":"84068638","full_name":"Daniel-KM/Omeka-S-module-IiifServer","owner":"Daniel-KM","description":"IIIF Server is a module for Omeka S that adds the IIIF specifications to serve any images and medias.","archived":false,"fork":false,"pushed_at":"2026-02-16T14:35:01.000Z","size":2996,"stargazers_count":17,"open_issues_count":2,"forks_count":11,"subscribers_count":5,"default_branch":"master","last_synced_at":"2026-02-16T22:10:15.026Z","etag":null,"topics":["deepzoom","iiif","iiif-server","omeka-s","omeka-s-module","zoomify"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Daniel-KM.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2017-03-06T12:04:44.000Z","updated_at":"2026-02-16T14:18:02.000Z","dependencies_parsed_at":"2023-02-14T05:16:43.278Z","dependency_job_id":"a7ea91aa-dd4b-4264-a1d5-0585b218031f","html_url":"https://github.com/Daniel-KM/Omeka-S-module-IiifServer","commit_stats":null,"previous_names":[],"tags_count":61,"template":false,"template_full_name":null,"purl":"pkg:github/Daniel-KM/Omeka-S-module-IiifServer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Daniel-KM%2FOmeka-S-module-IiifServer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Daniel-KM%2FOmeka-S-module-IiifServer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Daniel-KM%2FOmeka-S-module-IiifServer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Daniel-KM%2FOmeka-S-module-IiifServer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Daniel-KM","download_url":"https://codeload.github.com/Daniel-KM/Omeka-S-module-IiifServer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Daniel-KM%2FOmeka-S-module-IiifServer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29856804,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-26T08:51:08.701Z","status":"ssl_error","status_checked_at":"2026-02-26T08:50:19.607Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deepzoom","iiif","iiif-server","omeka-s","omeka-s-module","zoomify"],"created_at":"2024-11-11T14:06:21.770Z","updated_at":"2026-02-26T11:02:10.306Z","avatar_url":"https://github.com/Daniel-KM.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"IIIF Server (module for Omeka S)\n================================\n\n\u003e __New versions of this module and support for Omeka S version 3.0 and above\n\u003e are available on [GitLab], which seems to respect users and privacy better\n\u003e than the previous repository.__\n\n[IIIF Server] is a module for [Omeka S] that integrates the [IIIF specifications]\nto allow to process and share instantly images of any size and medias (pdf,\naudio, video, 3D…) in the desired formats. It can use any image server to\ndisplay images and media, like the module [Image Server], but you can use any\nother external one, like [Cantaloupe] or [IIP Image].\n\nThe full specifications of the [International Image Interoperability Framework]\nstandard are supported (service 2 or 3, level 2), so any web or desktop viewer\nthat supports it can use it. Rotation, zoom, inside search, text overlay, etc.\nmay be managed too. Dynamic lists of records may be created, for example for\nbrowse pages.\n\nThe IIIF manifests can be displayed with many viewers, the integrated [OpenSeadragon],\nthe [Universal Viewer], the advanced [Mirador], or the lighter and themable [Diva],\nor any other IIIF compatible viewer.\n\nThe search is provided by the module [Iiif Search] for common formats (xml [alto]\n(recommended), xml [pdf2xml], html [hocr]).\n\n\nInstallation\n------------\n\n### Module\n\nSee general end user documentation for [installing a module].\n\nThe module [Common] must be installed first.\n\nThe module uses external libraries, so use the release zip to install it, or\nuse and init the source.\n\n* From the zip\n\nDownload the last release [IiifServer.zip] from the list of releases (the\nmaster does not contain the dependency), and uncompress it in the `modules`\ndirectory.\n\n* From the source and for development\n\nIf the module was installed from the source, rename the name of the folder of\nthe module to `IiifServer`, go to the root of the module, and run:\n\n```sh\ncomposer install --no-dev\n```\n\nThen install it like any other Omeka module.\n\nNote: To keep old options from [Universal Viewer], upgrade it to version 3.4.3\nbefore enabling of IiifServer. Else, simply set them in the config form.\n\n### PHP\n\nPHP should be installed with the extension `exif` in order to get the size of\nimages. This is the case for all major distributions and providers.\n\n### Web server and identifiers containing `/`\n\nThis point is related to Apache. If you are using nginx, you can skip it.\n\nIf you are using Ark as identifier for items and IIIF manifests (through the\nmodules [Clean Url] and optionally [Ark]), or any other identifier that contains\na forward slash `/`, you must modify the Apache system configuration, because by\ndefault, [it doesn't allow the url encoded `/`] (`%2F`) inside urls for an old\nsecurity issue.\n\nIndeed, an Ark identifier contains at least two forward slashes `/` (`ark:/12345/bNw3sx`)\nand the IIIF specification requires to use [url encoded slashes], except for the\ncolon `:` (`ark:%2F12345%2FbNw3sx`). More precisely, this iiif requirement is\nspecified in the Image Api, not in the presentation Api, but the identifiers are\nused in the same way.\n\n```Apache\nAllowEncodedSlashes NoDecode\n```\n\nIf you cannot access to config of the server, two settings can fix it in the\nconfig of the module :\n- disable the advanced option \"Use the identifiers from Clean Url\";\n- set the \"Prefix to use for identifier\".\n\n### CORS (Cross-Origin Resource Sharing)\n\nTo be able to share manifests and contents with other IIIF servers, the server\nshould allow [CORS]. This feature can be enable in the config of the module, in\nthe config of the server, or in the file `.htaccess`.\n\n**Warning**: the cors headers should be set one time only. If it is set multiple\ntimes, it will be disabled. This is the purpose of the option in the main config\nof the module.\n\nIf you prefer to append cors via the config the server, disable the option in\nthe config first. On Apache 2.4, the module \"headers\" must be enabled:\n\n```sh\na2enmod headers\nsystemctl restart apache2\n```\n\nThen, you have to add the following rules, adapted to your needs, to the file\n`.htaccess` at the root of Omeka S or in the main config of the server:\n\n```\n# CORS access for some files.\n\u003cIfModule mod_headers.c\u003e\n    Header setIfEmpty Access-Control-Allow-Origin \"*\"\n    Header setIfEmpty Access-Control-Allow-Headers \"origin, x-requested-with, content-type\"\n    Header setIfEmpty Access-Control-Allow-Methods \"GET, POST\"\n\u003c/IfModule\u003e\n```\n\nIt is recommended to use the main config of the server, for example  with the\ndirective `\u003cDirectory\u003e`.\n\nTo fix Amazon issues with cors, see the [aws documentation].\n\n### Cache\n\nWhen your documents are big (more than 100 to 1000 pages, depending on your\nserver, your network and your public), you may want to cache manifests in order\nto deliver them instantly. In that case, check the option in the config.\n\n### Local access to iiif source\n\nThe iiif authentication api is not yet integrated. Anyway, to access iiif\nresources when authenticated, the [fix #omeka/omeka-s/1714] can be patched or\nthe module [Guest] can be used.\n\n### IIIF button in a theme\n\nA resource page block is available in the theme settings. Else, you can add the\nview helper in your theme:\n\n```php\n\u003c?= $this-\u003eiiifManifestLink($item) ?\u003e\n```\n\n* For test\n\nThe module includes a comprehensive test suite with unit and functional tests.\nRun them from the root of Omeka:\n\n```sh\nvendor/bin/phpunit -c modules/IiifServer/phpunit.xml --testdox\n```\n\n\nImage server\n------------\n\nExcept if all your manifests are external or if you have no media, you will need\nan image server that supports the standard IIIF . It can be the module [Image Server]\nor an external server, like [Cantaloupe], [IIP Image] or [many others]. The\nimage server may be used to display audio and video files too, if it supports\nthem, else they will be served by Omeka.\n\n### Module Image Server\n\nThe module [Image Server] serves original images and can create tile statically\nor dynamically. This is the simplest way to get instant big images with Omeka.\nThe images are the original ones, stored by Omeka. And if you use [vips] as\nbackend, the performance will be similar to specialized tools.\n\n### External images servers\n\nFor other image servers, two configurations are possible.\n\n#### Use the Omeka media type \"IIIF Image\"\n\nThe first possibility is to use an external server with a specific url path or a\nsubdomain. In that case, you have to create or import all your images as media\n\"[IIIF Image]\". With this media type, the full json is saved in metadata of the\nmedia itself and the original images remain separate from Omeka.\n\nThis is the simplest when your images are already managed by one or multiple\ndedicated third-party image server (for instance one of the software listed in\nthe [official list](https://github.com/IIIF/awesome-iiif/#image-servers) of the\nIIIF community).\n\nOf course, it requires a second tool to manage your images, at least to copy\nyour directories of images in the image server (generally by ftp or a shared\ndisk space).\n\n#### Use original files as storage for the image server\n\nThe second possibility is to use the original images, so the files inside the\ndirectory \"files/original\". In that cases, the original files are managed by\nOmeka with the media types \"Upload\" or \"Url\". This possibility requires that the\nimage server to be on the same server than Omeka, or at least that the image\nserver can access the original directory via the file system of via http, or any\nanother protocol.\n\nNote that for performance, it is recommended that the image server uses the same\nfile system than the web server (Omeka), so they should be on the same server or\nthe directory `files/original/` should be shared, so the image server can access\ndirectly to files on the filesystem, that is the quickest. The directory can be\nchanged on Omeka in the config local.config.php (keys `['file_store']['local']['base_path']\nand ['base_uri'], or in the config of the external server. It can be done via\nvolumes too if the infrastructure is containerized.\n\nThree params should be set:\n- set the original directory as the base path in your image server (option \"FilesystemSource.BasicLookupStrategy.path_prefix\"\n  for Cantaloupe);\n- set the option \"filename with extension\" in the config of the module.\n- add some rules in Apache config or in htaccess to redirect request to the\n  image server. Normally, a regex starting with iiif/ and finishing with the\n  supported file extensions is enough.\n\n#### Note for Cantaloupe\n\nSince 2020 (v5), Cantaloupe supports IIIF Image Api v3. If you use an older\nversion, keep Image Api v2.\n\n\nNotes\n-----\n\nThe module allows to manage collections (item set level/item list), and\nmanifests (item level). The info.json (media level) are managed by the image\nserver.\n\n### Using externally supplied IIIF manifest\n\nIf you are harvesting data (via OAI-PMH, for instance) from another system where\nimages are hosted and exposed via IIIF, you can use a configurable metadata\nfield to supply the manifest to the viewer (Universal Viewer, Mirador or Diva).\nIn this case, no images are hosted in the Omeka record, but one of the metadata\nfields has the URL of the manifest hosted on another server.\n\nFor example, you could set the alternative manifest element to \"Dublin Core:Has Format\"\nin the module configuration, and then put a URL like \"https://example.com/iiif/HI-SK20161207-0009/manifest\"\nin the specified element of a record. The viewer included on that record’s\ndisplay page will use that manifest URL to retrieve images and metadata for the\nviewer.\n\n### Config options for manifest\n\n#### Incompatible data\n\nSome extracted texts produce invalid unicode / utf-8 data, so it is recommended\nto exclude the ocr data from the manifest. Anyway, it should not be included to\nfollow the iiif specifications: the metadata are only used to display basic data\nabout the document.\n\nOn new install, these properties are not included: `dcterms:tableOfContents`,\n`bibo:content` and `extracttext:extracted_text`. You may have to add them to\navoid future issues.\n\n#### Text overlay\n\nFor the overlay, only alto xml files are supported currently. They are\nautomatically included in the manifest.\n\nTo enable it, you may check config of the viewer, for example add plugin \"overlay\"\nin the site setting of Mirador.\n\nThe alto xml files should be attached to the item as a media for now and it\nshould have the same source filename than the image (except extension).\n\nA future version will allow to use a linked media or a uri via a property.\n\nA full example of iiif manifest with search, autocomplete and overlay can be\nfound in the [Wellcome library](https://iiif.wellcomecollection.org/presentation/v2/b19956435).\n\n#### Input format of the property for structures (table of contents)\n\nIIIF allows to display [structures] of documents.\n\nThe default structure is the simple sequential list of iiif medias.\n\nTo build structures for a complex document with a table of contents, you can use\na specific property and fill a value with the needed json, or with a literal\nvalue with the following format. Each row is a part of the structure:\n\n```csv\n{id}, {label}, {canvasIndexOrRangeId1}; {canvasIndexOrRangeId2}; …; {canvasIndexOrRangeIdN}\n```\n\nExample:\n\n```csv\ncover, Front Cover, 1\nr2, Introduction, 2; 3; 4; 5\nbackCover, Back Cover, 6\n```\n\nA new format allows to include the view number in the third column:\n\n```csv\ncover, Front Cover, 1, 1\nr2, Introduction, 2, 2; 3; 4; 5\nbackCover, Back Cover, 6, 6\n```\n\n\nThe range id (first part of a row) is the name of the range, that will be used\nto create the uri. To avoid collision with other indexes, it must not be a\nnumeric value. It should be a simple alphanumeric name, without space, diacritic\nor any other special character, so it will be stable among all coding standards.\nIt must not contain characters `:/?\u0026#%=\u003c\u003e;,`. Ideally, the url-encoded id should be\nthe same than the id. Anyway, this name is url-encoded in the final uri.\n\nFurthermore, the range ids must be unique in all the item.\n\nIt can be skipped, so the line number will be used. In that case, keep the first\ncomma to indicate that there is no specific range name. For example if `r2` was\nnot provided above, the internal range id will be `r2` anyway, so `r` for range\nand `2` for second line. Nevertheless, this possibility is not recommended\nbecause the uri will change when a new line will be inserted.\n\nThe second part of the row is the label of the range, for example the title of\nthe chapter. If empty, it will be used for the structure, but not displayed in\nthe table of the viewer.\n\nThe last part of the row is the list of the top canvases or top ranges that the\ncurrent range contains, so generally a list of images and sub-sections.\n\nIn most of the cases, the canvas index is the media position. Only medias that\nare used in the iiif are enumerated, not the specific medias, like pdf, xml, etc.\nattached to the item, so take care of its value, that may be different from the\nOmeka internal position in the list of attached medias to an item. Other indexes\nwill be managed as range indexes if they are in the list of the range ids (first\npart of the row). If not, it will be a canvas alphanumeric name.\n\nThe first range id of the first line is  the root of the tree. There can be only\none root in iiif v3, but multiple structures. So if there are multiple roots\n(see below), a main range is added with all the roots as main branches.\n\nIf you use a xml value with module [DataType Rdf], the structure above will be\ncomposed of canvases (element `c` here):\n\n```xml\n\u003cc id=\"cover\" label=\"Front Cover\" ranges=\"1\"/\u003e\n\u003cc id=\"r2\" label=\"Introduction\" ranges=\"2; 3; 4; 5\"/\u003e\n\u003cc id=\"backcover\" label=\"Back Cover\" ranges=\"6\"/\u003e\n```\n\nThe ranges can be omitted anyway, since the xml structure itself provide it (see\nnested xml below).\n\nSo it is possible to build complex hierarchical table of contents from this\nliteral value, even with such an incomplete example, that is automatically\ncompleted with pages that are not sections:\n\n```csv\ntoc, Table of Contents, cover; intro; r1; r2; backcover\n    cover, Front cover, cover\n    intro, Introduction, 2-5\n    r1, First chapter, 6; r1-1; r1-2; 12\n        r1-1, First section, r1-1-1; r1-1-2; illustration1; illus2\n            r1-1-1, First sub-section, 8-9\n            r1-1-2, Second sub-section, 9-10\n    r2, Second chapter, 13\n    backcover, Back cover, \"backcover\"\nillustration1, First illustration non paginated, illus1\nillustration3, Third illustration non paginated, illus3\n```\n\nequivalent of this nested xml:\n\n```xml\n\u003cc id=\"toc\" label=\"Table of Contents\"\u003e\n    \u003cc id=\"cover\" label=\"Front cover\"/\u003e\n    \u003cc id=\"intro\" label=\"Introduction\" range=\"2-5\"/\u003e\n    \u003cc id=\"r1\" label=\"First chapter\" range=\"6; r1-1; r1-2; 12\"\u003e\n        \u003cc id=\"r1-1\" label=\"First section\" range=\"r1-1-1; r1-1-2; illustration1; illus2\"\u003e\n            \u003cc id=\"r1-1-1\" label=\"First sub-section\" range=\"8-9\"/\u003e\n            \u003cc id=\"r1-1-2\" label=\"Second sub-section\" range=\"9-10\"/\u003e\n            \u003cc id=\"illustration1\" label=\"First illustration non paginated\" range=\"illus1\"/\u003e\n        \u003c/c\u003e\n    \u003c/c\u003e\n    \u003cc id=\"r2\" label=\"Second chapter\" range=\"13\"/\u003e\n    \u003cc id=\"backcover\" label=\"Back cover\"/\u003e\n\u003c/c\u003e\n\u003cc id=\"illustration3\" label=\"Third illustration non paginated\" range=\"illus3\"/\u003e\n```\n\nequivalent of this flat indented xml (not recommended and deprecated):\n\n```xml\n\u003cc id=\"toc\" label=\"Table of Contents\" range=\"cover; intro; r1; r2; backcover\"/\u003e\n    \u003cc id=\"cover\" label=\"Front cover\" range=\"cover\"/\u003e\n    \u003cc id=\"intro\" label=\"Introduction\" range=\"2-5\"/\u003e\n    \u003cc id=\"r1\" label=\"First chapter\" range=\"6; r1-1; r1-2; 12\"/\u003e\n        \u003cc id=\"r1-1\" label=\"First section\" range=\"r1-1-1; r1-1-2; illustration1; illus2\"/\u003e\n            \u003cc id=\"r1-1-1\" label=\"First sub-section\" range=\"8-9\"/\u003e\n            \u003cc id=\"r1-1-2\" label=\"Second sub-section\" range=\"9-10\"/\u003e\n    \u003cc id=\"r2\" label=\"Second chapter\" range=\"13\"/\u003e\n    \u003cc id=\"backcover\" label=\"Back cover\" range=\"backcover\"/\u003e\n\u003cc id=\"illustration1\" label=\"First illustration non paginated\" range=\"illus1\"/\u003e\n\u003cc id=\"illustration3\" label=\"Third illustration non paginated\" range=\"illus3\"/\u003e\n```\n\nto this json output (iiif v2):\n\n```json\n[\n  {\n    \"@id\": \"https://example.org/iiif/book1/range/toc\",\n    \"@type\": \"sc:Range\",\n    \"label\": \"Table of Contents\",\n    \"ranges\": [\n      {\n        \"@id\": \"https://example.org/iiif/book1/range/cover\",\n        \"@type\": \"sc:Range\",\n        \"label\": \"Front cover\",\n        \"ranges\": [\n          {\n            \"@id\": \"https://example.org/iiif/book1/range/cover\",\n            \"@type\": \"sc:Range\",\n            \"label\": \"Front cover\"\n          }\n        ]\n      },\n      {\n        \"@id\": \"https://example.org/iiif/book1/range/intro\",\n        \"@type\": \"sc:Range\",\n        \"label\": \"Introduction\",\n        \"canvases\": [\n          \"https://example.org/iiif/book1/canvas/p2\",\n          \"https://example.org/iiif/book1/canvas/p3\",\n          \"https://example.org/iiif/book1/canvas/p4\",\n          \"https://example.org/iiif/book1/canvas/p5\"\n        ]\n      },\n      {\n        \"@id\": \"https://example.org/iiif/book1/range/r1\",\n        \"@type\": \"sc:Range\",\n        \"label\": \"First chapter\",\n        \"members\": [\n          {\n            \"@id\": \"https://example.org/iiif/book1/canvas/p6\",\n            \"@type\": \"sc:Canvas\",\n            \"label\": \"[6]\"\n          },\n          {\n            \"@id\": \"https://example.org/iiif/book1/range/r1-1\",\n            \"@type\": \"sc:Range\",\n            \"label\": \"First section\",\n            \"members\": [\n              {\n                \"@id\": \"https://example.org/iiif/book1/range/r1-1-1\",\n                \"@type\": \"sc:Range\",\n                \"label\": \"First sub-section\",\n                \"canvases\": [\n                  \"https://example.org/iiif/book1/canvas/p8\",\n                  \"https://example.org/iiif/book1/canvas/p9\"\n                ]\n              },\n              {\n                \"@id\": \"https://example.org/iiif/book1/range/r1-1-2\",\n                \"@type\": \"sc:Range\",\n                \"label\": \"Second sub-section\",\n                \"canvases\": [\n                  \"https://example.org/iiif/book1/canvas/p9\",\n                  \"https://example.org/iiif/book1/canvas/p10\"\n                ]\n              },\n              {\n                \"@id\": \"https://example.org/iiif/book1/range/illustration1\",\n                \"@type\": \"sc:Range\",\n                \"label\": \"First illustration non paginated\",\n                \"canvases\": [\n                  \"https://example.org/iiif/book1/canvas/illus1\"\n                ]\n              },\n              {\n                \"@id\": \"https://example.org/iiif/book1/canvas/illus2\",\n                \"@type\": \"sc:Canvas\",\n                \"label\": \"illus2\"\n              }\n            ]\n          },\n          {\n            \"@id\": \"https://example.org/iiif/book1/canvas/r1-2\",\n            \"@type\": \"sc:Canvas\",\n            \"label\": \"r1-2\"\n          },\n          {\n            \"@id\": \"https://example.org/iiif/book1/canvas/p12\",\n            \"@type\": \"sc:Canvas\",\n            \"label\": \"[12]\"\n          }\n        ]\n      },\n      {\n        \"@id\": \"https://example.org/iiif/book1/range/r2\",\n        \"@type\": \"sc:Range\",\n        \"label\": \"Second chapter\",\n        \"canvases\": [\n          \"https://example.org/iiif/book1/canvas/p13\"\n        ]\n      },\n      {\n        \"@id\": \"https://example.org/iiif/book1/range/backcover\",\n        \"@type\": \"sc:Range\",\n        \"label\": \"Back cover\",\n        \"canvases\": [\n          \"https://example.org/iiif/book1/canvas/backcover\"\n        ]\n      }\n    ]\n  },\n  {\n    \"@id\": \"https://example.org/iiif/book1/range/illustration3\",\n    \"@type\": \"sc:Range\",\n    \"label\": \"Third illustration non paginated\",\n    \"canvases\": [\n      \"https://example.org/iiif/book1/canvas/illus3\"\n    ]\n  }\n]\n```\n\nor to this json output (iiif v3), a little more verbose:\n\n```json\n[\n  {\n    \"id\": \"https://example.org/iiif/book1/range/rstructure1\",\n    \"type\": \"Range\",\n    \"label\": { \"none\": [ \"Content\" ] },\n    \"items\": [\n      {\n        \"id\": \"https://example.org/iiif/book1/range/toc\",\n        \"type\": \"Range\",\n        \"label\": { \"none\": [ \"Table of Contents\" ] },\n        \"items\": [\n          {\n            \"id\": \"https://example.org/iiif/book1/range/cover\",\n            \"type\": \"Range\",\n            \"label\": { \"none\": [ \"Front cover\" ] },\n            \"items\": [\n              { \"id\": \"https://example.org/iiif/book1/canvas/cover\", \"type\": \"Canvas\" }\n            ]\n          },\n          {\n            \"id\": \"https://example.org/iiif/book1/range/intro\",\n            \"type\": \"Range\",\n            \"label\": { \"none\": [ \"Introduction\" ] },\n            \"items\": [\n              { \"id\": \"https://example.org/iiif/book1/canvas/p2\", \"type\": \"Canvas\" },\n              { \"id\": \"https://example.org/iiif/book1/canvas/p3\", \"type\": \"Canvas\" },\n              { \"id\": \"https://example.org/iiif/book1/canvas/p4\", \"type\": \"Canvas\" },\n              { \"id\": \"https://example.org/iiif/book1/canvas/p5\", \"type\": \"Canvas\" }\n            ]\n          },\n          {\n            \"id\": \"https://example.org/iiif/book1/range/r1\",\n            \"type\": \"Range\",\n            \"label\": { \"none\": [ \"First chapter\" ] },\n            \"items\": [\n              { \"id\": \"https://example.org/iiif/book1/canvas/p6\", \"type\": \"Canvas\" },\n              {\n                \"id\": \"https://example.org/iiif/book1/range/r1-1\",\n                \"type\": \"Range\",\n                \"label\": { \"none\": [ \"First section\" ] },\n                \"items\": [\n                  {\n                    \"id\": \"https://example.org/iiif/book1/range/r1-1-1\",\n                    \"type\": \"Range\",\n                    \"label\": { \"none\": [ \"First sub-section\" ] },\n                    \"items\": [\n                      { \"id\": \"https://example.org/iiif/book1/canvas/p8\", \"type\": \"Canvas\" },\n                      { \"id\": \"https://example.org/iiif/book1/canvas/p9\", \"type\": \"Canvas\" }\n                    ]\n                  },\n                  {\n                    \"id\": \"https://example.org/iiif/book1/range/r1-1-2\",\n                    \"type\": \"Range\",\n                    \"label\": { \"none\": [ \"Second sub-section\" ] },\n                    \"items\": [\n                      { \"id\": \"https://example.org/iiif/book1/canvas/p9\", \"type\": \"Canvas\" },\n                      { \"id\": \"https://example.org/iiif/book1/canvas/p10\", \"type\": \"Canvas\" }\n                    ]\n                  },\n                  {\n                    \"id\": \"https://example.org/iiif/book1/range/illustration1\",\n                    \"type\": \"Range\",\n                    \"label\": { \"none\": [ \"First illustration non paginated\" ] },\n                    \"items\": [\n                      { \"id\": \"https://example.org/iiif/book1/canvas/illus1\", \"type\": \"Canvas\" }\n                    ]\n                  },\n                  { \"id\": \"https://example.org/iiif/book1/canvas/illus2\", \"type\": \"Canvas\" }\n                ]\n              },\n              { \"id\": \"https://example.org/iiif/book1/canvas/r1-2\", \"type\": \"Canvas\" },\n              { \"id\": \"https://example.org/iiif/book1/canvas/p12\", \"type\": \"Canvas\" }\n            ]\n          },\n          {\n            \"id\": \"https://example.org/iiif/book1/range/r2\",\n            \"type\": \"Range\",\n            \"label\": { \"none\": [ \"Second chapter\" ] },\n            \"items\": [\n              { \"id\": \"https://example.org/iiif/book1/canvas/p13\", \"type\": \"Canvas\" }\n            ]\n          },\n          {\n            \"id\": \"https://example.org/iiif/book1/range/backcover\",\n            \"type\": \"Range\",\n            \"label\": { \"none\": [ \"Back cover\" ] },\n            \"items\": [\n              { \"id\": \"https://example.org/iiif/book1/canvas/backcover\", \"type\": \"Canvas\" }\n            ]\n          }\n        ]\n      },\n      {\n        \"id\": \"https://example.org/iiif/book1/range/illustration3\",\n        \"type\": \"Range\",\n        \"label\": { \"none\": [ \"Third illustration non paginated\" ] },\n        \"items\": [\n          { \"id\": \"https://example.org/iiif/book1/canvas/illus3\", \"type\": \"Canvas\" }\n        ]\n      }\n    ]\n  }\n]\n```\n\nNotes to understand the conversion and to fix issues from the literal data:\n\n- The named canvases may not be supported, so you may have to avoid them.\n- The indentation is not required, but it simplifies literal visualisation.\n- The lines must be ordered as a table of contents or as an index, so the parser\n  can understand the structure.\n- Each line will be a range, even if it refers to a single canvas, because only\n  ranges are displayed to the user. So the cover, the second chapter and the\n  illustration are ranges with a single item that is a canvas.\n- There are some missing pages (not scanned or lost).\n- The names have some meanings (\"r1-1-1\"), but it's not needed.\n- The cover refers to itself, so it's a named canvas within a range. This is not\n  possible for iiif v2, so wrap it with double quotes.\n- The backcover has a canvas wrapped by double quotes to force it to be a canvas.\n- The sub-sections share one page (9).\n- The index \"r1-2\" in the indexes of the first chapter is missing, so it is\n  added as a named canvas.\n- The index \"12\" is a canvas, so it is added to the list, and, as a canvas,\n  won't be displayed in the index of the viewer.\n- The \"illustration1\" has a named canvas, since \"illus1\" is not a range.\n- The index \"illustration1\" of the first section is a range, but it is missing\n  from the ordered list below the first section. So it is added directly at the\n  right place and removed from the end.\n- The illustration3 is not used as a range, so it is a root, so there is more\n  than one root, so a range is added to wrap all the structure.\n\nTake care of nested structures: items must not belong to themselves, else they\nwill be managed as canvases.\n\nOf course, if the literal structure is well-formed, you don't have to consider\nthese fixes.\n\nOtherwise, in IIIF v3, multiple structures are appended when there are multiple\nvalues in the item. For example, in a newspaper, it's possible to have a\nstructure by page like a common table of contents, and a structure by articles.\nIndeed, an article can be written on multiple and non-sequential pages, and a\npage can contain many articles, illustrations, ads, etc. When there are multiple\nstructures, the names of the ranges should have the same meaning between each\nstructure (for example the index \"cover\" should be the front cover in all\nstructures), because their uri will be the same.\n\nMore info about structures for [IIIF presentation 2.1] and [IIIF presentation 3.0].\n\n### Customize data of manifests\n\nThe module creates manifests with all the metadata of each record. A lot of\nconfig options allows to define each part of them. If it's not enough, the event\n`iiifserver.manifest` can be used to modify the exposed data of a manifest for\nitems, collections, collection lists (search results) and media (`info.json`).\nSo, it is possible, for example, to modify the citation, to remove or to add\nsome metadata or to change the thumbnail.\n\nNote: with a collection list, the parameter `resource` is an array of resources.\n\nAll the combinations are possible: external manifest for items, iiif image for\nexternal medias, a local standard media file with module Image Server.\n\n### Options for writing urls\n\nDepending on infrastructure, proxy, etc., some options allow to modify the urls\nused.\n\n- Encode slash in identifiers: When enabled, forward slashes `/` in identifiers\n  are encoded as `%2F` in IIIF urls. This is required by the IIIF Image API\n  specification and needed when using identifiers that contain slashes (like Ark).\n  The option is auto-detected on config save based on the server capabilities.\n\n\nRoutes and urls\n---------------\n\nAll routes of the IIIF server are defined in `config/module.config.php`.\nThey follow the recommandations of the [IIIF specifications].\n\nTo view the json-ld manifests created for each resources of Omeka S, simply try\nthese urls (replace :id by a true id):\n\n- https://example.org/iiif/collection/:id for item sets;\n- https://example.org/iiif/:id/manifest for items;\n- https://example.org/iiif/set?id[]=:id,:id[]=:id,id[]=:id,id[]=:id…;\n- https://example.org/iiif/set/:id,:id,:id,:id is supported too;\n\n- https://example.org/iiif/:id/info.json for images files;\n- https://example.org/iiif/:id/:region/:size/:rotation/:quality.:format for\n  images, for example: https://example.org/iiif/1/full/full/270/gray.png;\n- https://example.org/iiif/:id/info.json for other files;\n- https://example.org/iiif/:id.:format for the files.\n\nBy default, ids are the internal ids of Omeka S, but it is recommended to use\nyour own single and permanent identifiers that don’t depend on an internal\npointer in a database. The term `Dublin Core Identifier` is designed for that\nand a record can have multiple single identifiers. There are many possibilities:\nnamed number like in a library or a museum, isbn for books, or random id like\nwith ark, noid, doi, etc. They can be displayed in the public url with the\nmodules [Ark] and/or [Clean Url].\n\n\n3D models\n---------\n\n3D models are not supported by the IIIF standard, that manages only images (IIIF v2),\nand audio and video files (IIIF v3). Nevertheless, it is possible to create\nmanifests that follows the standard except the format of the file, like an\nextended version of the standard. Only the widget [Universal Viewer] supports it\nnatively since version 2.3, via the [three.js] library. It is called \"ixif\".\n\nThe other viewers integrated via modules ([Diva] and [Mirador]) in Omeka don’t\nsupport 3D.\n\nFor more info about support of 3D models and possible other requirements, see\nthe module [Three JS Model viewer].\n\n\nTODO / Bugs\n-----------\n\n- [x] Implements ArrayObject to all classes to simplify events.\n- [ ] Implements ArrayObject to all classes to simplify events for Iiif v2.\n- [x] Use only arrays, not standard objects.\n- [ ] Type of manifest: Use a list of classes or templates to determine the 3D files.\n- [x] Type of manifest: Include pdf as rendering.\n- [ ] Structure: Clarify names of canvases and referenced canvas in the table of contents and list of items.\n- [ ] Structure: Implements recursive ranges in structures for IIIF v2.\n- [ ] Structure: Normalize the format of the structure. Currently, csv and xml are supported. Add json.\n- [ ] Structure: Convert structure v3 to v2 and vice-versa.\n- [x] Structure: Fully support alphanumeric name for canvas id.\n- [ ] Structure: Support translation of structure (use the language of the value?).\n- [x] Structure: Full support of named canvases.\n- [ ] Use the option \"no storage\" for url of a media for external server.\n- [ ] Manage url prefix.\n- [x] When a item set contains non image items, the left panel with the index is displayed only when the first item contains an image (UV). Fixed in recent versions of UV.\n- [ ] Job to update data of [IIIF Image].\n- [x] Create a way to cache big iiif manifests (useless for image info.json).\n- [ ] Include thumbnails in canvas to avoid fetching info.json (so cache whole manifest).\n- [x] Check if multiple roots is working for structures in iiif v3. (yes, as multiple structures).\n- [x] Store dimensions on item/media save.\n- [ ] Create a plugin MediaData that will merge MediaDimension, ImageSize, and allows to get media type.\n- [ ] Clarify option for home page with \"default site\", that may not be a site of the item.\n- [ ] Podcast on iiif v2.\n\nSee module [Image Server].\n\n\nWarning\n-------\n\nUse it at your own risk.\n\nIt’s always recommended to backup your files and your databases and to check\nyour archives regularly so you can roll back if needed.\n\n\nTroubleshooting\n---------------\n\nSee online issues on the [module issues] page on GitLab.\n\n\nLicense\n-------\n\nThis module is published under the [CeCILL v2.1] license, compatible with\n[GNU/GPL] and approved by [FSF] and [OSI].\n\nIn consideration of access to the source code and the rights to copy, modify and\nredistribute granted by the license, users are provided only with a limited\nwarranty and the software’s author, the holder of the economic rights, and the\nsuccessive licensors only have limited liability.\n\nIn this respect, the risks associated with loading, using, modifying and/or\ndeveloping or reproducing the software by the user are brought to the user’s\nattention, given its Free Software status, which may make it complicated to use,\nwith the result that its use is reserved for developers and experienced\nprofessionals having in-depth computer knowledge. Users are therefore encouraged\nto load and test the suitability of the software as regards their requirements\nin conditions enabling the security of their systems and/or data to be ensured\nand, more generally, to use and operate it in the same conditions of security.\n\nThis Agreement may be freely reproduced and published, provided it is not\naltered, and that no provisions are either added or removed herefrom.\n\n\nCopyright\n---------\n\n* Copyright Daniel Berthereau, 2015-2026 (see [Daniel-KM])\n* Copyright BibLibre, 2016-2017\n* Copyright Régis Robineau, 2019 (see [regisrob])\n* Copyright Satoru Nakamura, 2021 (see [nakamura196])\n\nFirst version of this plugin was built for the [Bibliothèque patrimoniale] of\n[Mines ParisTech]. This [Omeka S] module is a rewrite of the [Universal Viewer plugin for Omeka]\nby [BibLibre] with the same features. Then it was separated into three modules:\nthe IIIF server, the [Image Server] and the viewer [Universal Viewer].\nThis viewer integrates the tiler [Zoomify] that was used the plugin [OpenLayers Zoom]\nfor [Omeka Classic] and another tiler to support the [Deep Zoom Image] tile\nformat.\n\n\n[IIIF Server]: https://gitlab.com/Daniel-KM/Omeka-S-module-IiifServer\n[Omeka S]: https://omeka.org/s\n[Image Server]: https://gitlab.com/Daniel-KM/Omeka-S-module-ImageServer\n[International Image Interoperability Framework]: http://iiif.io\n[IIIF specifications]: http://iiif.io/api/\n[Cantaloupe]: https://cantaloupe-project.github.io\n[IIP Image]: https://iipimage.sourceforge.io\n[many others]: https://iiif.io/get-started/image-servers\n[OpenSeadragon]: https://openseadragon.github.io\n[Universal Viewer]: https://gitlab.com/Daniel-KM/Omeka-S-module-UniversalViewer\n[Mirador]: https://gitlab.com/Daniel-KM/Omeka-S-module-Mirador\n[Diva]: https://gitlab.com/Daniel-KM/Omeka-S-module-Diva\n[Three JS Model viewer]: https://gitlab.com/Daniel-KM/Omeka-S-module-ModelViewer\n[Omeka Classic]: https://omeka.org\n[Iiif Search]: https://github.com/bubdxm/Omeka-S-module-IiifSearch\n[GD]: https://secure.php.net/manual/en/book.image.php\n[Imagick]: https://php.net/manual/en/book.imagick.php\n[ImageMagick]: https://www.imagemagick.org/\n[installing a module]: https://omeka.org/s/docs/user-manual/modules\n[Common]: https://gitlab.com/Daniel-KM/Omeka-S-module-Common\n[IiifServer.zip]: https://gitlab.com/Daniel-KM/Omeka-S-module-IiifServer/-/releases\n[structures]: https://iiif.io/api/presentation/3.0/#54-range\n[DataType Rdf]: https://gitlab.com/Daniel-KM/Omeka-S-module-DataTypeRdf\n[fix #omeka/omeka-s/1714]: https://github.com/omeka/omeka-s/pull/1714\n[Guest]: https://gitlab.com/Daniel-KM/Omeka-S-module-Guest\n[it doesn't allow the url encoded `/`]: https://stackoverflow.com/questions/13834007/url-with-encoded-slashes-goes-to-404/13839424#13839424\n[url encoded slashes]: https://iiif.io/api/image/3.0/#9-uri-encoding-and-decoding\n[CORS]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS\n[aws documentation]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/cors.html\n[official list]: https://github.com/IIIF/awesome-iiif/#image-servers\n[internal image server]: #image-server\n[Universal Viewer]: https://gitlab.com/Daniel-KM/Omeka-S-module-UniversalViewer\n[IIIF presentation 2.1]: https://iiif.io/api/presentation/2.1/#range\n[IIIF presentation 3.0]: https://iiif.io/api/presentation/3.0/#54-range\n[Ark]: https://gitlab.com/Daniel-KM/omeka-s-module-Ark\n[Clean Url]: https://gitlab.com/Daniel-KM/omeka-s-module-CleanUrl\n[Collection Tree]: https://gitlab.com/Daniel-KM/Omeka-S-module-CollectionTree\n[Deep Zoom]: https://msdn.microsoft.com/en-us/library/cc645022(v=vs.95).aspx\n[Deep Zoom Image]: https://msdn.microsoft.com/en-us/library/cc645022(v=vs.95).aspx\n[Zoomify]: http://www.zoomify.com/\n[OpenLayers]: https://openlayers.org/\n[three.js]: https://threejs.org\n[Deepzoom library]: https://gitlab.com/Daniel-KM/LibraryDeepzoom\n[Zoomify library]: https://gitlab.com/Daniel-KM/LibraryZoomify\n[Deepzoom]: https://github.com/jeremytubbs/deepzoom\n[#6]: https://gitlab.com/Daniel-KM/Omeka-S-module-IiifServer/-/issues/6\n[IIIF Image]: https://omeka.org/s/docs/user-manual/content/items/#media\n[module issues]: https://gitlab.com/Daniel-KM/Omeka-S-module-IiifServer/-/issues\n[CeCILL v2.1]: https://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html\n[GNU/GPL]: https://www.gnu.org/licenses/gpl-3.0.html\n[FSF]: https://www.fsf.org\n[OSI]: http://opensource.org\n[Bibliothèque patrimoniale]: https://patrimoine.mines-paristech.fr\n[Mines ParisTech]: http://mines-paristech.fr\n[Universal Viewer plugin for Omeka]: https://gitlab.com/Daniel-KM/Omeka-plugin-UniversalViewer\n[BibLibre]: https://github.com/biblibre\n[OpenLayers Zoom]: https://gitlab.com/Daniel-KM/Omeka-S-module-OpenLayersZoom\n[nakamura196]: https://github.com/nakamura196\n[Daniel-KM]: https://gitlab.com/Daniel-KM \"Daniel Berthereau\"\n[GitLab]: https://gitlab.com/Daniel-KM\n[Daniel-KM]: https://gitlab.com/Daniel-KM \"Daniel Berthereau\"\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaniel-km%2Fomeka-s-module-iiifserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaniel-km%2Fomeka-s-module-iiifserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaniel-km%2Fomeka-s-module-iiifserver/lists"}