# IIIFManifest
[![Build Status](https://travis-ci.org/samvera-labs/iiif_manifest.svg?branch=master)](https://travis-ci.org/projecthydra-labs/iiif_manifest)

IIIF http://iiif.io/ defines an API for presenting related images in a viewer. This transforms Hydra::Works objects into that format usable by players such as http://universalviewer.io/

## Usage

Your application ***must*** have an object that implements `#file_set_presenters` and `#work_presenters`.  The former method should return as set of leaf nodes and the later any interstitial nodes. If none are found an empty array should be returned.

Additionally, it ***must*** have a `#description` method that returns a string.

Additionally it ***should*** implement `#manifest_url` that shows where the manifest can be found.

Additionally it ***should*** implement `#manifest_metadata` to provide an array containing hashes of metadata Label/Value pairs.

Additionally it ***may*** implement `#search_service` to contain the url for a IIIF search api compliant search endpoint and `#autocomplete_service` to contain the url for a IIIF search api compliant autocomplete endpoint. Please note, the autocomplete service is embedded within the search service description so if an autocomplete_service is supplied without a search_service it will be ignored. The IIIF `profile` added to the service descriptions is version 0 as this is the version supported by the current version of Universal Viewer. Only include a search_service within the manifest if your application has implemented a IIIF search service at the endpoint specified in the manifest.

Additionally it ***may*** implement `#sequence_rendering` to contain an array of hashes for file downloads to be offered at sequences level. Each hash must contain "@id", "format" (mime type) and "label" (eg. `{ "@id" => "download url", "format" => "application/pdf", "label" => "user friendly label" }`).

Finally, it ***may*** implement `ranges`, which returns an array of objects which
represent a table of contents or similar structure, each of which responds to
`label`, `ranges`, and `file_set_presenters`.

For example:

 ```ruby
  class Book
    def initialize(id, pages = [])
      @id = id
      @pages = pages
    end

    def file_set_presenters
      @pages
    end

    def work_presenters
      []
    end

    def manifest_url
      "http://test.host/books/#{@id}/manifest"
    end

    def description
      'a brief description'
    end

    def manifest_metadata
          [
            { "label" => "Title", "value" => "Title of the Item" },
            { "label" => "Creator", "value" => "Morrissey, Stephen Patrick" }
          ]
    end

    def search_service
      "http://test.host/books/#{@id}/search"
    end

    def autocomplete_service
      "http://test.host/books/#{@id}/autocomplete"
    end

    def sequence_rendering
      [{"@id" => "http://test.host/file_set/id/download", "format" => "application/pdf", "label" => "Download"}]
    end

    def ranges
      [
        ManifestRange.new(
          label: "Table of Contents",
          ranges: [
            ManifestRange.new(
              label: "Chapter 1",
              file_set_presenters: @pages
            )
          ]
        )
      ]
    end
  end

  class ManifestRange
    attr_reader :label, :ranges, :file_set_presenters
    def initialize(label:, ranges: [], file_set_presenters: [])
      @label = label
      @ranges = ranges
      @file_set_presenters = file_set_presenters
    end
  end
```

The class that represents the leaf nodes, must implement `#id`. It must also implement `#display_image` which returns an instance of `IIIFManifest::DisplayImage`

```ruby
  class Page
    def initialize(id)
      @id = id
    end

    def id
      @id
    end

    def display_image
      IIIFManifest::DisplayImage.new(id,
                                     width: 100,
                                     height: 100,
                                     format: "image/jpeg",
                                     iiif_endpoint: endpoint
                                     )
    end

    private

      def endpoint
        IIIFManifest::IIIFEndpoint.new("http://test.host/images/#{id}",
                                       profile: "http://iiif.io/api/image/2/level2.json")
      end
  end
```

Then you can produce the manifest on the book object like this:

```ruby
  book = Book.new('book-77',[Page.new('page-99')])
  IIIFManifest::ManifestFactory.new(book).to_h.to_json
```

## Presentation 3.0 (Alpha)

Provisional support for the [3.0 alpha version of the IIIF presentation api spec](https://iiif.io/api/presentation/3.0/) has been added with a focus on audiovisual content.  The [change log](https://iiif.io/api/presentation/3.0/change-log/) lists the changes to the specification.

The presentation 3.0 support has been contained to the `V3` namespace.  Version 2.0 manifests are still be built using `IIIFManifest::ManifestFactory` while version 3.0 manifests can now be built using `IIIFManifest::V3::ManifestFactory`.

```ruby
  book = Book.new('book-77',[Page.new('page-99')])
  IIIFManifest::V3::ManifestFactory.new(book).to_h.to_json
```

### Notable changes for Presentation 3.0
- Presenters must still define `#description` but it is now serialized as `summary`. (https://iiif.io/api/presentation/3.0/change-log/#126-rename-description-to-summary)
- All textual strings, including metadata labels and values, are now serialized as language maps and may be provided as a hash with language code keys with string values.  Values not provided in this format are automatically converted so no change to `#description`, `#manifest_metadata`, range labels, or other fields are required. (https://iiif.io/api/presentation/3.0/change-log/#133-use-language-map-pattern-for-label-value-summary)
- Presenters ***may*** implement `#homepage` to contain a hash for linking back to a repository webpage for this manifest. The hash must contain "id", "format" (mime type), "type", and "label" (eg. `{ "id" => "repository url", "format" => "text/html", "type" => "Text", "label" => { "en": ["View in repository"] }`).
- File set presenters may target a fragment of its content by providing `#media_fragment` which will be appended to its `id`.
- Range objects may now implement `#items` instead of `#ranges` and `#file_set_presenters` to allow for interleaving these objects.  `#items` is not required and existing range objects should continue to work.
- File set presenters may provide `#display_content` which should return an instance of `IIIFManifest::V3::DisplayContent` (or an array of instances in the case of a user `Choice`).  `#display_image` is no longer required but will still work if provided.
- DisplayContent may provide `#auth_service` which should return a hash containing a IIIF Authentication service definition (https://iiif.io/api/auth/1.0/) that will be included on the content resource.

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/samvera-labs/iiif_manifest. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.