Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add thumbnail property #79

Merged
merged 32 commits into from
Nov 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
841f0bb
make thumbnail property appear with type
kirkkwang Oct 11, 2022
8d79e9d
wip
kirkkwang Oct 11, 2022
e45340d
make thumbnail show up in items but not with service yet
kirkkwang Oct 11, 2022
d02df7a
alter canvas builder spec and refactor canvas builder
kirkkwang Oct 12, 2022
762d709
refactor thumbnail logic
kirkkwang Oct 12, 2022
c7732b3
fix canvas builder spec
kirkkwang Oct 13, 2022
0b3c002
add iiif endpoint guard
kirkkwang Oct 13, 2022
c38e517
add more tests to thumbnail builder spec
kirkkwang Oct 13, 2022
e0042fc
add service to thumbnail
kirkkwang Oct 13, 2022
b0f7e02
add service to thumbnail
kirkkwang Oct 13, 2022
45b12ae
start initial attributes for thumbnail on base manifest
kirkkwang Oct 13, 2022
6db9232
make thumbnail work for display image or display content
kirkkwang Oct 13, 2022
060bf05
make thumbnail property show on base manifest
kirkkwang Oct 13, 2022
ebed3d6
add thumbnail to manifest factory spec
kirkkwang Oct 14, 2022
c984cc5
remove accidental new line
kirkkwang Oct 14, 2022
1fd685e
refactor and adjust specs
kirkkwang Oct 14, 2022
8c22e7f
allow thumbnail type to be dynamic
kirkkwang Oct 17, 2022
611a024
allow thumbnail max edge to be configurable
kirkkwang Oct 17, 2022
d1418b4
fix spec for ruby 2.5 and 2.6
kirkkwang Oct 18, 2022
e9ecd79
remove apply thumbnail to manifest and fix specs
kirkkwang Oct 18, 2022
bfca0b0
allow thumbnails to have more than one thumbnail on manifest level
kirkkwang Oct 21, 2022
dc8ce06
refactor
kirkkwang Oct 21, 2022
113a1d9
adjust spec
kirkkwang Oct 21, 2022
5f47141
Merge pull request #80 from samvera/remove-apply-thumbnail-to-manifes…
kirkkwang Oct 21, 2022
cc7bfd9
Revert "remove apply thumbnail to manifest and fix specs"
kirkkwang Oct 21, 2022
0c619ca
Merge pull request #81 from samvera/revert-80-remove-apply-thumbnail-…
kirkkwang Oct 21, 2022
9fd0779
Use first canvas's thumbnail as the manifest thumbnail (if set)
cjcolvar Oct 21, 2022
9ac16c9
[WIP] Collect thumbnails for collection manifests [Needs tests]
cjcolvar Oct 21, 2022
94574d3
Allow passing in thumbnail and falling back to iiif_endpoint
cjcolvar Oct 21, 2022
12d4a2c
Appease rubocop
cjcolvar Oct 25, 2022
6942f0f
add configuration for manifest thumbnails
kirkkwang Oct 25, 2022
ceafb51
add some documentation for thumbnails configuration
kirkkwang Oct 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
kirkkwang marked this conversation as resolved.
Show resolved Hide resolved
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)
cjcolvar marked this conversation as resolved.
Show resolved Hide resolved
[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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a problem if the image_service_builder_factory receives nil?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like down the line, it will affect it here: https://github.com/samvera/iiif_manifest/blob/main/lib/iiif_manifest/v3/manifest_builder/image_service_builder.rb#L12-L14
are you thinking lonely operators for those three lines?

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