seadragon 0.2.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 +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +15 -0
  6. data/CODE_OF_CONDUCT.md +13 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +36 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +116 -0
  11. data/Rakefile +2 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +7 -0
  14. data/lib/seadragon.rb +151 -0
  15. data/lib/seadragon/engine.rb +4 -0
  16. data/lib/seadragon/version.rb +3 -0
  17. data/seadragon.gemspec +31 -0
  18. data/vendor/assets/images/openseadragon.github.io/fullpage_grouphover.png +0 -0
  19. data/vendor/assets/images/openseadragon.github.io/fullpage_hover.png +0 -0
  20. data/vendor/assets/images/openseadragon.github.io/fullpage_pressed.png +0 -0
  21. data/vendor/assets/images/openseadragon.github.io/fullpage_rest.png +0 -0
  22. data/vendor/assets/images/openseadragon.github.io/home_grouphover.png +0 -0
  23. data/vendor/assets/images/openseadragon.github.io/home_hover.png +0 -0
  24. data/vendor/assets/images/openseadragon.github.io/home_pressed.png +0 -0
  25. data/vendor/assets/images/openseadragon.github.io/home_rest.png +0 -0
  26. data/vendor/assets/images/openseadragon.github.io/next_grouphover.png +0 -0
  27. data/vendor/assets/images/openseadragon.github.io/next_hover.png +0 -0
  28. data/vendor/assets/images/openseadragon.github.io/next_pressed.png +0 -0
  29. data/vendor/assets/images/openseadragon.github.io/next_rest.png +0 -0
  30. data/vendor/assets/images/openseadragon.github.io/previous_grouphover.png +0 -0
  31. data/vendor/assets/images/openseadragon.github.io/previous_hover.png +0 -0
  32. data/vendor/assets/images/openseadragon.github.io/previous_pressed.png +0 -0
  33. data/vendor/assets/images/openseadragon.github.io/previous_rest.png +0 -0
  34. data/vendor/assets/images/openseadragon.github.io/rotateleft_grouphover.png +0 -0
  35. data/vendor/assets/images/openseadragon.github.io/rotateleft_hover.png +0 -0
  36. data/vendor/assets/images/openseadragon.github.io/rotateleft_pressed.png +0 -0
  37. data/vendor/assets/images/openseadragon.github.io/rotateleft_rest.png +0 -0
  38. data/vendor/assets/images/openseadragon.github.io/rotateright_grouphover.png +0 -0
  39. data/vendor/assets/images/openseadragon.github.io/rotateright_hover.png +0 -0
  40. data/vendor/assets/images/openseadragon.github.io/rotateright_pressed.png +0 -0
  41. data/vendor/assets/images/openseadragon.github.io/rotateright_rest.png +0 -0
  42. data/vendor/assets/images/openseadragon.github.io/zoomin_grouphover.png +0 -0
  43. data/vendor/assets/images/openseadragon.github.io/zoomin_hover.png +0 -0
  44. data/vendor/assets/images/openseadragon.github.io/zoomin_pressed.png +0 -0
  45. data/vendor/assets/images/openseadragon.github.io/zoomin_rest.png +0 -0
  46. data/vendor/assets/images/openseadragon.github.io/zoomout_grouphover.png +0 -0
  47. data/vendor/assets/images/openseadragon.github.io/zoomout_hover.png +0 -0
  48. data/vendor/assets/images/openseadragon.github.io/zoomout_pressed.png +0 -0
  49. data/vendor/assets/images/openseadragon.github.io/zoomout_rest.png +0 -0
  50. data/vendor/assets/javascripts/openseadragon.github.io/openseadragon.min.js +12 -0
  51. data/vendor/assets/javascripts/seadragon.js +1 -0
  52. metadata +223 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5ed5374126e0a3c22d70f11c64092c6bf22f0b3f
4
+ data.tar.gz: 448be2c364ba4e857e89e5c20a239806ac4610f3
5
+ SHA512:
6
+ metadata.gz: 48415e8236450e8fa7e5b4c9d01001d90a8991f8650449189924a8eeaa914b130d054f4ca95abb19343b8bd9a494268909bd24d39c3c4bc0be67446997b85d8c
7
+ data.tar.gz: af2cecd3dc3475c98ebe8357e5ce4a9611293d2a675a8191b5b58268aa64d3d625efe7aa8a9774e41cc247dc91b9c100a07ca68ad999ef633e2032c9ae8a32fc
@@ -0,0 +1 @@
1
+ service_name: travis-ci
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.0
4
+
5
+ before_install:
6
+ - gem install bundler -v 1.8
7
+
8
+ script: 'bundle exec rspec'
9
+
10
+ notifications:
11
+ email:
12
+ recipients:
13
+ - eddiej@gmail.com
14
+ on_failure: change
15
+ on_success: never
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in seadragon.gemspec
4
+ gemspec
@@ -0,0 +1,36 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features)
6
+
7
+ ## Uncomment to clear the screen before every task
8
+ # clearing :on
9
+
10
+ ## Guard internally checks for changes in the Guardfile and exits.
11
+ ## If you want Guard to automatically start up again, run guard in a
12
+ ## shell loop, e.g.:
13
+ ##
14
+ ## $ while bundle exec guard; do echo "Restarting Guard..."; done
15
+ ##
16
+ ## Note: if you are using the `directories` clause above and you are not
17
+ ## watching the project directory ('.'), then you will want to move
18
+ ## the Guardfile to a watched dir and symlink it back, e.g.
19
+ #
20
+ # $ mkdir config
21
+ # $ mv Guardfile config/
22
+ # $ ln -s config/Guardfile .
23
+ #
24
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
25
+
26
+ guard 'rspec', cmd: "bundle exec rspec" do
27
+ # watch /lib/ files
28
+ watch(%r{^lib/(.+).rb$}) do |m|
29
+ "spec/#{m[1]}_spec.rb"
30
+ end
31
+
32
+ # watch /spec/ files
33
+ watch(%r{^spec/(.+).rb$}) do |m|
34
+ "spec/#{m[1]}.rb"
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Eddie Johnston
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,116 @@
1
+ # Seadragon
2
+
3
+
4
+ [![Build Status](https://travis-ci.org/eddiej/seadragon.png?branch=master)](https://travis-ci.org/eddiej/seadragon)
5
+ [![Coverage Status](https://coveralls.io/repos/eddiej/seadragon/badge.png)](https://coveralls.io/r/eddiej/seadragon)
6
+
7
+
8
+ [OpenSeadragon](https://openseadragon.github.io/) is an open-source, web-based viewer for displaying high-resolution zoomable images. A viewer is displayed by passing paths to a set of image tiles and a DZI descriptor file to a function provided by the OpenSeadragon Javascript library.
9
+
10
+ OpenSeadragon doesn't handle the creation of the required tile images or descriptor file; this gem provides methods for creating tiles and descriptor files along with a Rails helper method for rendering image viewers in your Rails view templates.
11
+
12
+ There are a number of Ruby Gems that provide OpenSeadragon functionality. The [Openseadragon](https://github.com/IIIF/openseadragon-rails) gem packages the OpenSeadragon Javascript assets and provides helpers for using them, but leaves the descriptor and tile generation to the end-user. [SeadragonPaperclip](https://github.com/dustmoo/seadragon-paperclip) is a processor for the [Paperclip](https://github.com/thoughtbot/paperclip) gem that generates tiles, but it is tied to Paperclip and doesn't generate descriptor files or provide helper methods. This gem provides everything required for generating and displaying Deep Zoom Images on a webpage.
13
+
14
+
15
+ ## Requirements
16
+
17
+
18
+ Seadragon uses [Rmagick](https://github.com/rmagick/rmagick) to process source images - this requires [ImageMagick](http://www.imagemagick.org/script/index.php) to be installed on your system.
19
+
20
+ ## Installation
21
+
22
+ Install the latest stable release:
23
+
24
+ ```
25
+ $ gem install seadragon
26
+ ```
27
+
28
+ In Rails, add it to your Gemfile:
29
+
30
+ ```ruby
31
+ gem 'seadragon'
32
+ ```
33
+
34
+ Restart your server if required to apply the changes.
35
+
36
+ ## Usage
37
+
38
+
39
+ ### Backend - Creating the Image Tiles and Descriptor
40
+
41
+ Before a viewer can be displayed, the required tile images and descriptor file must be created.
42
+
43
+ First, create a Seadragon::Slice instance, passing to it the path to the image you want to tile, the output directory and a
44
+ unique handle, e.g.
45
+
46
+ ```ruby
47
+ slicer = Seadragon::Slicer.new({
48
+ source_path: '/images/starrynight.jpg',
49
+ tiles_path: '/www/public_html/deep_zoom_images',
50
+ handle: 'starrynight'
51
+ })
52
+ ```
53
+
54
+ To generate the image tiles, run:
55
+
56
+ ```ruby
57
+ slicer.slice!
58
+ ```
59
+
60
+ The descriptor file can the be generated by calling the `write_dzi_specification` method:
61
+
62
+ ```ruby
63
+ slicer.write_dzi_specification
64
+ ```
65
+
66
+ The steps above would create a `starrynight_files` directory in `/www/public_html/deep_zoom_images` with a subfolder of tile images for each zoom level (calculated according to the dimensions of the image). The descriptor file, configuration information in JSON format required by OpenSeadragon, would be saved to `/www/public_html/deep_zoom_images/starrynight.dzi`.
67
+
68
+ ### Frontend - Displaying a Viewer
69
+
70
+ To display a viewer in your view, first ensure the gem's Javascript is referenced from `application.js` in your app:
71
+
72
+ ```css
73
+ //= require seadragon
74
+ ```
75
+
76
+ Then create an empty element to hold the viewer (giving it a height so that it is displayed), and call the `seadragon` helper method, passing the element's id and the path to the descriptor file, returned by the `write_dzi_specification` method above.
77
+
78
+ ```html
79
+ <div id="starrynight_viewer" style="height: 400px;></div>
80
+
81
+ <%= seadragon({id: "starrynight_viewer", tileSources: "/www/public_html/deep_zoom_images/starrynight.dzi"}) %>
82
+ ```
83
+
84
+ This will construct a Javascript call to the bundled OpenSeadragon library which will render a viewer in the element specified.
85
+
86
+ Any parameters that are used by the OpenSeadragon library can be passed to the `seadragon` helper method.
87
+ If you wanted to use your own set of navigation icons instead of the defaults, and set the defauly zoom level at 5 for example, you would call the `seadragon` method like this:
88
+
89
+
90
+ ```html
91
+ <%= seadragon({
92
+ id: "starrynight_viewer",
93
+ tileSources: "/www/public_html/deep_zoom_images/starrynight.dzi",
94
+ defaultZoomLevel: 5,
95
+ prefixUrl: /www/public_html/icons/
96
+ }) %>
97
+ ```
98
+
99
+ A full list of the OpenSeadragon options is available in the [OpenSeadragon documentation](https://openseadragon.github.io/docs/OpenSeadragon.html#Options).
100
+
101
+ ## Tests
102
+
103
+ Tests are written using RSpec. To start the test suite, run:
104
+
105
+ ```ruby
106
+ bundle exec rspec
107
+ ```
108
+
109
+ or view the tests and results output on [Travis CI](https://travis-ci.org/eddiej/seadragon).
110
+
111
+
112
+
113
+
114
+
115
+
116
+
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "seadragon"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,151 @@
1
+ require 'rmagick'
2
+ require 'json'
3
+ require "seadragon/version"
4
+ require "seadragon/engine"
5
+
6
+ module Seadragon
7
+
8
+ class Slicer
9
+ attr_accessor :source_path, :tiles_path, :handle, :tile_size, :overlap,
10
+ :quality, :format, :dzi_name, :source_image, :width, :height
11
+
12
+ def initialize(attributes = {})
13
+ raise ArgumentError.new('source and destination paths are required') unless attributes[:source_path] && attributes[:tiles_path]
14
+ raise ArgumentError.new('a handle is required') unless attributes[:handle]
15
+
16
+ @source_path = attributes[:source_path]
17
+ @tiles_path = attributes[:tiles_path]
18
+ @handle = attributes[:handle]
19
+ @tile_size = attributes[:tile_size] || 512
20
+ @overlap = attributes[:overlap] || 1
21
+ @quality = attributes[:quality] || 100
22
+ @format = attributes[:format] || 'jpg'
23
+
24
+ raise ArgumentError.new("source file doesn't exist") unless File.exist? @source_path
25
+ @source_image = Magick::Image.read(@source_path)[0] # an Image object.
26
+ @width, @height = @source_image.columns, @source_image.rows # image dims
27
+
28
+ end
29
+
30
+ ##
31
+ # Generates the tiles.
32
+ ##
33
+ def slice!
34
+ # duplicate the source image, we'll be resizing it for each zoom layer.
35
+ image = @source_image.dup
36
+
37
+ # create a parent folder for all of the tiles
38
+ FileUtils.mkdir_p( File.join(tiles_path, handle+"_files") )
39
+
40
+ max_level(width, height).downto(0) do |level|
41
+
42
+ # get dimensions of the image (image is resized on each iteration)
43
+ current_level_width, current_level_height = image.columns, image.rows
44
+ current_level_dir = File.join(tiles_path, handle+"_files", level.to_s)
45
+ FileUtils.mkdir_p(current_level_dir) # create dir for current level
46
+
47
+ # iterate over columns
48
+ x, col_count = 0, 0
49
+ while x < current_level_width
50
+ # iterate over rows
51
+ y, row_count = 0, 0
52
+ while y < current_level_height
53
+ tile_file_path = File.join(current_level_dir,
54
+ "#{col_count}_#{row_count}.#{format}")
55
+ tile_width, tile_height = tile_dimensions(x, y, tile_size, overlap)
56
+ save_tile(image, tile_file_path, x, y, tile_width, tile_height, quality) unless File.exist? tile_file_path
57
+ y += (tile_height - (2 * overlap))
58
+ row_count += 1
59
+ end
60
+ x += (tile_width - (2 * overlap))
61
+ col_count += 1
62
+ end
63
+ image.resize!(0.5)
64
+ end
65
+ image.destroy!
66
+ end
67
+
68
+ ##
69
+ # Generates the DZI (Deep Zoom Image format) descriptor file in JSON.
70
+ ##
71
+ def write_dzi_specification
72
+ properties = {
73
+ 'Image': {
74
+ 'xmlns': "http://schemas.microsoft.com/deepzoom/2008",
75
+ 'Format': format,
76
+ 'Overlap': overlap.to_s,
77
+ 'TileSize': tile_size.to_s,
78
+ 'Size': {
79
+ 'Height': height.to_s,
80
+ 'Width': width.to_s
81
+ }
82
+ }
83
+ }
84
+
85
+ FileUtils.mkdir(tiles_path) unless File.exists?(tiles_path)
86
+ dzi_path = File.join(tiles_path, handle + ".dzi")
87
+
88
+ File.open(dzi_path, 'w') do |f|
89
+ f.write(JSON.pretty_generate(properties))
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ ##
96
+ # Calculates how many times an image can
97
+ # be halved until it is resized to 1x1px.
98
+ ##
99
+ def max_level(width, height)
100
+ return (Math.log([width, height].max) / Math.log(2)).ceil
101
+ end
102
+
103
+ ##
104
+ # Determines width and height for tiles, dependent of tile position.
105
+ # Center tiles: overlapping on each side.
106
+ # Borders: no overlapping on the border side, overlapping on other sides.
107
+ # Corners: only overlapping on the right and lower border.
108
+ ##
109
+ def tile_dimensions(x, y, tile_size, overlap)
110
+ overlapping_tile_size = tile_size + (2 * overlap)
111
+ border_tile_size = tile_size + overlap
112
+ tile_width = (x > 0) ? overlapping_tile_size : border_tile_size
113
+ tile_height = (y > 0) ? overlapping_tile_size : border_tile_size
114
+ return tile_width, tile_height
115
+ end
116
+
117
+ # Crop a tile from the source image and writes it to dest_path.
118
+ # Params: image: may be an Magick::Image object or a path to an image.
119
+ # dest_path: path where cropped image should be stored.
120
+ # x, y: offset from upper left corner of source image.
121
+ # width, height: width and height of cropped image.
122
+ # quality: compression level 0-100 (or 0.0-1.0), lower number means higher compression.
123
+ def save_tile(image, dest_path, x, y, width, height, quality)
124
+ image = if image.is_a? Magick::Image
125
+ image
126
+ else
127
+ Magick::Image::read(image).first
128
+ end
129
+
130
+ quality = quality * 100 if quality < 1
131
+
132
+ tile = image.crop(x, y, width, height, true)
133
+ tile.write(dest_path)
134
+ end
135
+
136
+ end
137
+
138
+ module SeadragonHelper
139
+ def seadragon(options = {})
140
+ raise ArgumentError.new('a target element must be passed via the id key') unless options[:id]
141
+ raise ArgumentError.new('a tile source must be passed via the tileSources key') unless options[:tileSources]
142
+
143
+ options[:prefixUrl] ||= "/assets/openseadragon.github.io/"
144
+
145
+ script = javascript_tag("var viewer = OpenSeadragon(#{options.to_json});")
146
+ end
147
+ end
148
+
149
+ ActionView::Base.send :include, SeadragonHelper
150
+
151
+ end
@@ -0,0 +1,4 @@
1
+ module Seadragon
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module Seadragon
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'seadragon/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "seadragon"
8
+ spec.version = Seadragon::VERSION
9
+ spec.authors = ["Eddie Johnston"]
10
+ spec.email = ["eddie@beanstalk.ie"]
11
+
12
+ spec.summary = %q{The Seadragon Gem provides everything you need to create and host your own Deep Zoom Images using the OpenSeadragon image viewer. It provides methods for generating descriptor files and tiles from an image and comes bundled with the required OpenSeadragon Javascript and assets for displaying viewers.}
13
+ spec.homepage = "https://eddiej.github.io/seadragon"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.8"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "rails" ## for testing ::Rails::Engine
25
+ spec.add_development_dependency "rspec-rails"
26
+ spec.add_development_dependency "guard"
27
+ spec.add_development_dependency "guard-rspec"
28
+
29
+ spec.add_dependency "rmagick"
30
+ spec.add_development_dependency "coveralls"
31
+ end