iiif_manifest 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +27 -8
- data/README.md +38 -18
- data/lib/iiif_manifest/configuration.rb +68 -0
- data/lib/iiif_manifest/display_image.rb +4 -2
- data/lib/iiif_manifest/manifest_builder/iiif_service.rb +1 -1
- data/lib/iiif_manifest/manifest_builder/record_property_builder.rb +4 -2
- data/lib/iiif_manifest/v3/display_content.rb +10 -10
- data/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb +15 -2
- data/lib/iiif_manifest/v3/manifest_builder/content_builder.rb +5 -4
- data/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb +32 -3
- data/lib/iiif_manifest/v3/manifest_builder/image_service_builder.rb +2 -2
- data/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb +36 -19
- data/lib/iiif_manifest/v3/manifest_builder/thumbnail_builder.rb +66 -0
- data/lib/iiif_manifest/v3/manifest_builder.rb +2 -1
- data/lib/iiif_manifest/v3/manifest_service_locator.rb +16 -2
- data/lib/iiif_manifest/version.rb +1 -1
- data/lib/iiif_manifest.rb +43 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8dd7b48693245057fa3e99f6b3f434da255e4f1e7f26412e1b56937a73dace4
|
4
|
+
data.tar.gz: 6b719a4f0b94ed7347420d5c940d8b2244195a201bd2415e9842ee7e55c8afab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f38cc5f1426ea385f128351a3189989cc31a1138faa693b4938f71a8fca3964da01bd1d17c3eff5501060c9483a6a6a95874feea87df1f7d9a35a55e4d3aa93
|
7
|
+
data.tar.gz: fb614960e6bcfd54792d061a783cb7f8719a5dddc1d6060b3ba8173136700537715ab2cc48234602c3788fef95d5c2ca8ffd9a505a4ae09c4f6c4fea54554a15
|
data/.circleci/config.yml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
version: 2.1
|
3
3
|
orbs:
|
4
|
-
samvera: samvera/circleci-orb@0
|
4
|
+
samvera: samvera/circleci-orb@1.0
|
5
5
|
|
6
6
|
jobs:
|
7
7
|
test:
|
@@ -10,7 +10,7 @@ jobs:
|
|
10
10
|
type: string
|
11
11
|
bundler_version:
|
12
12
|
type: string
|
13
|
-
default: 2.
|
13
|
+
default: 2.3.10
|
14
14
|
|
15
15
|
executor:
|
16
16
|
name: 'samvera/ruby'
|
@@ -36,16 +36,35 @@ workflows:
|
|
36
36
|
jobs:
|
37
37
|
- test:
|
38
38
|
name: "ruby3-0"
|
39
|
-
ruby_version: "3.0.
|
39
|
+
ruby_version: "3.0.3"
|
40
40
|
- test:
|
41
41
|
name: "ruby2-7"
|
42
|
-
ruby_version: "2.7.
|
42
|
+
ruby_version: "2.7.5"
|
43
43
|
- test:
|
44
44
|
name: "ruby2-6"
|
45
|
-
ruby_version: "2.6.
|
45
|
+
ruby_version: "2.6.9"
|
46
46
|
- test:
|
47
47
|
name: "ruby2-5"
|
48
|
-
ruby_version: "2.5.
|
48
|
+
ruby_version: "2.5.9"
|
49
|
+
|
50
|
+
nightly:
|
51
|
+
triggers:
|
52
|
+
- schedule:
|
53
|
+
cron: "0 0 * * *"
|
54
|
+
filters:
|
55
|
+
branches:
|
56
|
+
only:
|
57
|
+
- main
|
58
|
+
jobs:
|
49
59
|
- test:
|
50
|
-
name: "
|
51
|
-
ruby_version: "
|
60
|
+
name: "ruby3-0"
|
61
|
+
ruby_version: "3.0.3"
|
62
|
+
- test:
|
63
|
+
name: "ruby2-7"
|
64
|
+
ruby_version: "2.7.5"
|
65
|
+
- test:
|
66
|
+
name: "ruby2-6"
|
67
|
+
ruby_version: "2.6.9"
|
68
|
+
- test:
|
69
|
+
name: "ruby2-5"
|
70
|
+
ruby_version: "2.5.9"
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@ Code: [![CircleCI](https://circleci.com/gh/samvera/iiif_manifest.svg?style=svg)]
|
|
4
4
|
|
5
5
|
Docs: [![Contribution Guidelines](http://img.shields.io/badge/CONTRIBUTING-Guidelines-blue.svg)](./CONTRIBUTING.md) [![Apache 2.0 License](http://img.shields.io/badge/APACHE2-license-blue.svg)](./LICENSE)
|
6
6
|
|
7
|
-
|
7
|
+
Join the conversation on slack: [![Slack Status](https://raw.githubusercontent.com/samvera/maintenance/main/assets/slack_icon.png)](http://slack.samvera.org/)
|
8
8
|
|
9
9
|
# What is IIIFManifest
|
10
10
|
|
@@ -144,7 +144,7 @@ Then you can produce the manifest on the book object like this:
|
|
144
144
|
|
145
145
|
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.
|
146
146
|
|
147
|
-
The presentation 3.0 support has been contained to the `V3` namespace. Version 2.0 manifests are still
|
147
|
+
The presentation 3.0 support has been contained to the `V3` namespace. Version 2.0 manifests are still being built using `IIIFManifest::ManifestFactory` while version 3.0 manifests can now be built using `IIIFManifest::V3::ManifestFactory`.
|
148
148
|
|
149
149
|
```ruby
|
150
150
|
book = Book.new('book-77',[Page.new('page-99')])
|
@@ -161,30 +161,50 @@ The presentation 3.0 support has been contained to the `V3` namespace. Version 2
|
|
161
161
|
- 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.
|
162
162
|
- 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.
|
163
163
|
|
164
|
-
|
164
|
+
## Configuration
|
165
165
|
|
166
|
-
|
166
|
+
The `label`, `rights`, `homepage`, `description` (V2 only), and `summary` (V3 only) properties can be configured to pull its information from different attributes.
|
167
167
|
|
168
|
-
|
168
|
+
**NOTE:** In the V2 manifest, `label` and `description` is expected to be a string so if the model's attribute is multivalued, only the first value would be used.
|
169
169
|
|
170
|
-
|
170
|
+
To enable this, add the following code to a config file at `config/initializers/iiif_manifest_config.rb` in your application.
|
171
|
+
```ruby
|
172
|
+
# Example: use the default configuration but amend the summary property
|
173
|
+
IIIFManifest.config do |config|
|
174
|
+
config.manifest_property_to_record_method_name_map.merge!(summary: :abstract, rights: :license)
|
175
|
+
end
|
176
|
+
```
|
177
|
+
In the above example of a V3 manifest (since it is a `summary` instead of `description`), the `summary` property will be using the model's `#abstract` attribute value instead of the default `#description`. The `rights` property will use the model's `#license` attribute instead of the default `#rights_statement`. All other configurable properties will use their defaults.
|
171
178
|
|
172
|
-
|
179
|
+
```ruby
|
180
|
+
# Example: use this configuration to set the max edge length of thumbnails, default is 200
|
181
|
+
IIIFManifest.confg do |config|
|
182
|
+
config.max_edge_for_thumbnail = 100
|
183
|
+
end
|
184
|
+
```
|
185
|
+
Thumbnails have been added for version 3 manifests because [Universal Viewer](https://github.com/UniversalViewer/universalviewer/issues/102) currently require them to be explicitly set otherwise they would not show up. The above example is used to configure what the default size for the thumbnails would be.
|
173
186
|
|
174
|
-
|
187
|
+
```ruby
|
188
|
+
# Example: use this configuration to disable thumbnails to show up by default on the manifest level (version 3 only)
|
189
|
+
IIIFManifest.confg do |config|
|
190
|
+
config.manifest_thumbnail = false
|
191
|
+
end
|
192
|
+
```
|
193
|
+
According to the Presentation API 3.0 [specifications](https://iiif.io/api/presentation/3.0/#thumbnail):
|
194
|
+
> A Manifest *SHOULD* have the `thumbnail` property with at least one item.
|
195
|
+
|
196
|
+
The above configuration allows you to disable that if desired since it is not a *MUST*.
|
197
|
+
|
198
|
+
|
199
|
+
# Development
|
175
200
|
|
176
|
-
|
177
|
-
2. Increase the version number in `lib/iiif_manifest/version.rb`
|
178
|
-
3. Increase the same version number in `.github_changelog_generator`
|
179
|
-
4. Update `CHANGELOG.md` by running this command:
|
201
|
+
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.
|
180
202
|
|
181
|
-
|
182
|
-
github_changelog_generator --user samvera --project iiif_manifest --token YOUR_GITHUB_TOKEN_HERE
|
183
|
-
```
|
203
|
+
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).
|
184
204
|
|
185
|
-
|
205
|
+
## Contributing
|
186
206
|
|
187
|
-
|
207
|
+
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.
|
188
208
|
|
189
209
|
## Help
|
190
210
|
|
@@ -194,4 +214,4 @@ The Samvera community is here to help. Please see our [support guide](./SUPPORT.
|
|
194
214
|
|
195
215
|
This software has been developed by and is brought to you by the Samvera community. Learn more at the [Samvera website](http://samvera.org/).
|
196
216
|
|
197
|
-
![Samvera Logo](https://
|
217
|
+
![Samvera Logo](https://raw.githubusercontent.com/samvera/maintenance/main/assets/samvera_tree.png)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module IIIFManifest
|
4
|
+
##
|
5
|
+
# Handles configuration for the IIIFManifest gem
|
6
|
+
#
|
7
|
+
# @see IIIFManifest.config
|
8
|
+
class Configuration
|
9
|
+
DEFAULT_MANIFEST_PROPERTY_TO_RECORD_METHOD_NAME_MAP = {
|
10
|
+
summary: :description, # for V3 manifests
|
11
|
+
description: :description, # for V2 manifests
|
12
|
+
label: :to_s,
|
13
|
+
rights: :rights_statement,
|
14
|
+
homepage: :homepage
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
##
|
18
|
+
# @!attribute [w] manifest_property_to_record_method_name_map
|
19
|
+
# @param value [Hash<Symbol, Symbol>]
|
20
|
+
# @return [Hash<Symbol, Symbol>]
|
21
|
+
attr_writer :manifest_property_to_record_method_name_map
|
22
|
+
# Used to map a record's public method to a manifest property.
|
23
|
+
# @see manifest_value_for
|
24
|
+
# @see DEFAULT_MANIFEST_PROPERTY_TO_RECORD_METHOD_NAME_MAP DEFAULT_MANIFEST_PROPERTY_TO_RECORD_METHOD_NAME_MAP
|
25
|
+
# are the default values
|
26
|
+
# @return [Hash<Symbol, Symbol>]
|
27
|
+
def manifest_property_to_record_method_name_map
|
28
|
+
# the constant is frozen but we need to be able to configure something unfrozen
|
29
|
+
@manifest_property_to_record_method_name_map ||= DEFAULT_MANIFEST_PROPERTY_TO_RECORD_METHOD_NAME_MAP.dup
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# @param value [Integer]
|
34
|
+
attr_writer :max_edge_for_thumbnail
|
35
|
+
# Used to set max edge length for thumbnail generation.
|
36
|
+
# @return [Integer]
|
37
|
+
def max_edge_for_thumbnail
|
38
|
+
return @max_edge_for_thumbnail unless @max_edge_for_thumbnail.nil?
|
39
|
+
@max_edge_for_thumbnail = 200
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_writer :manifest_thumbnail
|
43
|
+
# Used to configure whether or not to show the manifest thumbnail property.
|
44
|
+
# @return [Boolean]
|
45
|
+
def manifest_thumbnail
|
46
|
+
return @manifest_thumbnail unless @manifest_thumbnail.nil?
|
47
|
+
@manifest_thumbnail = true
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# @api private
|
52
|
+
# @param record [Object] has the value for the :property we want to set
|
53
|
+
# @param property [Symbol] IIIF manifest property
|
54
|
+
# @return [NilClass] the record does not have a value for the given property
|
55
|
+
# @return [#to_s] the record has a value that is "present"
|
56
|
+
def manifest_value_for(record, property:)
|
57
|
+
method_name = map_property_to_method_name(property: property)
|
58
|
+
return nil unless record.respond_to?(method_name)
|
59
|
+
record.public_send(method_name)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def map_property_to_method_name(property:)
|
65
|
+
manifest_property_to_record_method_name_map.fetch(property)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -1,12 +1,14 @@
|
|
1
1
|
module IIIFManifest
|
2
2
|
class DisplayImage
|
3
|
-
attr_reader :url, :width, :height, :iiif_endpoint, :format
|
4
|
-
def initialize(url, width:, height:, format: nil, iiif_endpoint: nil)
|
3
|
+
attr_reader :url, :type, :width, :height, :iiif_endpoint, :format, :thumbnail
|
4
|
+
def initialize(url, width:, height:, format: nil, iiif_endpoint: nil, thumbnail: nil)
|
5
5
|
@url = url
|
6
|
+
@type = 'Image'
|
6
7
|
@width = width
|
7
8
|
@height = height
|
8
9
|
@format = format
|
9
10
|
@iiif_endpoint = iiif_endpoint
|
11
|
+
@thumbnail = thumbnail
|
10
12
|
end
|
11
13
|
end
|
12
14
|
end
|
@@ -10,8 +10,10 @@ module IIIFManifest
|
|
10
10
|
|
11
11
|
def apply(manifest)
|
12
12
|
manifest['@id'] = record.manifest_url.to_s
|
13
|
-
|
14
|
-
manifest.
|
13
|
+
label = Array(::IIIFManifest.config.manifest_value_for(record, property: :label)).first
|
14
|
+
manifest.label = label
|
15
|
+
description = Array(::IIIFManifest.config.manifest_value_for(record, property: :description)).first
|
16
|
+
manifest.description = description
|
15
17
|
manifest.viewing_hint = viewing_hint if viewing_hint.present?
|
16
18
|
manifest.viewing_direction = viewing_direction if viewing_direction.present?
|
17
19
|
manifest.metadata = record.manifest_metadata if valid_metadata?
|
@@ -2,18 +2,18 @@ module IIIFManifest
|
|
2
2
|
module V3
|
3
3
|
class DisplayContent
|
4
4
|
attr_reader :url, :width, :height, :duration, :iiif_endpoint, :format, :type,
|
5
|
-
:label, :auth_service
|
6
|
-
def initialize(url, type:,
|
7
|
-
format: nil, iiif_endpoint: nil, auth_service: nil)
|
5
|
+
:label, :auth_service, :thumbnail
|
6
|
+
def initialize(url, type:, **kwargs)
|
8
7
|
@url = url
|
9
8
|
@type = type
|
10
|
-
@width = width
|
11
|
-
@height = height
|
12
|
-
@duration = duration
|
13
|
-
@label = label
|
14
|
-
@format = format
|
15
|
-
@iiif_endpoint = iiif_endpoint
|
16
|
-
@auth_service = auth_service
|
9
|
+
@width = kwargs[:width]
|
10
|
+
@height = kwargs[:height]
|
11
|
+
@duration = kwargs[:duration]
|
12
|
+
@label = kwargs[:label]
|
13
|
+
@format = kwargs[:format]
|
14
|
+
@iiif_endpoint = kwargs[:iiif_endpoint]
|
15
|
+
@auth_service = kwargs[:auth_service]
|
16
|
+
@thumbnail = kwargs[:thumbnail]
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -3,20 +3,22 @@ module IIIFManifest
|
|
3
3
|
class ManifestBuilder
|
4
4
|
class CanvasBuilder
|
5
5
|
attr_reader :record, :parent, :iiif_canvas_factory, :content_builder,
|
6
|
-
:choice_builder, :iiif_annotation_page_factory
|
6
|
+
:choice_builder, :iiif_annotation_page_factory, :thumbnail_builder_factory
|
7
7
|
|
8
8
|
def initialize(record,
|
9
9
|
parent,
|
10
10
|
iiif_canvas_factory:,
|
11
11
|
content_builder:,
|
12
12
|
choice_builder:,
|
13
|
-
iiif_annotation_page_factory
|
13
|
+
iiif_annotation_page_factory:,
|
14
|
+
thumbnail_builder_factory:)
|
14
15
|
@record = record
|
15
16
|
@parent = parent
|
16
17
|
@iiif_canvas_factory = iiif_canvas_factory
|
17
18
|
@content_builder = content_builder
|
18
19
|
@choice_builder = choice_builder
|
19
20
|
@iiif_annotation_page_factory = iiif_annotation_page_factory
|
21
|
+
@thumbnail_builder_factory = thumbnail_builder_factory
|
20
22
|
apply_record_properties
|
21
23
|
# Presentation 2.x approach
|
22
24
|
attach_image if display_image
|
@@ -45,6 +47,8 @@ module IIIFManifest
|
|
45
47
|
record.display_image if record.respond_to?(:display_image)
|
46
48
|
end
|
47
49
|
|
50
|
+
# @return [Array<Object>] if the record has a display content
|
51
|
+
# @return [NilClass] if there is no display content
|
48
52
|
def display_content
|
49
53
|
Array.wrap(record.display_content) if record.respond_to?(:display_content) && record.display_content.present?
|
50
54
|
end
|
@@ -54,6 +58,15 @@ module IIIFManifest
|
|
54
58
|
canvas.label = ManifestBuilder.language_map(record.to_s) if record.to_s.present?
|
55
59
|
annotation_page['id'] = "#{path}/annotation_page/#{annotation_page.index}"
|
56
60
|
canvas.items = [annotation_page]
|
61
|
+
apply_thumbnail_to(canvas)
|
62
|
+
end
|
63
|
+
|
64
|
+
def apply_thumbnail_to(canvas)
|
65
|
+
if display_image
|
66
|
+
canvas.thumbnail = Array(thumbnail_builder_factory.new(display_image).build)
|
67
|
+
elsif display_content.try(:first)
|
68
|
+
canvas.thumbnail = Array(thumbnail_builder_factory.new(display_content.first).build)
|
69
|
+
end
|
57
70
|
end
|
58
71
|
|
59
72
|
def annotation_page
|
@@ -11,11 +11,12 @@ module IIIFManifest
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def apply(canvas)
|
14
|
-
annotation['target'] = canvas['id']
|
15
|
-
canvas['width'] = annotation.body['width']
|
16
|
-
canvas['height'] = annotation.body['height']
|
17
|
-
canvas['duration'] = annotation.body['duration']
|
18
14
|
# Assume first item in canvas is an annotation page
|
15
|
+
annotation['id'] = "#{canvas.items.first['id']}/annotation/#{annotation.index}"
|
16
|
+
annotation['target'] = canvas['id']
|
17
|
+
canvas['width'] = annotation.body['width'] if annotation.body['width'].present?
|
18
|
+
canvas['height'] = annotation.body['height'] if annotation.body['height'].present?
|
19
|
+
canvas['duration'] = annotation.body['duration'] if annotation.body['duration'].present?
|
19
20
|
canvas.items.first.items += [annotation]
|
20
21
|
end
|
21
22
|
|
@@ -65,13 +65,17 @@ module IIIFManifest
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def rights=(rights)
|
68
|
-
inner_hash['rights'] = rights
|
68
|
+
inner_hash['rights'] = Array(rights).first
|
69
69
|
end
|
70
70
|
|
71
71
|
def homepage=(homepage)
|
72
72
|
inner_hash['homepage'] = homepage
|
73
73
|
end
|
74
74
|
|
75
|
+
def thumbnail=(thumbnail)
|
76
|
+
inner_hash['thumbnail'] = thumbnail
|
77
|
+
end
|
78
|
+
|
75
79
|
def initial_attributes
|
76
80
|
{
|
77
81
|
'@context' => [
|
@@ -119,6 +123,14 @@ module IIIFManifest
|
|
119
123
|
inner_hash['items'] = items
|
120
124
|
end
|
121
125
|
|
126
|
+
def thumbnail
|
127
|
+
inner_hash['thumbnail']
|
128
|
+
end
|
129
|
+
|
130
|
+
def thumbnail=(thumbnail)
|
131
|
+
inner_hash['thumbnail'] = thumbnail
|
132
|
+
end
|
133
|
+
|
122
134
|
def initial_attributes
|
123
135
|
{
|
124
136
|
'type' => 'Canvas'
|
@@ -191,6 +203,10 @@ module IIIFManifest
|
|
191
203
|
inner_hash['body']
|
192
204
|
end
|
193
205
|
|
206
|
+
def index
|
207
|
+
@index ||= SecureRandom.uuid
|
208
|
+
end
|
209
|
+
|
194
210
|
def initial_attributes
|
195
211
|
{
|
196
212
|
'type' => 'Annotation',
|
@@ -212,7 +228,8 @@ module IIIFManifest
|
|
212
228
|
{
|
213
229
|
'@context' => 'http://iiif.io/api/search/1/context.json',
|
214
230
|
'profile' => 'http://iiif.io/api/search/1/search',
|
215
|
-
'label' => 'Search within this manifest'
|
231
|
+
'label' => 'Search within this manifest',
|
232
|
+
'type' => 'SearchService1'
|
216
233
|
}
|
217
234
|
end
|
218
235
|
end
|
@@ -229,7 +246,19 @@ module IIIFManifest
|
|
229
246
|
def initial_attributes
|
230
247
|
{
|
231
248
|
'profile' => 'http://iiif.io/api/search/1/autocomplete',
|
232
|
-
'label' => 'Get suggested words in this manifest'
|
249
|
+
'label' => 'Get suggested words in this manifest',
|
250
|
+
'type' => 'AutoCompleteService1'
|
251
|
+
}
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
class Thumbnail < IIIFService
|
256
|
+
def service=(service)
|
257
|
+
inner_hash['service'] = service
|
258
|
+
end
|
259
|
+
|
260
|
+
def initial_attributes
|
261
|
+
{
|
233
262
|
}
|
234
263
|
end
|
235
264
|
end
|
@@ -9,9 +9,9 @@ module IIIFManifest
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def apply(resource)
|
12
|
-
service['id'] = iiif_endpoint.url
|
12
|
+
service['@id'] = iiif_endpoint.url
|
13
13
|
service['profile'] = iiif_endpoint.profile
|
14
|
-
service['type'] = determine_type(iiif_endpoint.context)
|
14
|
+
service['@type'] = determine_type(iiif_endpoint.context)
|
15
15
|
resource.service = [service]
|
16
16
|
end
|
17
17
|
|
@@ -2,36 +2,36 @@ module IIIFManifest
|
|
2
2
|
module V3
|
3
3
|
class ManifestBuilder
|
4
4
|
class RecordPropertyBuilder < ::IIIFManifest::ManifestBuilder::RecordPropertyBuilder
|
5
|
-
attr_reader :canvas_builder_factory
|
5
|
+
attr_reader :canvas_builder_factory, :thumbnail_builder_factory
|
6
6
|
def initialize(record,
|
7
7
|
iiif_search_service_factory:,
|
8
8
|
iiif_autocomplete_service_factory:,
|
9
|
-
canvas_builder_factory
|
9
|
+
canvas_builder_factory:,
|
10
|
+
thumbnail_builder_factory:)
|
10
11
|
super(record,
|
11
12
|
iiif_search_service_factory: iiif_search_service_factory,
|
12
13
|
iiif_autocomplete_service_factory: iiif_autocomplete_service_factory)
|
13
14
|
@canvas_builder_factory = canvas_builder_factory
|
15
|
+
@thumbnail_builder_factory = thumbnail_builder_factory
|
14
16
|
end
|
15
17
|
|
16
18
|
def apply(manifest)
|
17
19
|
setup_manifest_from_record(manifest, record)
|
18
20
|
# Build the items array
|
19
21
|
canvas_builder.apply(manifest.items)
|
22
|
+
apply_thumbnail_to(manifest) unless manifest_thumbnail?
|
20
23
|
manifest
|
21
24
|
end
|
22
25
|
|
23
26
|
def populate_rendering
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
31
|
-
sequence_rendering
|
27
|
+
return unless record.respond_to?(:sequence_rendering)
|
28
|
+
record.sequence_rendering.collect do |rendering|
|
29
|
+
sequence_rendering = rendering.to_h.except('@id', 'label')
|
30
|
+
sequence_rendering['id'] = rendering['@id']
|
31
|
+
if rendering['label'].present?
|
32
|
+
sequence_rendering['label'] = ManifestBuilder.language_map(rendering['label'])
|
32
33
|
end
|
33
|
-
|
34
|
-
[]
|
34
|
+
sequence_rendering
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -41,19 +41,24 @@ module IIIFManifest
|
|
41
41
|
canvas_builder_factory.from(record)
|
42
42
|
end
|
43
43
|
|
44
|
-
|
44
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Metrics/MethodLength
|
45
45
|
def setup_manifest_from_record(manifest, record)
|
46
46
|
manifest['id'] = record.manifest_url.to_s
|
47
|
-
|
48
|
-
manifest.
|
47
|
+
label = ::IIIFManifest.config.manifest_value_for(record, property: :label)
|
48
|
+
manifest.label = ManifestBuilder.language_map(label) if label.present?
|
49
|
+
summary = ::IIIFManifest.config.manifest_value_for(record, property: :summary)
|
50
|
+
manifest.summary = ManifestBuilder.language_map(summary) if summary.present?
|
51
|
+
rights = ::IIIFManifest.config.manifest_value_for(record, property: :rights)
|
52
|
+
manifest.rights = rights if rights.present?
|
49
53
|
manifest.behavior = viewing_hint if viewing_hint.present?
|
50
|
-
manifest.metadata = metadata_from_record(record)
|
54
|
+
manifest.metadata = metadata_from_record(record) if metadata_from_record(record).present?
|
51
55
|
manifest.viewing_direction = viewing_direction if viewing_direction.present?
|
52
56
|
manifest.service = services if search_service.present?
|
53
|
-
manifest.rendering = populate_rendering
|
54
|
-
|
57
|
+
manifest.rendering = populate_rendering if populate_rendering.present?
|
58
|
+
homepage = ::IIIFManifest.config.manifest_value_for(record, property: :homepage)
|
59
|
+
manifest.homepage = homepage if homepage.present?
|
55
60
|
end
|
56
|
-
|
61
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Metrics/MethodLength
|
57
62
|
|
58
63
|
def metadata_from_record(record)
|
59
64
|
if valid_v3_metadata?
|
@@ -94,6 +99,18 @@ module IIIFManifest
|
|
94
99
|
metadata_field['value'] = ManifestBuilder.language_map(field['value'])
|
95
100
|
metadata_field
|
96
101
|
end
|
102
|
+
|
103
|
+
def apply_thumbnail_to(manifest)
|
104
|
+
if manifest.is_a? IIIFManifest::Collection
|
105
|
+
manifest.thumbnail = manifest.items.collect(&:thumbnail).compact
|
106
|
+
elsif manifest.items.first&.thumbnail.present?
|
107
|
+
manifest.thumbnail = manifest.items.first&.thumbnail
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def manifest_thumbnail?
|
112
|
+
::IIIFManifest.config.manifest_thumbnail == false
|
113
|
+
end
|
97
114
|
end
|
98
115
|
end
|
99
116
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module IIIFManifest
|
2
|
+
module V3
|
3
|
+
class ManifestBuilder
|
4
|
+
class ThumbnailBuilder
|
5
|
+
attr_reader :display_content, :iiif_thumbnail_factory, :image_service_builder_factory
|
6
|
+
def initialize(display_content, iiif_thumbnail_factory:, image_service_builder_factory:)
|
7
|
+
@display_content = display_content
|
8
|
+
@iiif_thumbnail_factory = iiif_thumbnail_factory
|
9
|
+
@image_service_builder_factory = image_service_builder_factory
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [Array<Object>]
|
13
|
+
def build
|
14
|
+
return Array(display_content.thumbnail.map(&:stringify_keys)) unless display_content.thumbnail.nil?
|
15
|
+
return nil if display_content.type != "Image" || iiif_endpoint.nil?
|
16
|
+
|
17
|
+
build_thumbnail
|
18
|
+
image_service_builder.apply(thumbnail)
|
19
|
+
[thumbnail]
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def build_thumbnail
|
25
|
+
thumbnail['id'] = File.join(
|
26
|
+
display_content.iiif_endpoint.url,
|
27
|
+
'full',
|
28
|
+
"!#{max_edge},#{max_edge}",
|
29
|
+
'0',
|
30
|
+
'default.jpg'
|
31
|
+
)
|
32
|
+
thumbnail['type'] = display_content.type
|
33
|
+
thumbnail['height'] = (display_content.height * reduction_ratio).round
|
34
|
+
thumbnail['width'] = (display_content.width * reduction_ratio).round
|
35
|
+
thumbnail['format'] = display_content.format
|
36
|
+
end
|
37
|
+
|
38
|
+
def reduction_ratio
|
39
|
+
width = display_content.width
|
40
|
+
height = display_content.height
|
41
|
+
max_edge = @max_edge.to_f
|
42
|
+
return 1 if width <= max_edge && height <= max_edge
|
43
|
+
|
44
|
+
long_edge = [height, width].max
|
45
|
+
max_edge / long_edge
|
46
|
+
end
|
47
|
+
|
48
|
+
def max_edge
|
49
|
+
@max_edge = ::IIIFManifest.config.max_edge_for_thumbnail
|
50
|
+
end
|
51
|
+
|
52
|
+
def thumbnail
|
53
|
+
@thumbnail ||= iiif_thumbnail_factory.new
|
54
|
+
end
|
55
|
+
|
56
|
+
def iiif_endpoint
|
57
|
+
display_content.try(:iiif_endpoint)
|
58
|
+
end
|
59
|
+
|
60
|
+
def image_service_builder
|
61
|
+
image_service_builder_factory.new(iiif_endpoint)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -6,6 +6,7 @@ require_relative 'manifest_builder/content_builder'
|
|
6
6
|
require_relative 'manifest_builder/body_builder'
|
7
7
|
require_relative 'manifest_builder/structure_builder'
|
8
8
|
require_relative 'manifest_builder/image_service_builder'
|
9
|
+
require_relative 'manifest_builder/thumbnail_builder'
|
9
10
|
|
10
11
|
module IIIFManifest
|
11
12
|
module V3
|
@@ -28,7 +29,7 @@ module IIIFManifest
|
|
28
29
|
|
29
30
|
def obj_to_language_map(obj)
|
30
31
|
return nil unless obj.is_a?(String) || (obj.is_a?(Array) && obj.all? { |o| o.is_a?(String) })
|
31
|
-
{ '
|
32
|
+
{ 'none' => Array(obj) }
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
@@ -29,7 +29,8 @@ module IIIFManifest
|
|
29
29
|
ManifestBuilder::RecordPropertyBuilder,
|
30
30
|
iiif_search_service_factory: iiif_search_service_factory,
|
31
31
|
iiif_autocomplete_service_factory: iiif_autocomplete_service_factory,
|
32
|
-
canvas_builder_factory: deep_canvas_builder_factory
|
32
|
+
canvas_builder_factory: deep_canvas_builder_factory,
|
33
|
+
thumbnail_builder_factory: thumbnail_builder_factory
|
33
34
|
# canvas_builder_factory: canvas_builder_factory
|
34
35
|
)
|
35
36
|
end
|
@@ -55,7 +56,8 @@ module IIIFManifest
|
|
55
56
|
iiif_canvas_factory: iiif_canvas_factory,
|
56
57
|
content_builder: content_builder,
|
57
58
|
choice_builder: choice_builder,
|
58
|
-
iiif_annotation_page_factory: iiif_annotation_page_factory
|
59
|
+
iiif_annotation_page_factory: iiif_annotation_page_factory,
|
60
|
+
thumbnail_builder_factory: thumbnail_builder_factory
|
59
61
|
)
|
60
62
|
end
|
61
63
|
|
@@ -84,6 +86,14 @@ module IIIFManifest
|
|
84
86
|
)
|
85
87
|
end
|
86
88
|
|
89
|
+
def thumbnail_builder_factory
|
90
|
+
IIIFManifest::ManifestServiceLocator::InjectedFactory.new(
|
91
|
+
ManifestBuilder::ThumbnailBuilder,
|
92
|
+
iiif_thumbnail_factory: iiif_thumbnail_factory,
|
93
|
+
image_service_builder_factory: image_service_builder_factory
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
87
97
|
def image_service_builder_factory
|
88
98
|
IIIFManifest::ManifestServiceLocator::InjectedFactory.new(
|
89
99
|
ManifestBuilder::ImageServiceBuilder,
|
@@ -142,6 +152,10 @@ module IIIFManifest
|
|
142
152
|
def iiif_autocomplete_service_factory
|
143
153
|
IIIFManifest::V3::ManifestBuilder::IIIFManifest::AutocompleteService
|
144
154
|
end
|
155
|
+
|
156
|
+
def iiif_thumbnail_factory
|
157
|
+
IIIFManifest::V3::ManifestBuilder::IIIFManifest::Thumbnail
|
158
|
+
end
|
145
159
|
end
|
146
160
|
end
|
147
161
|
end
|
data/lib/iiif_manifest.rb
CHANGED
@@ -5,6 +5,7 @@ require 'active_support/core_ext/object'
|
|
5
5
|
|
6
6
|
module IIIFManifest
|
7
7
|
extend ActiveSupport::Autoload
|
8
|
+
autoload :Configuration
|
8
9
|
autoload :ManifestBuilder
|
9
10
|
autoload :ManifestFactory
|
10
11
|
autoload :ManifestServiceLocator
|
@@ -12,4 +13,46 @@ module IIIFManifest
|
|
12
13
|
autoload :IIIFCollection
|
13
14
|
autoload :IIIFEndpoint
|
14
15
|
autoload :V3
|
16
|
+
|
17
|
+
##
|
18
|
+
# @api public
|
19
|
+
#
|
20
|
+
# Exposes the IIIFManifest configuration.
|
21
|
+
#
|
22
|
+
# In the below examples, you would add the code to a `config/initializers/iiif_manifest_config.rb` file
|
23
|
+
# in your application.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# # obliterate the default configuration and only use the one we gave
|
27
|
+
# IIIFManifest.config do |config|
|
28
|
+
# config.manifest_property_to_record_method_name_map = { summary: :abstract }
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# # use the default configuration but amend the summary property
|
32
|
+
# IIIFManifest.config do |config|
|
33
|
+
# config.manifest_property_to_record_method_name_map.merge!(summary: :abstract)
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# # set max edge length for thumbnail images
|
37
|
+
# # the below example will set the max edge to 100px
|
38
|
+
# IIIFManifest.confg do |config|
|
39
|
+
# config.max_edge_for_thumbnail = 100
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# # disable the thumbnail property on the manifest level
|
43
|
+
# # since it will be shown by default
|
44
|
+
# IIIFManifest.confg do |config|
|
45
|
+
# config.manifest_thumbnail = false
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# @yield [IIIFManifest::Configuration] if a block is passed
|
49
|
+
# @return [IIIFManifest::Configuration]
|
50
|
+
# @see IIIFManifest::Configuration for configuration options
|
51
|
+
def self.config(&block)
|
52
|
+
@config ||= IIIFManifest::Configuration.new
|
53
|
+
|
54
|
+
yield @config if block
|
55
|
+
|
56
|
+
@config
|
57
|
+
end
|
15
58
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iiif_manifest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Coyne
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-11-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -138,6 +138,7 @@ files:
|
|
138
138
|
- bin/setup
|
139
139
|
- iiif_manifest.gemspec
|
140
140
|
- lib/iiif_manifest.rb
|
141
|
+
- lib/iiif_manifest/configuration.rb
|
141
142
|
- lib/iiif_manifest/display_image.rb
|
142
143
|
- lib/iiif_manifest/iiif_collection.rb
|
143
144
|
- lib/iiif_manifest/iiif_endpoint.rb
|
@@ -168,6 +169,7 @@ files:
|
|
168
169
|
- lib/iiif_manifest/v3/manifest_builder/image_service_builder.rb
|
169
170
|
- lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb
|
170
171
|
- lib/iiif_manifest/v3/manifest_builder/structure_builder.rb
|
172
|
+
- lib/iiif_manifest/v3/manifest_builder/thumbnail_builder.rb
|
171
173
|
- lib/iiif_manifest/v3/manifest_factory.rb
|
172
174
|
- lib/iiif_manifest/v3/manifest_service_locator.rb
|
173
175
|
- lib/iiif_manifest/version.rb
|
@@ -189,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
189
191
|
- !ruby/object:Gem::Version
|
190
192
|
version: '0'
|
191
193
|
requirements: []
|
192
|
-
rubygems_version: 3.
|
194
|
+
rubygems_version: 3.1.6
|
193
195
|
signing_key:
|
194
196
|
specification_version: 4
|
195
197
|
summary: Generate IIIF presentation manifests for Hydra::Works
|