Skip to content

Commit

Permalink
Merge pull request #79 from samvera/add-thumbnail-property
Browse files Browse the repository at this point in the history
Add thumbnail property
  • Loading branch information
cjcolvar authored Nov 9, 2022
2 parents eadc239 + ceafb51 commit 3c9841d
Show file tree
Hide file tree
Showing 14 changed files with 486 additions and 148 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,26 @@ To enable this, add the following code to a config file at `config/initializers/
```
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.

```ruby
# Example: use this configuration to set the max edge length of thumbnails, default is 200
IIIFManifest.confg do |config|
config.max_edge_for_thumbnail = 100
end
```
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.

```ruby
# Example: use this configuration to disable thumbnails to show up by default on the manifest level (version 3 only)
IIIFManifest.confg do |config|
config.manifest_thumbnail = false
end
```
According to the Presentation API 3.0 [specifications](https://iiif.io/api/presentation/3.0/#thumbnail):
> A Manifest *SHOULD* have the `thumbnail` property with at least one item.
The above configuration allows you to disable that if desired since it is not a *MUST*.


# 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.
Expand Down
12 changes: 12 additions & 0 deletions lib/iiif_manifest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ module IIIFManifest
# config.manifest_property_to_record_method_name_map.merge!(summary: :abstract)
# end
#
# # set max edge length for thumbnail images
# # the below example will set the max edge to 100px
# IIIFManifest.confg do |config|
# config.max_edge_for_thumbnail = 100
# end
#
# # disable the thumbnail property on the manifest level
# # since it will be shown by default
# IIIFManifest.confg do |config|
# config.manifest_thumbnail = false
# end
#
# @yield [IIIFManifest::Configuration] if a block is passed
# @return [IIIFManifest::Configuration]
# @see IIIFManifest::Configuration for configuration options
Expand Down
18 changes: 18 additions & 0 deletions lib/iiif_manifest/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,24 @@ def manifest_property_to_record_method_name_map
@manifest_property_to_record_method_name_map ||= DEFAULT_MANIFEST_PROPERTY_TO_RECORD_METHOD_NAME_MAP.dup
end

##
# @param value [Integer]
attr_writer :max_edge_for_thumbnail
# Used to set max edge length for thumbnail generation.
# @return [Integer]
def max_edge_for_thumbnail
return @max_edge_for_thumbnail unless @max_edge_for_thumbnail.nil?
@max_edge_for_thumbnail = 200
end

attr_writer :manifest_thumbnail
# Used to configure whether or not to show the manifest thumbnail property.
# @return [Boolean]
def manifest_thumbnail
return @manifest_thumbnail unless @manifest_thumbnail.nil?
@manifest_thumbnail = true
end

##
# @api private
# @param record [Object] has the value for the :property we want to set
Expand Down
6 changes: 4 additions & 2 deletions lib/iiif_manifest/display_image.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
module IIIFManifest
class DisplayImage
attr_reader :url, :width, :height, :iiif_endpoint, :format
def initialize(url, width:, height:, format: nil, iiif_endpoint: nil)
attr_reader :url, :type, :width, :height, :iiif_endpoint, :format, :thumbnail
def initialize(url, width:, height:, format: nil, iiif_endpoint: nil, thumbnail: nil)
@url = url
@type = 'Image'
@width = width
@height = height
@format = format
@iiif_endpoint = iiif_endpoint
@thumbnail = thumbnail
end
end
end
20 changes: 10 additions & 10 deletions lib/iiif_manifest/v3/display_content.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ module IIIFManifest
module V3
class DisplayContent
attr_reader :url, :width, :height, :duration, :iiif_endpoint, :format, :type,
:label, :auth_service
def initialize(url, type:, width: nil, height: nil, duration: nil, label: nil,
format: nil, iiif_endpoint: nil, auth_service: nil)
:label, :auth_service, :thumbnail
def initialize(url, type:, **kwargs)
@url = url
@type = type
@width = width
@height = height
@duration = duration
@label = label
@format = format
@iiif_endpoint = iiif_endpoint
@auth_service = auth_service
@width = kwargs[:width]
@height = kwargs[:height]
@duration = kwargs[:duration]
@label = kwargs[:label]
@format = kwargs[:format]
@iiif_endpoint = kwargs[:iiif_endpoint]
@auth_service = kwargs[:auth_service]
@thumbnail = kwargs[:thumbnail]
end
end
end
Expand Down
1 change: 1 addition & 0 deletions lib/iiif_manifest/v3/manifest_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require_relative 'manifest_builder/body_builder'
require_relative 'manifest_builder/structure_builder'
require_relative 'manifest_builder/image_service_builder'
require_relative 'manifest_builder/thumbnail_builder'

module IIIFManifest
module V3
Expand Down
17 changes: 15 additions & 2 deletions lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,22 @@ module V3
class ManifestBuilder
class CanvasBuilder
attr_reader :record, :parent, :iiif_canvas_factory, :content_builder,
:choice_builder, :iiif_annotation_page_factory
:choice_builder, :iiif_annotation_page_factory, :thumbnail_builder_factory

def initialize(record,
parent,
iiif_canvas_factory:,
content_builder:,
choice_builder:,
iiif_annotation_page_factory:)
iiif_annotation_page_factory:,
thumbnail_builder_factory:)
@record = record
@parent = parent
@iiif_canvas_factory = iiif_canvas_factory
@content_builder = content_builder
@choice_builder = choice_builder
@iiif_annotation_page_factory = iiif_annotation_page_factory
@thumbnail_builder_factory = thumbnail_builder_factory
apply_record_properties
# Presentation 2.x approach
attach_image if display_image
Expand Down Expand Up @@ -45,6 +47,8 @@ def display_image
record.display_image if record.respond_to?(:display_image)
end

# @return [Array<Object>] if the record has a display content
# @return [NilClass] if there is no display content
def display_content
Array.wrap(record.display_content) if record.respond_to?(:display_content) && record.display_content.present?
end
Expand All @@ -54,6 +58,15 @@ def apply_record_properties
canvas.label = ManifestBuilder.language_map(record.to_s) if record.to_s.present?
annotation_page['id'] = "#{path}/annotation_page/#{annotation_page.index}"
canvas.items = [annotation_page]
apply_thumbnail_to(canvas)
end

def apply_thumbnail_to(canvas)
if display_image
canvas.thumbnail = Array(thumbnail_builder_factory.new(display_image).build)
elsif display_content.try(:first)
canvas.thumbnail = Array(thumbnail_builder_factory.new(display_content.first).build)
end
end

def annotation_page
Expand Down
23 changes: 23 additions & 0 deletions lib/iiif_manifest/v3/manifest_builder/iiif_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ def homepage=(homepage)
inner_hash['homepage'] = homepage
end

def thumbnail=(thumbnail)
inner_hash['thumbnail'] = thumbnail
end

def initial_attributes
{
'@context' => [
Expand Down Expand Up @@ -119,6 +123,14 @@ def items=(items)
inner_hash['items'] = items
end

def thumbnail
inner_hash['thumbnail']
end

def thumbnail=(thumbnail)
inner_hash['thumbnail'] = thumbnail
end

def initial_attributes
{
'type' => 'Canvas'
Expand Down Expand Up @@ -239,6 +251,17 @@ def initial_attributes
}
end
end

class Thumbnail < IIIFService
def service=(service)
inner_hash['service'] = service
end

def initial_attributes
{
}
end
end
end
end
end
Expand Down
23 changes: 19 additions & 4 deletions lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@ module IIIFManifest
module V3
class ManifestBuilder
class RecordPropertyBuilder < ::IIIFManifest::ManifestBuilder::RecordPropertyBuilder
attr_reader :canvas_builder_factory
attr_reader :canvas_builder_factory, :thumbnail_builder_factory
def initialize(record,
iiif_search_service_factory:,
iiif_autocomplete_service_factory:,
canvas_builder_factory:)
canvas_builder_factory:,
thumbnail_builder_factory:)
super(record,
iiif_search_service_factory: iiif_search_service_factory,
iiif_autocomplete_service_factory: iiif_autocomplete_service_factory)
@canvas_builder_factory = canvas_builder_factory
@thumbnail_builder_factory = thumbnail_builder_factory
end

def apply(manifest)
setup_manifest_from_record(manifest, record)
# Build the items array
canvas_builder.apply(manifest.items)
apply_thumbnail_to(manifest) unless manifest_thumbnail?
manifest
end

Expand All @@ -38,7 +41,7 @@ def canvas_builder
canvas_builder_factory.from(record)
end

# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Metrics/MethodLength
def setup_manifest_from_record(manifest, record)
manifest['id'] = record.manifest_url.to_s
label = ::IIIFManifest.config.manifest_value_for(record, property: :label)
Expand All @@ -55,7 +58,7 @@ def setup_manifest_from_record(manifest, record)
homepage = ::IIIFManifest.config.manifest_value_for(record, property: :homepage)
manifest.homepage = homepage if homepage.present?
end
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Metrics/MethodLength

def metadata_from_record(record)
if valid_v3_metadata?
Expand Down Expand Up @@ -96,6 +99,18 @@ def transform_field(field)
metadata_field['value'] = ManifestBuilder.language_map(field['value'])
metadata_field
end

def apply_thumbnail_to(manifest)
if manifest.is_a? IIIFManifest::Collection
manifest.thumbnail = manifest.items.collect(&:thumbnail).compact
elsif manifest.items.first&.thumbnail.present?
manifest.thumbnail = manifest.items.first&.thumbnail
end
end

def manifest_thumbnail?
::IIIFManifest.config.manifest_thumbnail == false
end
end
end
end
Expand Down
66 changes: 66 additions & 0 deletions lib/iiif_manifest/v3/manifest_builder/thumbnail_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
module IIIFManifest
module V3
class ManifestBuilder
class ThumbnailBuilder
attr_reader :display_content, :iiif_thumbnail_factory, :image_service_builder_factory
def initialize(display_content, iiif_thumbnail_factory:, image_service_builder_factory:)
@display_content = display_content
@iiif_thumbnail_factory = iiif_thumbnail_factory
@image_service_builder_factory = image_service_builder_factory
end

# @return [Array<Object>]
def build
return Array(display_content.thumbnail.map(&:stringify_keys)) unless display_content.thumbnail.nil?
return nil if display_content.type != "Image" || iiif_endpoint.nil?

build_thumbnail
image_service_builder.apply(thumbnail)
[thumbnail]
end

private

def build_thumbnail
thumbnail['id'] = File.join(
display_content.iiif_endpoint.url,
'full',
"!#{max_edge},#{max_edge}",
'0',
'default.jpg'
)
thumbnail['type'] = display_content.type
thumbnail['height'] = (display_content.height * reduction_ratio).round
thumbnail['width'] = (display_content.width * reduction_ratio).round
thumbnail['format'] = display_content.format
end

def reduction_ratio
width = display_content.width
height = display_content.height
max_edge = @max_edge.to_f
return 1 if width <= max_edge && height <= max_edge

long_edge = [height, width].max
max_edge / long_edge
end

def max_edge
@max_edge = ::IIIFManifest.config.max_edge_for_thumbnail
end

def thumbnail
@thumbnail ||= iiif_thumbnail_factory.new
end

def iiif_endpoint
display_content.try(:iiif_endpoint)
end

def image_service_builder
image_service_builder_factory.new(iiif_endpoint)
end
end
end
end
end
18 changes: 16 additions & 2 deletions lib/iiif_manifest/v3/manifest_service_locator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def record_property_builder
ManifestBuilder::RecordPropertyBuilder,
iiif_search_service_factory: iiif_search_service_factory,
iiif_autocomplete_service_factory: iiif_autocomplete_service_factory,
canvas_builder_factory: deep_canvas_builder_factory
canvas_builder_factory: deep_canvas_builder_factory,
thumbnail_builder_factory: thumbnail_builder_factory
# canvas_builder_factory: canvas_builder_factory
)
end
Expand All @@ -55,7 +56,8 @@ def canvas_builder
iiif_canvas_factory: iiif_canvas_factory,
content_builder: content_builder,
choice_builder: choice_builder,
iiif_annotation_page_factory: iiif_annotation_page_factory
iiif_annotation_page_factory: iiif_annotation_page_factory,
thumbnail_builder_factory: thumbnail_builder_factory
)
end

Expand Down Expand Up @@ -84,6 +86,14 @@ def body_builder_factory
)
end

def thumbnail_builder_factory
IIIFManifest::ManifestServiceLocator::InjectedFactory.new(
ManifestBuilder::ThumbnailBuilder,
iiif_thumbnail_factory: iiif_thumbnail_factory,
image_service_builder_factory: image_service_builder_factory
)
end

def image_service_builder_factory
IIIFManifest::ManifestServiceLocator::InjectedFactory.new(
ManifestBuilder::ImageServiceBuilder,
Expand Down Expand Up @@ -142,6 +152,10 @@ def iiif_search_service_factory
def iiif_autocomplete_service_factory
IIIFManifest::V3::ManifestBuilder::IIIFManifest::AutocompleteService
end

def iiif_thumbnail_factory
IIIFManifest::V3::ManifestBuilder::IIIFManifest::Thumbnail
end
end
end
end
Expand Down
Loading

0 comments on commit 3c9841d

Please sign in to comment.