wax_iiif 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +25 -0
  4. data/.travis.yml +2 -2
  5. data/Gemfile +3 -2
  6. data/README.md +4 -4
  7. data/lib/wax_iiif.rb +34 -44
  8. data/lib/wax_iiif/base_properties.rb +90 -0
  9. data/lib/wax_iiif/builder.rb +252 -0
  10. data/lib/wax_iiif/collection.rb +58 -0
  11. data/lib/{iiif_s3 → wax_iiif}/config.rb +46 -60
  12. data/lib/{iiif_s3 → wax_iiif}/errors.rb +6 -12
  13. data/lib/{iiif_s3 → wax_iiif}/full_image.rb +3 -8
  14. data/lib/wax_iiif/image_info.rb +89 -0
  15. data/lib/{iiif_s3 → wax_iiif}/image_record.rb +48 -63
  16. data/lib/{iiif_s3 → wax_iiif}/image_tile.rb +8 -14
  17. data/lib/{iiif_s3 → wax_iiif}/image_variant.rb +20 -38
  18. data/lib/wax_iiif/manifest.rb +151 -0
  19. data/lib/{iiif_s3 → wax_iiif}/thumbnail.rb +5 -9
  20. data/lib/{iiif_s3 → wax_iiif}/utilities.rb +5 -5
  21. data/lib/{iiif_s3 → wax_iiif}/utilities/helpers.rb +18 -48
  22. data/lib/{iiif_s3 → wax_iiif}/utilities/pdf_splitter.rb +20 -23
  23. data/spec/base_properties_spec.rb +8 -12
  24. data/spec/shared_contexts.rb +28 -92
  25. data/spec/spec_helper.rb +3 -3
  26. data/spec/wax_iiif/builder_spec.rb +143 -0
  27. data/spec/wax_iiif/collection_spec.rb +53 -0
  28. data/spec/wax_iiif/config_spec.rb +15 -0
  29. data/spec/wax_iiif/image_info_spec.rb +57 -0
  30. data/spec/wax_iiif/image_record_spec.rb +82 -0
  31. data/spec/wax_iiif/image_variant_spec.rb +66 -0
  32. data/spec/wax_iiif/manifest_spec.rb +97 -0
  33. data/spec/wax_iiif/utilities/pdf_splitter_spec.rb +14 -0
  34. data/wax_iiif.gemspec +15 -19
  35. metadata +52 -97
  36. data/Guardfile +0 -10
  37. data/Rakefile +0 -24
  38. data/lib/iiif_s3/base_properties.rb +0 -95
  39. data/lib/iiif_s3/builder.rb +0 -241
  40. data/lib/iiif_s3/collection.rb +0 -61
  41. data/lib/iiif_s3/image_info.rb +0 -96
  42. data/lib/iiif_s3/manifest.rb +0 -151
  43. data/lib/iiif_s3/version.rb +0 -5
  44. data/spec/iiif_s3/builder_spec.rb +0 -152
  45. data/spec/iiif_s3/collection_spec.rb +0 -68
  46. data/spec/iiif_s3/config_spec.rb +0 -15
  47. data/spec/iiif_s3/image_info_spec.rb +0 -57
  48. data/spec/iiif_s3/image_record_spec.rb +0 -96
  49. data/spec/iiif_s3/image_variant_spec.rb +0 -68
  50. data/spec/iiif_s3/manifest_spec.rb +0 -97
  51. data/spec/iiif_s3/utilities/pdf_splitter_spec.rb +0 -17
  52. data/test.rb +0 -77
data/Guardfile DELETED
@@ -1,10 +0,0 @@
1
- clearing :on
2
-
3
-
4
- # Run the test suite
5
- guard :rspec, cmd: 'bundle exec rspec' do
6
- watch(%r{^spec/.+_spec\.rb$})
7
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
8
- watch('spec/spec_helper.rb') { "spec" }
9
- end
10
-
data/Rakefile DELETED
@@ -1,24 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
- require 'yard'
4
-
5
- RSpec::Core::RakeTask.new(:spec)
6
-
7
- YARD::Rake::YardocTask.new do |t|
8
- t.files = ['lib/**/*.rb'] # optional
9
- t.stats_options = ['--list-undoc'] # optional
10
- end
11
-
12
- desc "Clear the screen"
13
- task :cls do
14
- puts "Clearing the Screen \033c"
15
- end
16
-
17
- task :full do
18
- ENV["TEST_INTERNET_CONNECTIVITY"] = "yes"
19
- ENV["SKIP_EXPENSIVE_TESTS"] = nil
20
- end
21
-
22
- task :default => [:cls, :spec, :yard]
23
-
24
- task :full_test => [:full, :default]
@@ -1,95 +0,0 @@
1
- require_relative "utilities"
2
- module IiifS3
3
-
4
- # Module BaseProperties provides the set of properties that are shared across
5
- # all IIIF types. It is not a comprehensive list (yet), but it does handle
6
- # most of the shared types. It does not include any types that have class-based
7
- # restrictions.
8
- #
9
- # It also performs some basic sanity checking on (some of) the fields, and
10
- # provides utility classes that are applicable across the fields.
11
- #
12
- # @todo Add within, service, seeAlso, viewingHint fields
13
- #
14
- # @author David Newbury <david.newbury@gmail.com>
15
- #
16
- module BaseProperties
17
- include Utilities::Helpers
18
-
19
-
20
- # @!attribute [rw] label
21
- # @return [String] The human-readable label for this record
22
- attr_accessor :label
23
- # @!attribute [r] id
24
- # @return [String] The URI for this record
25
- attr_reader :id
26
- # @!attribute [rw] description
27
- # @return [String] The long-form description of this record
28
- attr_accessor :description
29
- # @!attribute [rw] metadata
30
- # @return [Hash] A set of key/value pairs describing additional metadata for the object.
31
- attr_accessor :metadata
32
- # @!attribute [rw] attribution
33
- # @return [String] a human-readable label, typically used for attribution or credit.
34
- attr_accessor :attribution
35
- # @!attribute [rw] logo
36
- # @return [String] The URI to an image for the logo of the institution associated with this record.
37
- attr_accessor :logo
38
- # @!attribute [rw] license
39
- # @return [String] The URI to a resource that describes the license or rights statement associated.
40
- attr_accessor :license
41
- # @!attribute [rw] related
42
- # @return [String, Array<String>] The URI to related resources. Can be both a string or an array
43
- attr_accessor :related
44
-
45
- # The type of resource provided by this record.
46
- #
47
- # @return [String] The type of record
48
- def type
49
- self.class::TYPE
50
- end
51
-
52
- # Set the unique id for this record.
53
- # This will automatically append the defined prefixes and suffixes.
54
- #
55
- # @param [String] _id The unique portion of this ID
56
- # @return [string] The URI for this record
57
- def id=(_id)
58
- @id = generate_id(_id)
59
- end
60
-
61
- # Return the base data structure for this record as a Hash
62
- # This will be in IIIF format, and should convert to JSON as JSON-LD nicely.
63
- #
64
- # @return [Hash] The base properties of this record
65
- def base_properties
66
- obj = {
67
- "@context" => PRESENTATION_CONTEXT,
68
- "@id" => self.id,
69
- "@type" => self.type,
70
- "label" => self.label
71
- }
72
- obj["attribution"] = self.attribution if self.attribution
73
- obj["logo"] = self.logo if self.logo
74
- obj["description"] = self.description if self.description
75
- obj["attribution"] = self.attribution if self.attribution
76
- obj["license"] = self.license if self.license
77
- obj["related"] = self.related if self.related
78
- obj["metadata"] = self.metadata if self.metadata
79
-
80
- obj
81
- end
82
-
83
- # Save the JSON representation of this record to disk and to S3 (if enabled).
84
- #
85
- # @return [Void]
86
- def save
87
- save_to_disk(JSON.parse(self.to_json))
88
- end
89
-
90
-
91
-
92
- protected
93
-
94
- end
95
- end
@@ -1,241 +0,0 @@
1
- require_relative "utilities"
2
- require 'pathname'
3
-
4
- module IiifS3
5
- class Builder
6
-
7
- include Utilities::Helpers
8
-
9
- HEADER_VAL = 'filename'
10
-
11
- #
12
- # @!attribute [r] data
13
- # @return [Array<Hash>] The raw data computed for the given set of images
14
- attr_reader :data
15
-
16
- #
17
- # @!attribute [r] manifests
18
- # @return [Array<Hash>] The manifest hashes for this configuration
19
- attr_accessor :manifests
20
-
21
- # @!attribute [r] config
22
- # @return [IiifS3::Config] The configuration object
23
- attr_reader :config
24
-
25
- # Initialize the builder.
26
- #
27
- # @param [Hash] config an optional configuration object.
28
- # @see IiifS3::Config
29
- # @return [Void]
30
- #
31
- def initialize(config = {})
32
- @manifests = []
33
- @config = IiifS3::Config.new(config)
34
- end
35
-
36
-
37
- #
38
- # Load data into the IIIF builder.
39
- #
40
- # This will load the data, perform some basic verifications on it, and sort
41
- # it into proper order.
42
- #
43
- # @param [Array<ImageRecord>, ImageRecord] data
44
- # Either a single ImageRecord or an Array of ImageRecords.
45
- # @raise [IiifS3::Error::InvalidImageData] if any of the data does
46
- # not pass the validation checks
47
- #
48
- # @return [Void]
49
- #
50
- def load(data)
51
- @data = [data].flatten # handle hashes and arrays of hashes
52
-
53
- # validate
54
- @data.each do |image_record|
55
- raise IiifS3::Error::InvalidImageData, "Image record #{image_record.inspect} is not an ImageRecord" unless image_record.is_a? ImageRecord
56
- raise IiifS3::Error::InvalidImageData, "Image record #{image_record.inspect} does not have an ID and/or a page number" if image_record.id.nil? || image_record.page_number.nil?
57
- end
58
- end
59
-
60
-
61
- #
62
- # Take the loaded data and generate all the files.
63
- #
64
- # @param [Boolean] force_image_generation Generate images even if they already exist
65
- #
66
- # @return [Void]
67
- #
68
- def process_data(force_image_generation=false)
69
- return nil if @data.nil? # do nothing without data.
70
- @manifests = []
71
-
72
- resources = {}
73
- @data.each do |image_record|
74
-
75
- # image generation
76
- #
77
- # It attempts to load the info files and skip generation — not currently working.
78
- info_file = image_info_file_name(image_record)
79
- if (File.exist?(info_file) && !force_image_generation)
80
- puts "skipping #{info_file}" if @config.verbose?
81
- image_record.variants = load_variants(info_file)
82
- else
83
- image_record.variants = generate_variants(image_record, @config)
84
- generate_tiles(image_record, @config)
85
- generate_image_json(image_record, @config)
86
- end
87
- # Save the image info for the manifest
88
- resources[image_record.id] ||= []
89
- resources[image_record.id].push image_record
90
- end
91
-
92
- # Generate the manifests
93
- resources.each do |key, val|
94
- manifests.push generate_manifest(val, @config)
95
- end
96
-
97
- generate_collection
98
- end
99
-
100
- def generate_collection(label="top")
101
- collection = Collection.new(label,@config)
102
- manifests.each{|m| collection.add_manifest(m)}
103
- collection.save
104
- end
105
-
106
- # Creates the required directories for exporting to the file system.
107
- #
108
- # @return [Void]
109
- def create_build_directories
110
- root_dir = generate_build_location("")
111
- Dir.mkdir root_dir unless Dir.exists?(root_dir)
112
- img_dir = generate_image_location("","").split("/")[0...-1].join("/")
113
- Dir.mkdir img_dir unless Dir.exists?(img_dir)
114
- end
115
-
116
- # Load data into the IIIF server from a CSV
117
- #
118
- # @param [String] csv_path Path to the CSV file containing the image data
119
- #
120
- # @return [Void]
121
- # @todo Fix this to use the correct data format!
122
- #
123
- def load_csv(csv_path)
124
- raise Error::InvalidCSV unless File.exist? csv_path
125
- begin
126
- vals = CSV.read(csv_path)
127
- rescue CSV::MalformedCSVError
128
- raise Error::InvalidCSV
129
- end
130
-
131
- raise Error::BlankCSV if vals.length == 0
132
- raise Error::InvalidCSV if vals[0].length != 3
133
-
134
- # remove optional header
135
- vals.shift if vals[0][0] == HEADER_VAL
136
-
137
- @data = vals.collect do |data|
138
- {
139
- "image_path" => data[0],
140
- "id" => data[1],
141
- "label" => data[2]
142
- }
143
- end
144
- end
145
-
146
- protected
147
-
148
- #----------------------------------------------------------------
149
- def load_variants(path)
150
-
151
- data = JSON.parse File.read(path)
152
- id = data["@id"]
153
- w = data["width"]
154
- h = data["height"]
155
- thumb_size = data["sizes"].find{|a| a["width"] == config.thumbnail_size || a["height"] == config.thumbnail_size}
156
- thumb_w = thumb_size["width"]
157
- thumb_h = thumb_size["height"]
158
- full_url = "#{id}/full/full/0/default.jpg"
159
- thumb_url = "#{id}/full/#{thumb_w},/0/default.jpg"
160
- full = FakeImageVariant.new( id,w, h,full_url, "image/jpeg")
161
- thumbnail = FakeImageVariant.new( id, thumb_w, thumb_h, thumb_url, "image/jpeg")
162
- return {"full" => full, "thumbnail" => thumbnail}
163
- end
164
-
165
- def generate_tiles(data, config)
166
- width = data.variants["full"].width
167
- tile_width = config.tile_width
168
- height = data.variants["full"].height
169
- tiles = []
170
- config.tile_scale_factors.each do |s|
171
- (0..(height*1.0/(tile_width*s)).floor).each do |tileY|
172
- (0..(width*1.0/(tile_width*s)).floor).each do |tileX|
173
- tile = {
174
- scale_factor: s,
175
- xpos: tileX,
176
- ypos: tileY,
177
- x: tileX * tile_width * s,
178
- y: tileY * tile_width * s,
179
- width: tile_width * s,
180
- height: tile_width * s,
181
- xSize: tile_width,
182
- ySize: tile_width
183
- }
184
- if (tile[:x] + tile[:width] > width)
185
- tile[:width] = width - tile[:x]
186
- tile[:xSize] = (tile[:width]/(s*1.0)).ceil
187
- end
188
- if (tile[:y] + tile[:height] > height)
189
- tile[:height] = height - tile[:y]
190
- tile[:ySize] = (tile[:height]/(s*1.0)).ceil
191
- end
192
- tiles.push(tile)
193
- end
194
- end
195
- end
196
- tiles.each do |tile|
197
- ImageTile.new(data, @config, tile)
198
- end
199
- end
200
-
201
- def image_info_file_name(data)
202
- "#{generate_image_location(data.id,data.page_number)}/info.json"
203
- end
204
-
205
- def generate_image_json(data, config)
206
- filename = image_info_file_name(data)
207
- info = ImageInfo.new(data.variants["full"].id, data.variants ,config.tile_width, config.tile_scale_factors)
208
-
209
- puts "writing #{filename}" if config.verbose?
210
- Pathname.new(Pathname.new(filename).dirname).mkpath
211
- File.open(filename, "w") do |file|
212
- file.puts info.to_json
213
- end
214
- if @config.upload_to_s3
215
- add_file_to_s3(filename)
216
- add_default_redirect(filename)
217
- end
218
- return info
219
- end
220
-
221
-
222
- def generate_manifest(data, config)
223
- m = Manifest.new(data, config)
224
- m.save_all_files_to_disk
225
- return m
226
- end
227
-
228
-
229
- def generate_variants(data, config)
230
- obj = {
231
- "full" => FullImage.new(data, config),
232
- "thumbnail" => Thumbnail.new(data, config)
233
- }
234
-
235
- config.variants.each do |key,image_size|
236
- obj[key] = ImageVariant.new(data, config, image_size)
237
- end
238
- return obj
239
- end
240
- end
241
- end
@@ -1,61 +0,0 @@
1
- module IiifS3
2
-
3
- #
4
- # Class Collection is an abstraction over the IIIF Collection, which is an aggregation
5
- # of IIIF manifests.
6
- #
7
- # @author David Newbury <david.newbury@gmail.com>
8
- #
9
- class Collection
10
-
11
- # @return [String] The IIIF Type for collections
12
- TYPE = "sc:Collection"
13
-
14
- include BaseProperties
15
- attr_reader :collections, :manifests
16
-
17
- def initialize(label, config, name="top")
18
- raise IiifS3::Error::MissingCollectionName if label.nil? || label.empty?
19
- @config = config
20
- @manifests = []
21
- @collections = []
22
- self.label = label
23
- self.id = "collection/#{name}"
24
- end
25
-
26
- def add_collection(collection)
27
- raise IiifS3::Error::NotACollection unless collection.respond_to?(:type) && collection.type == Collection::TYPE
28
- @collections.push(collection)
29
- end
30
-
31
- def add_manifest(manifest)
32
- raise IiifS3::Error::NotAManifest unless manifest.respond_to?(:type) && manifest.type == Manifest::TYPE
33
- @manifests.push(manifest)
34
- end
35
-
36
- # The JSON representation of this collection in the IIIF-expected format
37
- #
38
- #
39
- # @return [String] The JSON representation as a string
40
- #
41
- def to_json
42
- obj = base_properties
43
- obj["collections"] = collect_object(collections) unless collections.empty?
44
- obj["manifests"] = collect_object(manifests) unless manifests.empty?
45
- JSON.pretty_generate obj
46
- end
47
-
48
- protected
49
-
50
- def collect_object(things)
51
- things.collect do |thing|
52
- {
53
- "@id" => thing.id,
54
- "@type" => thing.type,
55
- "label" => thing.label
56
- }
57
- end
58
- end
59
- end
60
- end
61
-
@@ -1,96 +0,0 @@
1
- module IiifS3
2
-
3
- #
4
- # Class ImageInfo is a data object for the JSON representation of the image.
5
- #
6
- # It is designed to support the http://iiif.io/api/image/2.0/#image-information spec.
7
- class ImageInfo
8
-
9
- attr_accessor :id
10
- attr_accessor :width
11
- attr_accessor :height
12
- attr_accessor :tile_width
13
- attr_accessor :tile_scale_factors
14
-
15
- def initialize(uri, variants, tile_width= nil, tile_scale_factors = nil)
16
-
17
- raise IiifS3::Error::InvalidImageData, "No full variant provided: variants: #{variants}" unless variants["full"]
18
- raise IiifS3::Error::InvalidImageData, "No thumbnail variant provided: variants: #{variants}" unless variants["thumbnail"]
19
- raise IiifS3::Error::InvalidImageData, "No URI was provided for this image!" if uri.nil?
20
-
21
- @id = uri
22
- full = variants["full"]
23
- @variants = variants
24
- @width = full.width
25
- @height = full.height
26
- @tile_width = tile_width
27
- @tile_scale_factors = tile_scale_factors
28
- end
29
-
30
- # @return [Hash] a collection of valid sizes based on the available image variants
31
- #
32
- def sizes
33
- @variants.collect do |name,obj|
34
- {"width" => obj.width, "height" => obj.height}
35
- end
36
- end
37
-
38
- # The hash of tile information, or nil if the information does not exist.
39
- #
40
- #
41
- # @return [Hash, nil] A hash of the tile metadata properly formatted for IIIF JSON.
42
- #
43
- def tiles
44
- return nil if @tile_scale_factors.nil? || @tile_scale_factors.empty?
45
-
46
- return [{
47
- "width" => @tile_width,
48
- "scaleFactors" => @tile_scale_factors
49
- }]
50
- end
51
-
52
-
53
- # Generate the JSON data for this image in the IIIF-expected format.
54
- #
55
- #
56
- # @return [String] the JSON representation of this image
57
- #
58
- def to_json
59
- obj = {
60
- "@context" => context,
61
- "@id" => URI.escape(id),
62
- "protocol" => protocol,
63
- "width" => width,
64
- "height" => height,
65
- "sizes" => sizes,
66
- "profile" => profile,
67
- }
68
- obj["tiles"] = tiles unless tiles.nil?
69
- obj["profile"] = profile
70
- obj["service"] = service unless service.nil?
71
- JSON.pretty_generate obj
72
- end
73
-
74
- # @return [String] The IIIF context for this image
75
- def context
76
- IiifS3::IMAGE_CONTEXT
77
- end
78
-
79
- # @return [String] The IIIF protocol for this image
80
- def protocol
81
- IiifS3::IMAGE_PROTOCOL
82
- end
83
-
84
- # @return [String] The IIIF profile this image supports
85
- def profile
86
- [IiifS3::LEVEL_0,{
87
- supports: ["cors","sizeByWhListed", "baseUriRedirect"]
88
- }]
89
- end
90
-
91
- # TODO: Implement this. See <http://iiif.io/api/annex/services/#physical-dimensions>
92
- def service
93
- return nil
94
- end
95
- end
96
- end