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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce4c4a874b8b50d50d11344dac0e635248754bb2b3498331cbd94945a5f3be75
4
- data.tar.gz: 3da6a490d37ea8107e50a4393c21d5189ff97e47e9f34a65bd87848f3504f307
3
+ metadata.gz: d8dd7b48693245057fa3e99f6b3f434da255e4f1e7f26412e1b56937a73dace4
4
+ data.tar.gz: 6b719a4f0b94ed7347420d5c940d8b2244195a201bd2415e9842ee7e55c8afab
5
5
  SHA512:
6
- metadata.gz: e54f567e6ed14b4a802d05433a17547e119da18ee6dc851db026f157eb344e431649d8bc84076d102449bbee5b7d69a73bbf8b22d36eb43e6e46d7062e4c7133
7
- data.tar.gz: ea7afaf08fdd7d142d7bbe6c0087521ddcda5ec520f183f7d2ae479961dd36908fa4b34054fdb379ad88f6c71c4a898d0e5c19419528e0135005a49be3deb6ee
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.0.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.0"
39
+ ruby_version: "3.0.3"
40
40
  - test:
41
41
  name: "ruby2-7"
42
- ruby_version: "2.7.0"
42
+ ruby_version: "2.7.5"
43
43
  - test:
44
44
  name: "ruby2-6"
45
- ruby_version: "2.6.5"
45
+ ruby_version: "2.6.9"
46
46
  - test:
47
47
  name: "ruby2-5"
48
- ruby_version: "2.5.7"
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: "ruby2-4"
51
- ruby_version: "2.4.9"
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
- Jump in: [![Slack Status](http://slack.samvera.org/badge.svg)](http://slack.samvera.org/)
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 be built using `IIIFManifest::ManifestFactory` while version 3.0 manifests can now be built using `IIIFManifest::V3::ManifestFactory`.
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
- # Development
164
+ ## Configuration
165
165
 
166
- 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.
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
- 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).
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
- ## Contributing
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
- 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.
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
- ## Releasing
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
- 1. `bundle install`
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
- 5. Commit these changes to the master branch
205
+ ## Contributing
186
206
 
187
- 6. Run `rake release`
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://wiki.duraspace.org/download/thumbnails/87459292/samvera-fall-font2-200w.png?version=1&modificationDate=1498550535816&api=v2)
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
@@ -6,7 +6,7 @@ module IIIFManifest
6
6
  @inner_hash = initial_attributes
7
7
  end
8
8
 
9
- delegate :[]=, :[], :as_json, :to_json, :has_key?, to: :inner_hash
9
+ delegate :[]=, :[], :as_json, :to_json, :has_key?, :key?, to: :inner_hash
10
10
 
11
11
  def initial_attributes
12
12
  {}
@@ -10,8 +10,10 @@ module IIIFManifest
10
10
 
11
11
  def apply(manifest)
12
12
  manifest['@id'] = record.manifest_url.to_s
13
- manifest.label = record.to_s
14
- manifest.description = record.description
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:, width: nil, height: nil, duration: nil, label: nil,
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
- if record.respond_to?(:sequence_rendering)
25
- record.sequence_rendering.collect do |rendering|
26
- sequence_rendering = rendering.to_h.except('@id', 'label')
27
- sequence_rendering['id'] = rendering['@id']
28
- if rendering['label'].present?
29
- sequence_rendering['label'] = ManifestBuilder.language_map(rendering['label'])
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
- else
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
- # rubocop:disable Metrics/CyclomaticComplexity
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
- manifest.label = ManifestBuilder.language_map(record.to_s) if record.to_s.present?
48
- manifest.summary = ManifestBuilder.language_map(record.description) if record.try(:description).present?
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
- manifest.homepage = record.homepage if record.try(:homepage).present?
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
- # rubocop:enable Metrics/CyclomaticComplexity
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
- { '@none' => Array(obj) }
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
@@ -1,3 +1,3 @@
1
1
  module IIIFManifest
2
- VERSION = '1.1.0'.freeze
2
+ VERSION = '1.2.0'.freeze
3
3
  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.1.0
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: 2021-01-13 00:00:00.000000000 Z
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.2.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