wax_iiif 0.0.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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +29 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +11 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +10 -0
  7. data/LICENSE.md +30 -0
  8. data/README.md +8 -0
  9. data/Rakefile +24 -0
  10. data/lib/iiif_s3/base_properties.rb +95 -0
  11. data/lib/iiif_s3/builder.rb +254 -0
  12. data/lib/iiif_s3/collection.rb +61 -0
  13. data/lib/iiif_s3/config.rb +142 -0
  14. data/lib/iiif_s3/errors.rb +37 -0
  15. data/lib/iiif_s3/full_image.rb +20 -0
  16. data/lib/iiif_s3/image_info.rb +96 -0
  17. data/lib/iiif_s3/image_record.rb +141 -0
  18. data/lib/iiif_s3/image_tile.rb +46 -0
  19. data/lib/iiif_s3/image_variant.rb +126 -0
  20. data/lib/iiif_s3/manifest.rb +151 -0
  21. data/lib/iiif_s3/thumbnail.rb +35 -0
  22. data/lib/iiif_s3/utilities.rb +12 -0
  23. data/lib/iiif_s3/utilities/helpers.rb +96 -0
  24. data/lib/iiif_s3/utilities/pdf_splitter.rb +50 -0
  25. data/lib/iiif_s3/version.rb +5 -0
  26. data/lib/wax_iiif.rb +83 -0
  27. data/spec/base_properties_spec.rb +22 -0
  28. data/spec/data/blank.csv +0 -0
  29. data/spec/data/invalid.csv +1 -0
  30. data/spec/data/no_header.csv +1 -0
  31. data/spec/data/test.csv +2 -0
  32. data/spec/data/test.jpg +0 -0
  33. data/spec/data/test.pdf +0 -0
  34. data/spec/iiif_s3/builder_spec.rb +152 -0
  35. data/spec/iiif_s3/collection_spec.rb +68 -0
  36. data/spec/iiif_s3/config_spec.rb +15 -0
  37. data/spec/iiif_s3/image_info_spec.rb +57 -0
  38. data/spec/iiif_s3/image_record_spec.rb +96 -0
  39. data/spec/iiif_s3/image_variant_spec.rb +71 -0
  40. data/spec/iiif_s3/manifest_spec.rb +97 -0
  41. data/spec/iiif_s3/utilities/pdf_splitter_spec.rb +17 -0
  42. data/spec/shared_contexts.rb +115 -0
  43. data/spec/spec_helper.rb +12 -0
  44. data/test.rb +77 -0
  45. data/wax_iiif.gemspec +29 -0
  46. metadata +218 -0
@@ -0,0 +1,50 @@
1
+ require "mini_magick"
2
+
3
+ module IiifS3
4
+ module Utilities
5
+
6
+ #
7
+ # Class PdfSplitter is a utility function designed to convert a PDF into a stack of images.
8
+ #
9
+ # @author David Newbury <david.newbury@gmail.com>
10
+ #
11
+ class PdfSplitter
12
+ # Convert a PDF File into a series of JPEG images, one per-page
13
+ #
14
+ # @param [String] path The path to the PDF file
15
+ # @param [Hash] options The configuration hash
16
+ #
17
+ # @return [Array<String>] The paths to the generated files
18
+ #
19
+ def self.split(path, options={})
20
+
21
+ output_dir = options.fetch(:output_dir, ".")
22
+ verbose = options.fetch(:verbose, false)
23
+ puts "processing #{path}" if verbose
24
+ name = File.basename(path, File.extname(path))
25
+
26
+ pages = []
27
+
28
+ pdf = MiniMagick::Image.open(path)
29
+
30
+ pdf.pages.each_with_index do |page, index|
31
+ page_file_name = "#{output_dir}/#{name}_#{index+1}.jpg"
32
+
33
+ MiniMagick::Tool::Convert.new do |convert|
34
+ convert.density("300")
35
+ convert.units("PixelsPerInch")
36
+ convert << page.path
37
+ convert.quality("80")
38
+ convert.colorspace("sRGB")
39
+ convert.interlace("none")
40
+ convert.flatten
41
+ convert << page_file_name
42
+ end
43
+ pages.push(page_file_name)
44
+ end
45
+ GC.start
46
+ pages
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,5 @@
1
+ module IiifS3
2
+ #
3
+ # @return [String] The current library version
4
+ VERSION = "0.1.0"
5
+ end
data/lib/wax_iiif.rb ADDED
@@ -0,0 +1,83 @@
1
+ require 'csv'
2
+ require 'json'
3
+
4
+ require "iiif_s3/version"
5
+ require "iiif_s3/errors"
6
+ require "iiif_s3/base_properties"
7
+ require "iiif_s3/image_record"
8
+ require "iiif_s3/builder"
9
+ require "iiif_s3/manifest"
10
+ require "iiif_s3/config"
11
+ require "iiif_s3/collection"
12
+ require "iiif_s3/image_variant"
13
+ require "iiif_s3/thumbnail"
14
+ require "iiif_s3/image_tile"
15
+ require "iiif_s3/full_image"
16
+ require "iiif_s3/image_info"
17
+ require "iiif_s3/utilities"
18
+
19
+ # Module IiifS3 is a tool for generating IIIF resources from a set of files.
20
+ # It's designed to support the IIIF level 0 profile, and generates entirely static files.
21
+ #
22
+ # @author David Newbury <david.newbury@gmail.com>
23
+ #
24
+ module IiifS3
25
+
26
+
27
+ #--------------------------------------------------------------------------
28
+ # CONSTANTS
29
+ #--------------------------------------------------------------------------
30
+
31
+
32
+ # @return [String] The URI of the presentation context for the IIIF V.2
33
+ PRESENTATION_CONTEXT = "http://iiif.io/api/presentation/2/context.json"
34
+ # @return [String] The URI of the image context for the IIIF V.2
35
+ IMAGE_CONTEXT = "http://iiif.io/api/image/2/context.json"
36
+ # @return [String] The URI of the image protocol for IIIF
37
+ IMAGE_PROTOCOL = "http://iiif.io/api/image"
38
+ # @return [String] The URI of the Level 0 profile for the IIIF V.2
39
+ LEVEL_0 = "http://iiif.io/api/image/2/level0.json"
40
+ # @return [String] The IIIF default type for a sequence.
41
+ SEQUENCE_TYPE = "sc:Sequence"
42
+ # @return [String] The IIIF default type for a canvas
43
+ CANVAS_TYPE = "sc:Canvas"
44
+ # @return [String] The IIIF default type for a annotation.
45
+ ANNOTATION_TYPE = "oa:Annotation"
46
+ # @return [String] The IIIF default type for an image.
47
+ IMAGE_TYPE = "dcterms:Image"
48
+ # @return [String] The default label for a canvas without a specified name.
49
+ MOTIVATION = "sc:painting"
50
+ # @return [String] The default label for a canvas without a specified name.
51
+ DEFAULT_CANVAS_LABEL = "front"
52
+ # @return [String] The default name for a sequence without a specified name.
53
+ DEFAULT_SEQUENCE_NAME = "default"
54
+ # @return [String] The default reading direction for this manifest.
55
+ DEFAULT_VIEWING_DIRECTION = "left-to-right"
56
+ # @return [Number] The size in pixels below which the canvas will be doubled.
57
+ MIN_CANVAS_SIZE = 1200
58
+
59
+
60
+ #--------------------------------------------------------------------------
61
+ # HELPERS
62
+ #--------------------------------------------------------------------------
63
+
64
+
65
+
66
+ # Validates a viewing direction string against the IIIF V.2.0 spec.
67
+ #
68
+ # According to v2 of the IIIF standards, there are only four valid viewing directions:
69
+ # "left-to-right", "top-to-bottom”, "bottom-to-top" , and "right-to-left". This
70
+ # returns true if the provided direction is one of these, and falst for anything else.
71
+ #
72
+ # @param [String] direction A viewing direction string
73
+ #
74
+ # @return [boolean] Is the provided string a valid viewing direction?
75
+ #
76
+ def self.is_valid_viewing_direction(direction)
77
+ direction == "left-to-right" ||
78
+ direction == "top-to-bottom" ||
79
+ direction == "bottom-to-top" ||
80
+ direction == "right-to-left"
81
+ end
82
+
83
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples "base properties" do
4
+ it "has a description" do
5
+ test_string = "test description"
6
+ @object.description = test_string
7
+ expect(@object.description).to eq test_string
8
+ end
9
+ it "has an @id" do
10
+ expect(@object.id).not_to be_nil
11
+ end
12
+ it "has a valid @id" do
13
+ expect(@object.id).to include IiifS3::Config::DEFAULT_URL
14
+ end
15
+ it "encodes @ids when set" do
16
+ @object.id = "http://www.example.com/a space"
17
+ expect(@object.id).to include "%20"
18
+ end
19
+ it "reveals the type" do
20
+ expect(@object.type).to eq described_class::TYPE
21
+ end
22
+ end
File without changes
@@ -0,0 +1 @@
1
+ I'm an invalid CSV file "
@@ -0,0 +1 @@
1
+ spec/data/test.jpg,1,"Test Image #1"
@@ -0,0 +1,2 @@
1
+ filename,identifier,name
2
+ spec/data/test.jpg,1,"Test Image #1"
Binary file
Binary file
@@ -0,0 +1,152 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+ require 'fileutils'
4
+
5
+ describe IiifS3::Builder do
6
+ # before(:each) do
7
+ # fake_aws_bucket = OpenStruct.new({
8
+ # "exists?" => "true"
9
+ # })
10
+ # unless ENV["TEST_INTERNET_CONNECTIVITY"]
11
+ # allow(Aws::S3::Bucket).to receive(:new) {nil}
12
+ # allow(Aws::S3::Bucket).to receive(:exists?) {true}
13
+ # end
14
+ # end
15
+
16
+ let (:iiif) { IiifS3::Builder.new() }
17
+ let (:test_object_0) {ImageRecord.new({"id" => 1, "page_number" => 1})}
18
+ let (:test_object_1) {ImageRecord.new({"id" => 2, "page_number" => 1})}
19
+ let (:test_object_2) {ImageRecord.new({"id" => 2, "page_number" => 2})}
20
+ let (:data) {[test_object_0, test_object_1,test_object_2]}
21
+
22
+ context "When initializing" do
23
+ it "generates manifests" do
24
+ expect(iiif.manifests).to eq(Array.new)
25
+ end
26
+ it "uses the default config" do
27
+ expect(iiif.config).to eq(IiifS3::Config.new)
28
+ end
29
+ it "will accept a configuration hash" do
30
+ opts = {tile_width: 99}
31
+ iiif2 = IiifS3::Builder.new(opts)
32
+ expect(iiif2.config.tile_width).to eq(99)
33
+ end
34
+ end
35
+
36
+ context "loading data" do
37
+ it "will accept an array of objects" do
38
+ iiif.load(data)
39
+ expect(iiif.data).to eq(data)
40
+ end
41
+ it "will accept a single object" do
42
+ iiif.load(test_object_1)
43
+ expect(iiif.data).to eq([test_object_1])
44
+ end
45
+
46
+ it "will error if the data is bad" do
47
+ expect{iiif.load({random: "hash"})}.to raise_error(IiifS3::Error::InvalidImageData)
48
+ expect{iiif.load([{random: "hash"}])}.to raise_error(IiifS3::Error::InvalidImageData)
49
+ end
50
+ end
51
+
52
+ context "when processing data" do
53
+ include_context("fake variants")
54
+ include_context("fake data")
55
+
56
+ before(:example) do
57
+ @iiif = IiifS3::Builder.new({base_url: 'http://0.0.0.0', verbose: true, thumbnail_size: 120})
58
+ @iiif.load(@fake_data)
59
+ allow(@iiif).to receive(:generate_tiles) {nil}
60
+ allow(@iiif).to receive(:generate_variants) {@fake_variants}
61
+
62
+ end
63
+ it "does not fail with no data" do
64
+ expect {iiif.process_data}.not_to raise_error
65
+ end
66
+
67
+ it "does not fail with real data" do
68
+ expect {@iiif.process_data}.not_to raise_error
69
+ end
70
+
71
+ it " passes the Temporary Manifest Check" do
72
+ @iiif.process_data
73
+ expect(@iiif.manifests.count).to eq 1
74
+ expect(@iiif.manifests.first.to_json).to eq @fake_manifest
75
+ end
76
+ end
77
+
78
+
79
+ context "when dealing with already loaded data" do
80
+ include_context("fake data")
81
+ include_context("fake variants")
82
+
83
+ before(:example) do
84
+ @dir = Dir.mktmpdir
85
+ @iiif = IiifS3::Builder.new({output_dir: @dir, base_url: 'http://0.0.0.0', verbose: true, thumbnail_size: 120})
86
+ @iiif.load(@fake_data)
87
+ @info_json = "#{@dir}/images/1-1/info.json"
88
+ allow(@iiif).to receive(:generate_tiles) {nil}
89
+ allow(@iiif).to receive(:generate_variants) {@fake_variants}
90
+ @iiif.process_data
91
+ end
92
+
93
+ after(:example) do
94
+ FileUtils.remove_entry @dir
95
+ end
96
+
97
+ it "has the temporary file" do
98
+ expect(File.exist?(@info_json)).to eq true
99
+ end
100
+
101
+ it "does try to generate images if that file is missing" do
102
+ File.delete(@info_json)
103
+ @iiif.process_data
104
+ expect(@iiif).to have_received(:generate_tiles).twice
105
+ expect(@iiif).to have_received(:generate_variants).twice
106
+ end
107
+
108
+ it "does not try to generate images if that file is present" do
109
+ @iiif.process_data
110
+ expect(@iiif).to have_received(:generate_tiles).once
111
+ expect(@iiif).to have_received(:generate_variants).once
112
+ end
113
+
114
+ it "generates the correct manifest anyway" do
115
+ @iiif.process_data
116
+ expect(@iiif.manifests.count).to eq 1
117
+ expect(@iiif.manifests.first.to_json).to eq @fake_manifest
118
+ end
119
+
120
+ end
121
+
122
+ context "When load_csving CSV files" do
123
+ it "accepts a path" do
124
+ expect{iiif.load_csv('./spec/data/test.csv')}.not_to raise_error()
125
+ end
126
+ it "fails on blank CSV files" do
127
+ expect{iiif.load_csv('./spec/data/blank.csv')}.to raise_error(IiifS3::Error::BlankCSV)
128
+ end
129
+ it "fails on invalid CSV files" do
130
+ expect{iiif.load_csv('./spec/data/invalid.csv')}.to raise_error(IiifS3::Error::InvalidCSV)
131
+ end
132
+ it "fails on missing CSV files" do
133
+ expect{iiif.load_csv('./spec/data/i_dont_exist.csv')}.to raise_error(IiifS3::Error::InvalidCSV)
134
+ end
135
+ end
136
+
137
+ context "When loading a CSV file" do
138
+ it "saves the data into the @data param" do
139
+ expect(iiif.data).to be_nil
140
+ iiif.load_csv('./spec/data/test.csv')
141
+ expect(iiif.data).not_to be_nil
142
+ end
143
+ it "removes headers" do
144
+ iiif.load_csv('./spec/data/test.csv')
145
+ expect(iiif.data[0]['image_path']).to eq('spec/data/test.jpg')
146
+ end
147
+ it "doesn't remove headers if not present" do
148
+ iiif.load_csv('./spec/data/no_header.csv')
149
+ expect(iiif.data[0]['image_path']).to eq('spec/data/test.jpg')
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+ require "base_properties_spec"
3
+
4
+ describe IiifS3::Collection do
5
+ let (:config) {IiifS3::Config.new()}
6
+ context "base" do
7
+ before(:each) do
8
+ @object = IiifS3::Collection.new("Test data", config)
9
+ end
10
+ it_behaves_like "base properties"
11
+ end
12
+
13
+ context "initialization" do
14
+ it "initializes without issues" do
15
+ collection = nil
16
+ expect{collection = IiifS3::Collection.new("Test data", config)}.not_to raise_error
17
+ expect(collection.id).to eq("http://0.0.0.0/collection/top.json")
18
+ end
19
+ it "initializes without issues when provided a name" do
20
+ collection = nil
21
+ expect{collection = IiifS3::Collection.new("Test data", config, "name")}.not_to raise_error
22
+ expect(collection.id).to eq("http://0.0.0.0/collection/name.json")
23
+ end
24
+ it "initializes without issues when provided a name with a space in it" do
25
+ collection = nil
26
+ expect{collection = IiifS3::Collection.new("Test data", config, "name and space")}.not_to raise_error
27
+ expect(collection.id).to eq("http://0.0.0.0/collection/name%20and%20space.json")
28
+ end
29
+ it "fails if there is no label" do
30
+ expect{collection = IiifS3::Collection.new(nil, config)}.to raise_error(IiifS3::Error::MissingCollectionName)
31
+ end
32
+ it "fails if there is a blank label" do
33
+ expect{collection = IiifS3::Collection.new("", config)}.to raise_error(IiifS3::Error::MissingCollectionName)
34
+ end
35
+ it "has the correct default name" do
36
+ collection = IiifS3::Collection.new("Test data", config)
37
+ expect(collection.id).to include "top.json"
38
+ end
39
+ end
40
+ context "data init" do
41
+ include_context("fake data")
42
+ let(:collection) {IiifS3::Collection.new("Test Data", config, "name")}
43
+ let(:manifest) {IiifS3::Manifest.new([@fake_data],config)}
44
+ it "has a label" do
45
+ expect(collection.label).to eq "Test Data"
46
+ end
47
+ it "has an id" do
48
+ expect(collection.id).to eq "http://0.0.0.0/collection/name.json"
49
+ end
50
+
51
+ it "allows you to add a collection" do
52
+ newCollection = collection.clone
53
+ expect{collection.add_collection(newCollection)}.not_to raise_error
54
+ end
55
+
56
+ it "fails if you add something else to a collection" do
57
+ newCollection = {}
58
+ expect{collection.add_collection(newCollection)}.to raise_error(IiifS3::Error::NotACollection)
59
+ end
60
+
61
+
62
+ it "generates correct JSON" do
63
+ collection.add_manifest(manifest)
64
+ expect(collection.to_json).to eq @fake_collection
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe IiifS3::Config do
5
+ context "comparing" do
6
+ it "shows equal things to be equal" do
7
+ expect(IiifS3::Config.new).to eq(IiifS3::Config.new)
8
+ end
9
+ it "shows different things to be different" do
10
+ opts = {tile_width: 99, upload_to_s3: false}
11
+ opts2 = {tile_width: 100, upload_to_s3: false}
12
+ expect(IiifS3::Config.new opts).not_to eq(IiifS3::Config.new opts2)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ include IiifS3
5
+
6
+ describe IiifS3::ImageInfo do
7
+
8
+ let(:uri) {"http://www.example.com/test/1"}
9
+ include_context("fake variants")
10
+
11
+ context "intialization" do
12
+
13
+
14
+ it "initializes without errors" do
15
+ expect{ImageInfo.new(uri,@fake_variants)}.not_to raise_error
16
+ end
17
+ it "raises an error without a 'full' variant" do
18
+ @fake_variants.delete "full"
19
+ expect{ImageInfo.new(uri,@fake_variants)}.to raise_error(Error::InvalidImageData)
20
+ end
21
+ it "raises an error without a 'thumbnail' variant" do
22
+ @fake_variants.delete "thumbnail"
23
+ expect{ImageInfo.new(uri,@fake_variants)}.to raise_error(Error::InvalidImageData)
24
+ end
25
+ end
26
+
27
+
28
+ context "valid data" do
29
+ before(:example) do
30
+ @info = ImageInfo.new(uri,@fake_variants)
31
+ end
32
+
33
+ it "generates correct sizes" do
34
+ expect(@info.sizes).to eq([{"width" => 1000, "height" => 1200},{"width" => 100, "height" => 120}])
35
+ end
36
+
37
+ it "generates nil when no tile data appears" do
38
+ expect(@info.tiles).to be_nil
39
+ end
40
+
41
+ it "generates tile info when tile data appears" do
42
+ info = ImageInfo.new(uri,@fake_variants,500,[1,2,4])
43
+ expect(info.tiles).to eq([{"width" => 500, "scaleFactors" => [1,2,4]}])
44
+ end
45
+
46
+ it "has the correct other methods" do
47
+ expect(@info).to respond_to "context"
48
+ expect(@info).to respond_to "protocol"
49
+ expect(@info).to respond_to "profile"
50
+ expect(@info).to respond_to "service"
51
+ end
52
+
53
+ it "generates correct JSON" do
54
+ expect(@info.to_json).to eq(@fake_image_info)
55
+ end
56
+ end
57
+ end