iiif_print 3.1.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4050e3e2e6c79d5d93bc63e2801f75bb13a7a1985dc2729ead3c0a15c4855075
4
- data.tar.gz: b0d627fa5b6d190d0fbdae66e2db116e77eb74bf3473630c008a61dd794ed600
3
+ metadata.gz: d1fb1a28565daaa498895ea28019b1a5d9cb16e01558872b4840e19ab002fb07
4
+ data.tar.gz: 76a5d7afeda6cb264d2a77e31ff0e60be1c8d2a416917907a4d848e3b8975e63
5
5
  SHA512:
6
- metadata.gz: c9f52bc069331756409eae5f67b878b2801c46d14e44fa36804faed750cfb2c07c5159015e5e236c084b0dcf8e0d2412d2d907905c43a07feba068c475761160
7
- data.tar.gz: 7f58c71b8f933df6cec597f766494091a3c33fcd4734a14118ee7213d80262d52ebd3196b452267136913dfcba3a614fbc81dfbf7399edacb44687a6defa7876
6
+ metadata.gz: '08d25b39fccf79fe3b147b35dd8eb7fff99ac9d66bb91426ea4c1b98a16009b539f48283850225725f8ebd1d62876f3d30ff61508722f09def19eb02b67f4b63'
7
+ data.tar.gz: ef3c25dca1cdc1ab648d90ec303ad56df9a26fc6df4358e610154634eccd934b64295d99c2611fd454b8321d761f7aca9bcde4263495ac2fdfc58cca8447bc61
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /app/samvera/hyrax-webapp
3
3
  specs:
4
- iiif_print (3.1.0)
4
+ iiif_print (3.1.1)
5
5
  blacklight_iiif_search (>= 1.0, < 3.0)
6
6
  derivative-rodeo (~> 0.5)
7
7
  hyrax (>= 2.5, < 6)
@@ -76,19 +76,23 @@ GEM
76
76
  i18n (>= 0.7, < 2)
77
77
  minitest (~> 5.1)
78
78
  tzinfo (~> 1.1)
79
- addressable (2.8.9)
79
+ addressable (2.9.0)
80
80
  public_suffix (>= 2.0.2, < 8.0)
81
81
  almond-rails (0.3.0)
82
82
  rails (>= 4.2)
83
+ anonymous_loader (0.1.2)
84
+ version_gem (~> 1.1, >= 1.1.13)
83
85
  arel (9.0.0)
84
86
  ast (2.4.2)
87
+ auth-sanitizer (0.2.2)
88
+ version_gem (~> 1.1, >= 1.1.10)
85
89
  autoprefixer-rails (10.4.16.0)
86
90
  execjs (~> 2)
87
91
  awesome_nested_set (3.9.0)
88
92
  activerecord (>= 4.0.0, < 8.2)
89
93
  aws-eventstream (1.4.0)
90
- aws-partitions (1.1222.0)
91
- aws-sdk-core (3.243.0)
94
+ aws-partitions (1.1262.0)
95
+ aws-sdk-core (3.252.0)
92
96
  aws-eventstream (~> 1, >= 1.3.0)
93
97
  aws-partitions (~> 1, >= 1.992.0)
94
98
  aws-sigv4 (~> 1.9)
@@ -96,15 +100,15 @@ GEM
96
100
  bigdecimal
97
101
  jmespath (~> 1, >= 1.6.1)
98
102
  logger
99
- aws-sdk-kms (1.122.0)
100
- aws-sdk-core (~> 3, >= 3.241.4)
103
+ aws-sdk-kms (1.129.0)
104
+ aws-sdk-core (~> 3, >= 3.248.0)
101
105
  aws-sigv4 (~> 1.5)
102
- aws-sdk-s3 (1.215.0)
103
- aws-sdk-core (~> 3, >= 3.243.0)
106
+ aws-sdk-s3 (1.226.0)
107
+ aws-sdk-core (~> 3, >= 3.248.0)
104
108
  aws-sdk-kms (~> 1)
105
109
  aws-sigv4 (~> 1.5)
106
- aws-sdk-sqs (1.111.0)
107
- aws-sdk-core (~> 3, >= 3.241.4)
110
+ aws-sdk-sqs (1.116.0)
111
+ aws-sdk-core (~> 3, >= 3.248.0)
108
112
  aws-sigv4 (~> 1.5)
109
113
  aws-sigv4 (1.12.1)
110
114
  aws-eventstream (~> 1, >= 1.0.2)
@@ -118,7 +122,7 @@ GEM
118
122
  i18n
119
123
  bcrypt (3.1.21)
120
124
  benchmark (0.3.0)
121
- bigdecimal (4.0.1)
125
+ bigdecimal (4.1.2)
122
126
  bindex (0.8.1)
123
127
  bixby (5.0.2)
124
128
  rubocop (= 1.28.2)
@@ -300,8 +304,9 @@ GEM
300
304
  equivalent-xml (0.6.0)
301
305
  nokogiri (>= 1.4.3)
302
306
  erubi (1.12.0)
303
- ethon (0.15.0)
307
+ ethon (0.18.0)
304
308
  ffi (>= 1.15.0)
309
+ logger
305
310
  execjs (2.9.1)
306
311
  factory_bot (4.11.1)
307
312
  activesupport (>= 3.0.0)
@@ -495,7 +500,7 @@ GEM
495
500
  json-schema (6.2.0)
496
501
  addressable (~> 2.8)
497
502
  bigdecimal (>= 3.1, < 5)
498
- jwt (2.10.2)
503
+ jwt (2.10.3)
499
504
  base64
500
505
  kaminari (1.2.2)
501
506
  activesupport (>= 4.1.0)
@@ -584,7 +589,7 @@ GEM
584
589
  mime-types (3.7.0)
585
590
  logger
586
591
  mime-types-data (~> 3.2025, >= 3.2025.0507)
587
- mime-types-data (3.2026.0303)
592
+ mime-types-data (3.2026.0414)
588
593
  mini_magick (4.13.2)
589
594
  mini_mime (1.1.5)
590
595
  mini_portile2 (2.8.5)
@@ -616,13 +621,16 @@ GEM
616
621
  nokogiri (1.15.5)
617
622
  mini_portile2 (~> 2.8.2)
618
623
  racc (~> 1.4)
619
- oauth (1.1.3)
624
+ oauth (1.1.7)
625
+ auth-sanitizer (~> 0.2, >= 0.2.1)
620
626
  base64 (~> 0.1)
621
- oauth-tty (~> 1.0, >= 1.0.6)
622
- snaky_hash (~> 2.0)
623
- version_gem (~> 1.1, >= 1.1.9)
624
- oauth-tty (1.0.6)
625
- version_gem (~> 1.1, >= 1.1.9)
627
+ oauth-tty (~> 1.0, >= 1.0.10)
628
+ snaky_hash (~> 2.0, >= 2.0.5)
629
+ version_gem (~> 1.1, >= 1.1.12)
630
+ oauth-tty (1.0.12)
631
+ anonymous_loader (~> 0.1, >= 0.1.1)
632
+ auth-sanitizer (~> 0.2, >= 0.2.2)
633
+ version_gem (~> 1.1, >= 1.1.12)
626
634
  oauth2 (1.4.11)
627
635
  faraday (>= 0.17.3, < 3.0)
628
636
  jwt (>= 1.0, < 3.0)
@@ -882,7 +890,7 @@ GEM
882
890
  simplecov-html (~> 0.10.0)
883
891
  simplecov-html (0.10.2)
884
892
  slop (4.10.1)
885
- snaky_hash (2.0.3)
893
+ snaky_hash (2.0.6)
886
894
  hashie (>= 0.1.0, < 6)
887
895
  version_gem (>= 1.1.8, < 3)
888
896
  solargraph (0.48.0)
@@ -955,8 +963,8 @@ GEM
955
963
  actionpack (>= 3.1)
956
964
  jquery-rails
957
965
  railties (>= 3.1)
958
- typhoeus (1.5.0)
959
- ethon (>= 0.9.0, < 0.16.0)
966
+ typhoeus (1.6.0)
967
+ ethon (>= 0.18.0)
960
968
  tzinfo (1.2.11)
961
969
  thread_safe (~> 0.1)
962
970
  uber (0.1.0)
@@ -979,7 +987,7 @@ GEM
979
987
  rdf-vocab
980
988
  reform (~> 2.2)
981
989
  reform-rails
982
- version_gem (1.1.9)
990
+ version_gem (1.1.13)
983
991
  warden (1.2.9)
984
992
  rack (>= 2.0.9)
985
993
  web-console (3.7.0)
data/README.md CHANGED
@@ -121,16 +121,50 @@ We created IiifPrint with an assumption of ActiveFedora. However, as Hyrax now
121
121
 
122
122
  ### IIIF URL configuration
123
123
 
124
- If you set `EXTERNAL_IIIF_URL` in your environment, IiifPrint will use that URL as the root for your IIIF URLs. It will also switch from using the file set ID to using the file's checksum hex digest as the identifier. In Wings/Fedora mode the digest is stored as a `urn:sha1:HEX` string; in Valkyrie mode it is stored as a plain hex string — IiifPrint handles both automatically.
124
+ If you set `EXTERNAL_IIIF_URL` in your environment, IiifPrint will use that URL as the root for your IIIF URLs and will build the IIIF identifier from the file's storage key rather than its Hyrax file set ID.
125
125
 
126
- This enables using serverless_iiif or Cantaloupe (referred to as the service) by pointing the service to the same S3 bucket that the repository writes uploaded files to. By setting it up that way you do not need the service to connect to Fedora or Hyrax at all — both natively support connecting to an S3 bucket to retrieve file content.
126
+ This enables using serverless-iiif or Cantaloupe by pointing the service directly at the same S3 bucket the repository writes uploaded files to no Fedora or Hyrax connection required.
127
127
 
128
- If your S3 bucket organises files under a folder prefix (e.g. a `staging/` or `production/` subfolder), set `IIIF_S3_FOLDER_PREFIX` to that prefix. IiifPrint will prepend it to the digest identifier and percent-encode the separator so the combined value is a single IIIF path segment:
128
+ #### How the identifier is chosen
129
+
130
+ The identifier depends on your persistence layer:
131
+
132
+ **Valkyrie storage (e.g. Shrine + S3)**
133
+
134
+ IiifPrint indexes a `storage_file_identifier` on each FileSet at ingest time. This is the storage backend's native key with its protocol prefix stripped — for example `shrine://file_set_id/uuid` becomes `file_set_id/uuid`, which is the S3 object key. This approach works with any Valkyrie storage adapter that writes to S3.
135
+
136
+ Your IIIF service (serverless-iiif, Cantaloupe, etc.) must be configured to resolve identifiers as S3 object keys against the same bucket. For serverless-iiif this means setting `resolver_template` to `"%s"` so the identifier is used as-is.
137
+
138
+ After enabling `EXTERNAL_IIIF_URL`, existing FileSets must be reindexed so their `storage_file_identifier` is populated in Solr. Until then the presenter falls back to the checksum digest (see below).
139
+
140
+ **Wings/Fedora (ActiveFedora)**
141
+
142
+ IiifPrint uses the file's checksum hex digest as the identifier. Fedora stores files in S3 using the SHA-1 digest as the object key, and stores the digest in Solr as `urn:sha1:HEX`; IiifPrint strips the `urn:sha1:` prefix automatically. No special resolver configuration is needed.
143
+
144
+ **Fallback**
145
+
146
+ If no `storage_file_identifier` is indexed for a FileSet (e.g. before reindexing, or on ActiveFedora), IiifPrint falls back to the checksum hex digest.
147
+
148
+ #### Folder prefix
149
+
150
+ If your S3 bucket organises files under a subfolder (e.g. `staging/` or `production/`), set `IIIF_S3_FOLDER_PREFIX` to that prefix. IiifPrint prepends it to the identifier and percent-encodes the separator so the combined value is a single IIIF path segment:
129
151
 
130
152
  ```
131
153
  EXTERNAL_IIIF_URL=https://iiif.example.org
132
154
  IIIF_S3_FOLDER_PREFIX=staging
133
- # → identifier used: staging%2F<hex-digest>
155
+ # Valkyrie: → identifier used: staging%2F<file_set_id/uuid>
156
+ # Fedora: → identifier used: staging%2F<sha1-hex>
157
+ ```
158
+
159
+ #### Configuration via initializer
160
+
161
+ These can also be set in `config/initializers/iiif_print.rb` instead of environment variables:
162
+
163
+ ```ruby
164
+ IiifPrint.config do |config|
165
+ config.external_iiif_url = 'https://iiif.example.org'
166
+ config.iiif_s3_folder_prefix = 'staging'
167
+ end
134
168
  ```
135
169
 
136
170
  ### Model level configurations
@@ -23,6 +23,7 @@ module IiifPrint
23
23
  # index for full text search
24
24
  solr_doc['all_text_tsimv'] = solr_doc['all_text_timv'] = all_text(object)
25
25
  solr_doc['digest_ssim'] = find_checksum(object)
26
+ solr_doc['storage_file_identifier_ss'] = find_storage_file_identifier(object)
26
27
  end
27
28
 
28
29
  def image?(object)
@@ -44,6 +45,15 @@ module IiifPrint
44
45
  digest.to_s
45
46
  end
46
47
 
48
+ def find_storage_file_identifier(object)
49
+ file = object.try(:original_file)
50
+ return unless file.is_a?(Hyrax::FileMetadata)
51
+ identifier = file.file_identifier.to_s
52
+ # Strip whatever protocol prefix the storage backend adds (e.g. shrine://, disk://)
53
+ # leaving the backend-specific path/key.
54
+ identifier.sub(%r{\A\w+://}, '').presence
55
+ end
56
+
47
57
  def all_text(object)
48
58
  file = object.original_file
49
59
  return unless file
@@ -50,6 +50,7 @@ SolrDocument.prepend(IiifPrint::SolrDocumentDecorator)
50
50
  SolrDocument.attribute :is_child, Hyrax::SolrDocument::Metadata::Solr::String, 'is_child_bsi'
51
51
  SolrDocument.attribute :split_from_pdf_id, Hyrax::SolrDocument::Metadata::Solr::String, 'split_from_pdf_id_ssi'
52
52
  SolrDocument.attribute :digest, Hyrax::SolrDocument::Metadata::Solr::String, 'digest_ssim'
53
+ SolrDocument.attribute :storage_file_identifier, Hyrax::SolrDocument::Metadata::Solr::String, 'storage_file_identifier_ss'
53
54
 
54
55
  # @note These properties came from the newspaper_works gem. They are configurable.
55
56
  SolrDocument.class_attribute :iiif_print_solr_field_names, default: %w[alternative_title genre
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+ require 'cgi'
3
+
4
+ module IiifPrint
5
+ # Presenter for FileSets when an external IIIF server is configured.
6
+ # Returned by IiifManifestPresenter.for when IiifPrint.config.external_iiif_url is set.
7
+ # Overrides only the three methods that control URL construction.
8
+ class ExternalIiifDisplayImagePresenter < Hyrax::IiifManifestPresenter::DisplayImagePresenter
9
+ def display_image_url(_base_url = nil)
10
+ url_builder = Hyrax.config.iiif_image_url_builder
11
+ args = [latest_file_id, IiifPrint.config.external_iiif_url, Hyrax.config.iiif_image_size_default]
12
+ args << image_format(alpha_channels) if url_builder.arity == 4
13
+ url_builder.call(*args).gsub(%r{images/}, '')
14
+ end
15
+
16
+ def iiif_endpoint(_file_id = nil, _base_url: nil)
17
+ IIIFManifest::IIIFEndpoint.new(
18
+ File.join(IiifPrint.config.external_iiif_url, latest_file_id),
19
+ profile: Hyrax.config.iiif_image_compliance_level_uri
20
+ )
21
+ end
22
+
23
+ private
24
+
25
+ def latest_file_id
26
+ @latest_file_id ||= begin
27
+ hex = model.storage_file_identifier || digest_hex
28
+ return nil if hex.blank?
29
+
30
+ prefix = IiifPrint.config.iiif_s3_folder_prefix.presence
31
+ prefix ? CGI.escape("#{prefix}/#{hex}") : CGI.escape(hex)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,109 +1,44 @@
1
- require 'cgi'
2
-
3
- # mixin to provide URL for IIIF Content Search service
4
1
  module IiifPrint
5
2
  module IiifManifestPresenter
6
- # OVERRIDE: Hyrax v3.x
7
3
  module DisplayImagePresenterDecorator
8
- extend ActiveSupport::Concern
9
- # Extending the presenter to the base url which includes the protocol.
10
- # We need the base url to render the facet links and normalize the interface.
4
+ # Hyrax defines attr_writer :hostname but not the reader; we add the full accessor.
11
5
  attr_accessor :base_url
12
6
 
13
- # Extending this class because there is an #ability= but not #ability and this definition
14
- # mirrors the Hyrax::IiifManifestPresenter#ability.
15
- def ability
16
- @ability ||= NullAbility.new
17
- end
18
-
19
- def display_image
20
- return nil unless latest_file_id
21
- return nil unless model.image?
22
- return nil unless IiifPrint.config.default_iiif_manifest_version == 2
23
-
24
- IIIFManifest::DisplayImage
25
- .new(display_image_url(hostname),
26
- format: image_format(alpha_channels),
27
- width: width,
28
- height: height,
29
- iiif_endpoint: iiif_endpoint(latest_file_id, base_url: hostname))
30
- end
31
-
32
- # OVERRIDE: IIIF Hyrax AV v0.2 #display_content for prez 3 manifests
33
- def display_content
34
- return nil unless latest_file_id
35
- return super unless model.image?
36
-
37
- IIIFManifest::V3::DisplayContent
38
- .new(display_image_url(hostname),
39
- format: image_format(alpha_channels),
40
- width: width,
41
- height: height,
42
- type: 'Image',
43
- iiif_endpoint: iiif_endpoint(latest_file_id, base_url: hostname))
44
- end
45
-
46
- def display_image_url(base_url)
47
- if ENV['EXTERNAL_IIIF_URL'].present?
48
- # At the moment we are only concerned about Hyrax's default image url builder
49
- iiif_image_url_builder(url_builder: Hyrax.config.iiif_image_url_builder)
50
- else
51
- super
52
- end
53
- end
7
+ # The three methods below are defined directly on DisplayImagePresenter in recent Hyrax.
8
+ # We keep them here as fallbacks for older Hyrax versions via `super rescue NoMethodError`:
9
+ # - Recent Hyrax: super succeeds silently, no deprecation noise.
10
+ # - Older Hyrax: super raises NoMethodError, deprecation fires, fallback value returned.
11
+ # These fallbacks will be removed in the next major version of iiif_print.
54
12
 
55
- def iiif_endpoint(file_id, base_url: request.base_url)
56
- if ENV['EXTERNAL_IIIF_URL'].present?
57
- IIIFManifest::IIIFEndpoint.new(
58
- File.join(ENV['EXTERNAL_IIIF_URL'], file_id),
59
- profile: Hyrax.config.iiif_image_compliance_level_uri
60
- )
61
- else
62
- super
63
- end
13
+ def ability
14
+ super
15
+ rescue NoMethodError
16
+ Deprecation.warn(self.class,
17
+ "IiifPrint is providing #ability as a fallback for older Hyrax versions. " \
18
+ "This fallback will be removed in the next major iiif_print version — " \
19
+ "please upgrade to a version of Hyrax that defines this method on DisplayImagePresenter.")
20
+ @ability ||= Hyrax::IiifManifestPresenter::NullAbility.new
64
21
  end
65
22
 
66
23
  def hostname
24
+ super
25
+ rescue NoMethodError
26
+ Deprecation.warn(self.class,
27
+ "IiifPrint is providing #hostname as a fallback for older Hyrax versions. " \
28
+ "This fallback will be removed in the next major iiif_print version — " \
29
+ "please upgrade to a version of Hyrax that defines this method on DisplayImagePresenter.")
67
30
  @hostname || 'localhost'
68
31
  end
69
32
 
70
- ##
71
- # @return [Boolean] false
72
33
  def work?
34
+ super
35
+ rescue NoMethodError
36
+ Deprecation.warn(self.class,
37
+ "IiifPrint is providing #work? as a fallback for older Hyrax versions. " \
38
+ "This fallback will be removed in the next major iiif_print version — " \
39
+ "please upgrade to a version of Hyrax that defines this method on DisplayImagePresenter.")
73
40
  false
74
41
  end
75
-
76
- private
77
-
78
- def latest_file_id
79
- if ENV['EXTERNAL_IIIF_URL'].present?
80
- external_latest_file_id
81
- else
82
- super
83
- end
84
- end
85
-
86
- def external_latest_file_id
87
- hex = digest_hex
88
- return nil if hex.blank?
89
-
90
- prefix = ENV['IIIF_S3_FOLDER_PREFIX'].presence
91
- return hex unless prefix
92
-
93
- CGI.escape("#{prefix}/#{hex}")
94
- end
95
-
96
- def iiif_image_url_builder(url_builder:)
97
- args = [
98
- latest_file_id,
99
- ENV['EXTERNAL_IIIF_URL'],
100
- Hyrax.config.iiif_image_size_default
101
- ]
102
- # In Hyrax 3, Hyrax.config.iiif_image_url_builder takes an additional argument
103
- args << image_format(alpha_channels) if url_builder.arity == 4
104
-
105
- url_builder.call(*args).gsub(%r{images/}, '')
106
- end
107
42
  end
108
43
  end
109
44
  end
@@ -32,4 +32,18 @@ module IiifPrint
32
32
  end
33
33
  end
34
34
  end
35
+
36
+ module IiifPrint
37
+ # Overrides the .for factory to return ExternalIiifDisplayImagePresenter for FileSets
38
+ # when an external IIIF server is configured, instead of the default DisplayImagePresenter.
39
+ module IiifManifestPresenterFactoryDecorator
40
+ def for(model)
41
+ return super unless model.file_set? && IiifPrint.config.external_iiif_url.present?
42
+
43
+ IiifPrint::ExternalIiifDisplayImagePresenter.new(model)
44
+ end
45
+ end
46
+ end
47
+
35
48
  Hyrax::IiifManifestPresenter.prepend(IiifPrint::IiifManifestPresenterDecorator)
49
+ Hyrax::IiifManifestPresenter.singleton_class.prepend(IiifPrint::IiifManifestPresenterFactoryDecorator)
@@ -134,6 +134,16 @@ module IiifPrint
134
134
  @default_iiif_manifest_version.presence || 2
135
135
  end
136
136
 
137
+ attr_writer :external_iiif_url
138
+ def external_iiif_url
139
+ @external_iiif_url || ENV['EXTERNAL_IIIF_URL']
140
+ end
141
+
142
+ attr_writer :iiif_s3_folder_prefix
143
+ def iiif_s3_folder_prefix
144
+ @iiif_s3_folder_prefix || ENV['IIIF_S3_FOLDER_PREFIX']
145
+ end
146
+
137
147
  attr_writer :metadata_fields
138
148
  # rubocop:disable Metrics/MethodLength
139
149
  # @api private
@@ -1,3 +1,3 @@
1
1
  module IiifPrint
2
- VERSION = '3.1.0'.freeze
2
+ VERSION = '3.1.1'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iiif_print
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Upton
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2026-06-16 00:00:00.000000000 Z
17
+ date: 2026-06-23 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: blacklight_iiif_search
@@ -380,6 +380,7 @@ files:
380
380
  - app/models/iiif_print/iiif_search_response_decorator.rb
381
381
  - app/models/iiif_print/ingest_file_relation.rb
382
382
  - app/models/iiif_print/pending_relationship.rb
383
+ - app/presenters/iiif_print/external_iiif_display_image_presenter.rb
383
384
  - app/presenters/iiif_print/file_set_presenter_decorator.rb
384
385
  - app/presenters/iiif_print/iiif_manifest_presenter/display_image_presenter_decorator.rb
385
386
  - app/presenters/iiif_print/iiif_manifest_presenter_decorator.rb