phantom_svg 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/lib/phantom/frame.rb +1 -1
  3. data/lib/phantom/parser/abstract_image_reader.rb +34 -0
  4. data/lib/phantom/parser/abstract_image_writer.rb +20 -0
  5. data/lib/phantom/parser/gif_reader.rb +66 -0
  6. data/lib/phantom/parser/jpeg_reader.rb +61 -0
  7. data/lib/phantom/parser/png_reader.rb +98 -0
  8. data/lib/phantom/parser/png_writer.rb +57 -0
  9. data/lib/phantom/parser/svg_reader.rb +4 -21
  10. data/lib/phantom/parser/svg_writer.rb +3 -6
  11. data/lib/phantom/svg.rb +78 -16
  12. data/phantom_svg.gemspec +2 -1
  13. data/spec/images/apngasm.gif +0 -0
  14. data/spec/images/jpeg_test/1.jpg +0 -0
  15. data/spec/images/jpeg_test/10.jpg +0 -0
  16. data/spec/images/jpeg_test/11.jpg +0 -0
  17. data/spec/images/jpeg_test/12.jpg +0 -0
  18. data/spec/images/jpeg_test/13.jpg +0 -0
  19. data/spec/images/jpeg_test/14.jpg +0 -0
  20. data/spec/images/jpeg_test/15.jpg +0 -0
  21. data/spec/images/jpeg_test/16.jpg +0 -0
  22. data/spec/images/jpeg_test/17.jpg +0 -0
  23. data/spec/images/jpeg_test/18.jpg +0 -0
  24. data/spec/images/jpeg_test/19.jpg +0 -0
  25. data/spec/images/jpeg_test/2.jpg +0 -0
  26. data/spec/images/jpeg_test/20.jpg +0 -0
  27. data/spec/images/jpeg_test/21.jpg +0 -0
  28. data/spec/images/jpeg_test/22.jpg +0 -0
  29. data/spec/images/jpeg_test/23.jpg +0 -0
  30. data/spec/images/jpeg_test/24.jpg +0 -0
  31. data/spec/images/jpeg_test/25.jpg +0 -0
  32. data/spec/images/jpeg_test/26.jpg +0 -0
  33. data/spec/images/jpeg_test/27.jpg +0 -0
  34. data/spec/images/jpeg_test/28.jpg +0 -0
  35. data/spec/images/jpeg_test/29.jpg +0 -0
  36. data/spec/images/jpeg_test/3.jpg +0 -0
  37. data/spec/images/jpeg_test/30.jpg +0 -0
  38. data/spec/images/jpeg_test/31.jpg +0 -0
  39. data/spec/images/jpeg_test/32.jpg +0 -0
  40. data/spec/images/jpeg_test/33.jpg +0 -0
  41. data/spec/images/jpeg_test/34.jpg +0 -0
  42. data/spec/images/jpeg_test/4.jpg +0 -0
  43. data/spec/images/jpeg_test/5.jpg +0 -0
  44. data/spec/images/jpeg_test/6.jpg +0 -0
  45. data/spec/images/jpeg_test/7.jpg +0 -0
  46. data/spec/images/jpeg_test/8.jpg +0 -0
  47. data/spec/images/jpeg_test/9.jpg +0 -0
  48. data/spec/phantom/svg_spec.rb +66 -4
  49. metadata +92 -3
  50. data/lib/phantom/parser/raster.rb +0 -115
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 18f99a7628287a82ef66f940b39600f89297d9e2
4
- data.tar.gz: c2bd3e70192b8e27330905329355a86794e64e9a
3
+ metadata.gz: c6580bef212ec7f4f1d43dc3405e0243b95ae9fd
4
+ data.tar.gz: 11c27a5533bb0c7ae2e05f95cc1782878d4200fb
5
5
  SHA512:
6
- metadata.gz: 350c5c93b3691d385b5b4c8152cbb488b36869c66b2e89c5c479084cb6f76d5985c17b8575b5ced6b72a437dc801d0b2fddb33658353a759a9e2884f0440028f
7
- data.tar.gz: ef0dcd3d8ffb2ea1a77db9ca7bff109c1ca7f65d31be7a4352599a97d8c8cf2ee753e3ad4a85817a7632100ce4ed6104442a064f60b5da3fee7da4ed154d6253
6
+ metadata.gz: 17d4cdb9a98c7aa7e9397fb1ddae4ab4370b375de8f703252e1022b9067aff5cca3966054b7493be1eef64e7d5f5c0f1dd17792cdcdbd5d68a6d0e541901e621
7
+ data.tar.gz: fd59d981ca5e03034e333f8586d130114e9b35b886f5e172b6f78abefe384ac0602cc665359f4df2344f4b84621a6b60255365a99eec5c9325879d933bf76fb6
data/lib/phantom/frame.rb CHANGED
@@ -27,7 +27,7 @@ module Phantom
27
27
 
28
28
  def set_from_text(text)
29
29
  values = text.split(' ', 4)
30
- initialize(values[0], values[1], values[2], values[3])
30
+ initialize(values[0].to_i, values[1].to_i, values[2].to_i, values[3].to_i)
31
31
  self
32
32
  end
33
33
 
@@ -0,0 +1,34 @@
1
+
2
+ module Phantom
3
+ module SVG
4
+ module Parser
5
+ # Image reader.
6
+ class AbstractImageReader
7
+ attr_reader :frames, :width, :height, :loops, :skip_first, :has_animation
8
+ alias_method :has_animation?, :has_animation
9
+
10
+ # Construct AbstractImageReader object.
11
+ def initialize(path = nil, options = {})
12
+ read(path, options)
13
+ end
14
+
15
+ # Read image file from path.
16
+ def read(path, options = {})
17
+ fail 'Called abstract method.'
18
+ end
19
+
20
+ private
21
+
22
+ # Reset SVGReader object.
23
+ def reset
24
+ @frames = []
25
+ @width = nil
26
+ @height = nil
27
+ @loops = nil
28
+ @skip_first = nil
29
+ @has_animation = false
30
+ end
31
+ end # class AbstractImageReader
32
+ end # module Parser
33
+ end # module SVG
34
+ end # module Phantom
@@ -0,0 +1,20 @@
1
+
2
+ module Phantom
3
+ module SVG
4
+ module Parser
5
+ # Image writer.
6
+ class AbstractImageWriter
7
+ # Construct AbstractImageWriter object.
8
+ def initialize(path = nil, object = nil)
9
+ write(path, object)
10
+ end
11
+
12
+ # Write image file from object to path.
13
+ # Return write size.
14
+ def write(path, object)
15
+ fail 'Called abstract method.'
16
+ end
17
+ end # class AbstractImageWriter
18
+ end # module Parser
19
+ end # module SVG
20
+ end # module Phantom
@@ -0,0 +1,66 @@
1
+ require 'rexml/document'
2
+ require 'RMagick'
3
+ require 'rsvg2'
4
+
5
+ require_relative '../frame'
6
+ require_relative 'abstract_image_reader'
7
+
8
+ module Phantom
9
+ module SVG
10
+ module Parser
11
+ # GIF reader.
12
+ class GIFReader < AbstractImageReader
13
+ include Magick
14
+ # Read gif file from path.
15
+ def read(path, options = {})
16
+ reset
17
+
18
+ return if path.nil? || path.empty?
19
+
20
+ frames = create_frames(path)
21
+ @frames += frames
22
+ @width = "#{frames.first.width}px"
23
+ @height = "#{frames.first.height}px"
24
+ end
25
+
26
+ private
27
+
28
+ # Create frames for each frame in the gif.
29
+ def create_frames(path, duration = nil)
30
+ frames = []
31
+ lst = ImageList.new path
32
+
33
+ lst.each do |img|
34
+ frame = Phantom::SVG::Frame.new
35
+ frame.width = "#{img.columns}px"
36
+ frame.height = "#{img.rows}px"
37
+ frame.viewbox.set_from_text("0 0 #{img.columns} #{img.rows}")
38
+ frame.surfaces = create_surfaces(path, img.columns, img.rows)
39
+ frame.duration = img.delay * 10.0 unless img.delay.nil?
40
+ frame.namespaces = {
41
+ 'xmlns' => 'http://www.w3.org/2000/svg',
42
+ 'xlink' => 'http://www.w3.org/1999/xlink'
43
+ }
44
+ frames << frame
45
+ end
46
+ frames
47
+ end
48
+
49
+ # Create surfaces.
50
+ def create_surfaces(path, width, height)
51
+ bin = File.binread(path)
52
+ base64 = [bin].pack('m')
53
+
54
+ image = REXML::Element.new('image')
55
+ image.add_attributes(
56
+ 'width' => width,
57
+ 'height' => height,
58
+ 'xlink:href' => "data:image/gif;base64,#{base64}"
59
+ )
60
+
61
+ [image]
62
+ end
63
+ end # class GIFReader
64
+ end # module Parser
65
+ end # module SVG
66
+ end # module Phantom
@@ -0,0 +1,61 @@
1
+ require 'rsvg2'
2
+ require 'rexml/document'
3
+
4
+ require_relative '../frame'
5
+ require_relative 'abstract_image_reader'
6
+
7
+ module Phantom
8
+ module SVG
9
+ module Parser
10
+ # JPEG reader.
11
+ class JPEGReader < AbstractImageReader
12
+ # Read jpeg file from path.
13
+ def read(path, options = {})
14
+ reset
15
+
16
+ return if path.nil? || path.empty?
17
+
18
+ frame = create_frame(path)
19
+ @frames << frame
20
+ @width = "#{frame.width}px"
21
+ @height = "#{frame.height}px"
22
+ end
23
+
24
+ private
25
+
26
+ # Create frame.
27
+ def create_frame(path, duration = nil)
28
+ pixbuf = Gdk::Pixbuf.new(path)
29
+
30
+ frame = Phantom::SVG::Frame.new
31
+ frame.width = "#{pixbuf.width}px"
32
+ frame.height = "#{pixbuf.height}px"
33
+ frame.viewbox.set_from_text("0 0 #{pixbuf.width} #{pixbuf.height}")
34
+ frame.surfaces = create_surfaces(path, pixbuf.width, pixbuf.height)
35
+ frame.duration = duration unless duration.nil?
36
+ frame.namespaces = {
37
+ 'xmlns' => 'http://www.w3.org/2000/svg',
38
+ 'xlink' => 'http://www.w3.org/1999/xlink'
39
+ }
40
+
41
+ frame
42
+ end
43
+
44
+ # Create surfaces.
45
+ def create_surfaces(path, width, height)
46
+ bin = File.binread(path)
47
+ base64 = [bin].pack('m')
48
+
49
+ image = REXML::Element.new('image')
50
+ image.add_attributes(
51
+ 'width' => width,
52
+ 'height' => height,
53
+ 'xlink:href' => "data:image/jpeg;base64,#{base64}"
54
+ )
55
+
56
+ [image]
57
+ end
58
+ end # class JPEGReader
59
+ end # module Parser
60
+ end # module SVG
61
+ end # module Phantom
@@ -0,0 +1,98 @@
1
+
2
+ require 'rapngasm'
3
+ require 'rsvg2'
4
+ require 'tmpdir'
5
+ require 'rexml/document'
6
+
7
+ require_relative '../frame.rb'
8
+ require_relative 'abstract_image_reader.rb'
9
+
10
+ module Phantom
11
+ module SVG
12
+ module Parser
13
+ # PNG reader.
14
+ class PNGReader < AbstractImageReader
15
+ # Read png file from path.
16
+ def read(path, options = {})
17
+ reset
18
+
19
+ return if path.nil? || path.empty?
20
+
21
+ apngasm = APNGAsm.new
22
+ apngasm.disassemble(path)
23
+
24
+ if apngasm.frame_count == 1
25
+ read_png(path)
26
+ else
27
+ read_apng(apngasm)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def read_png(path)
34
+ frame = create_frame(path)
35
+ @frames << frame
36
+ @width = "#{frame.width}px"
37
+ @height = "#{frame.height}px"
38
+ end
39
+
40
+ def read_apng(apngasm)
41
+ @width = @height = 0
42
+ Dir.mktmpdir(nil, File.dirname(__FILE__)) do |dir|
43
+ # Create temporary file.
44
+ apngasm.save_pngs(dir)
45
+
46
+ # Create frames.
47
+ apngasm.get_frames.each_with_index do |png_frame, index|
48
+ @width = png_frame.width if @width < png_frame.width
49
+ @height = png_frame.height if @height < png_frame.height
50
+ duration = png_frame.delay_numerator.to_f / png_frame.delay_denominator.to_f
51
+ @frames << create_frame("#{dir}/#{index}.png", duration)
52
+ end
53
+ end
54
+
55
+ @width = "#{@width}px"
56
+ @height = "#{@height}px"
57
+ @loops = apngasm.get_loops
58
+ @skip_first = apngasm.is_skip_first
59
+ @has_animation = true
60
+ end
61
+
62
+ # Create frame.
63
+ def create_frame(path, duration = nil)
64
+ pixbuf = Gdk::Pixbuf.new(path)
65
+
66
+ frame = Phantom::SVG::Frame.new
67
+ frame.width = "#{pixbuf.width}px"
68
+ frame.height = "#{pixbuf.height}px"
69
+ frame.viewbox.set_from_text("0 0 #{pixbuf.width} #{pixbuf.height}")
70
+ frame.surfaces = create_surfaces(path, pixbuf.width, pixbuf.height)
71
+ frame.duration = duration unless duration.nil?
72
+ frame.namespaces = {
73
+ 'xmlns' => 'http://www.w3.org/2000/svg',
74
+ 'xlink' => 'http://www.w3.org/1999/xlink'
75
+ }
76
+
77
+ frame
78
+ end
79
+
80
+ # Create surfaces.
81
+ def create_surfaces(path, width, height)
82
+ bin = File.binread(path)
83
+ base64 = [bin].pack('m')
84
+
85
+ image = REXML::Element.new('image')
86
+ image.add_attributes(
87
+ 'width' => width,
88
+ 'height' => height,
89
+ 'xlink:href' => "data:image/png;base64,#{base64}"
90
+ )
91
+
92
+ [image]
93
+ end
94
+
95
+ end # class PNGReader
96
+ end # Parser
97
+ end # SVG
98
+ end # Phantom
@@ -0,0 +1,57 @@
1
+
2
+ require 'tmpdir'
3
+ require 'rapngasm'
4
+ require 'cairo'
5
+
6
+ require_relative 'svg_writer.rb'
7
+ require_relative 'abstract_image_writer.rb'
8
+
9
+ module Phantom
10
+ module SVG
11
+ module Parser
12
+ # Image writer.
13
+ class PNGWriter < AbstractImageWriter
14
+ # Write png file from object to path.
15
+ # Return write size.
16
+ def write(path, object)
17
+ return 0 if path.nil? || path.empty? || object.nil?
18
+
19
+ object.set_size
20
+
21
+ apngasm = APNGAsm.new
22
+ convert_frames(apngasm, object)
23
+ result = apngasm.assemble(path)
24
+
25
+ result
26
+ end
27
+
28
+ private
29
+
30
+ def convert_frames(apngasm, object)
31
+ apngasm.set_loops(object.loops)
32
+ apngasm.set_skip_first(object.skip_first)
33
+
34
+ Dir.mktmpdir(nil, File.dirname(__FILE__)) do |dir|
35
+ object.frames.each_with_index do |frame, index|
36
+ tmp_file_path = "#{dir}/tmp#{index}"
37
+ create_temporary_file(tmp_file_path, frame, object.width.to_i, object.height.to_i)
38
+ apngasm.add_frame_file("#{tmp_file_path}.png", frame.duration.to_f * 1000, 1000)
39
+ end
40
+ end
41
+ end
42
+
43
+ def create_temporary_file(path, frame, width, height)
44
+ Parser::SVGWriter.new.write("#{path}.svg", frame)
45
+
46
+ handle = RSVG::Handle.new_from_file("#{path}.svg")
47
+ surface = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, width, height)
48
+ context = Cairo::Context.new(surface)
49
+ context.scale(width.to_f / handle.dimensions.width, height.to_f / handle.dimensions.height)
50
+ context.render_rsvg_handle(handle)
51
+ surface.write_to_png("#{path}.png")
52
+ surface.finish
53
+ end
54
+ end # class PNGWriter
55
+ end # module Parser
56
+ end # module SVG
57
+ end # module Phantom
@@ -2,20 +2,13 @@
2
2
  require 'rexml/document'
3
3
 
4
4
  require_relative '../frame.rb'
5
+ require_relative 'abstract_image_reader.rb'
5
6
 
6
7
  module Phantom
7
8
  module SVG
8
9
  module Parser
9
10
  # SVG reader.
10
- class SVGReader
11
- attr_reader :frames, :width, :height, :loops, :skip_first, :has_animation
12
- alias_method :has_animation?, :has_animation
13
-
14
- # Construct SVGReader object.
15
- def initialize(path = nil, options = {})
16
- read(path, options)
17
- end
18
-
11
+ class SVGReader < AbstractImageReader
19
12
  # Read svg file from path.
20
13
  def read(path, options = {})
21
14
  reset
@@ -35,16 +28,6 @@ module Phantom
35
28
 
36
29
  private
37
30
 
38
- # Reset SVGReader object.
39
- def reset
40
- @frames = []
41
- @width = nil
42
- @height = nil
43
- @loops = nil
44
- @skip_first = nil
45
- @has_animation = false
46
- end
47
-
48
31
  # Read no animation svg.
49
32
  def read_svg(options)
50
33
  read_images(@root, options)
@@ -66,13 +49,13 @@ module Phantom
66
49
  def read_size(node, dest, options = {})
67
50
  dest.viewbox.set_from_text(choice_value(node.attributes['viewBox'], options[:viewbox]).to_s) unless node.attributes['viewBox'].nil?
68
51
 
69
- if node.attributes['width'].nil? then
52
+ if node.attributes['width'].nil?
70
53
  dest.instance_variable_set(:@width, choice_value("#{dest.viewbox.width}px", options[:width]))
71
54
  else
72
55
  dest.instance_variable_set(:@width, choice_value(node.attributes['width'], options[:width]))
73
56
  end
74
57
 
75
- if node.attributes['height'].nil? then
58
+ if node.attributes['height'].nil?
76
59
  dest.instance_variable_set(:@height, choice_value("#{dest.viewbox.height}px", options[:height]))
77
60
  else
78
61
  dest.instance_variable_set(:@height, choice_value(node.attributes['height'], options[:height]))
@@ -1,16 +1,13 @@
1
1
 
2
2
  require 'rexml/document'
3
3
 
4
+ require_relative 'abstract_image_writer.rb'
5
+
4
6
  module Phantom
5
7
  module SVG
6
8
  module Parser
7
9
  # SVG writer.
8
- class SVGWriter
9
- # Construct SVGWriter object.
10
- def initialize(path = nil, object = nil)
11
- write(path, object)
12
- end
13
-
10
+ class SVGWriter < AbstractImageWriter
14
11
  # Write svg file from object to path.
15
12
  # Return write size.
16
13
  def write(path, object)
data/lib/phantom/svg.rb CHANGED
@@ -1,15 +1,17 @@
1
1
 
2
- require_relative 'frame.rb'
3
- require_relative 'parser/raster.rb'
4
- require_relative 'parser/svg_reader.rb'
5
- require_relative 'parser/svg_writer.rb'
6
- require_relative 'parser/json_animation_reader.rb'
7
- require_relative 'parser/xml_animation_reader.rb'
2
+ require_relative 'frame'
3
+ require_relative 'parser/png_reader'
4
+ require_relative 'parser/png_writer'
5
+ require_relative 'parser/svg_reader'
6
+ require_relative 'parser/svg_writer'
7
+ require_relative 'parser/jpeg_reader'
8
+ require_relative 'parser/gif_reader'
9
+ require_relative 'parser/json_animation_reader'
10
+ require_relative 'parser/xml_animation_reader'
8
11
 
9
12
  module Phantom
10
13
  module SVG
11
14
  class Base
12
- include Parser::Raster
13
15
  attr_accessor :frames, :width, :height, :loops, :skip_first
14
16
 
15
17
  def initialize(path = nil, options = {})
@@ -31,6 +33,9 @@ module Phantom
31
33
  case File.extname(file)
32
34
  when '.svg' then load_from_svg(file, options)
33
35
  when '.png' then load_from_png(file, options)
36
+ when '.jpg' then load_from_jpeg(file, options)
37
+ when '.jpeg' then load_from_jpeg(file, options)
38
+ when '.gif' then load_from_gif(file, options)
34
39
  when '.json' then load_from_json(file, options)
35
40
  when '.xml' then load_from_xml(file, options)
36
41
  end
@@ -47,17 +52,42 @@ module Phantom
47
52
  end
48
53
  end
49
54
 
50
- def set_size
51
- @width = 0
52
- @height = 0
53
- frames.each do |frame|
54
- @width = frame.width.to_i if frame.width.to_i > @width
55
- @height = frame.height.to_i if frame.height.to_i > @height
55
+ def set_size(width = nil, height = nil)
56
+ # width
57
+ if width.nil? then
58
+ if @width.nil? || @width == 0 then
59
+ frames.each do |frame|
60
+ @width = frame.width.to_i if frame.width.to_i > @width
61
+ end
62
+ end
63
+ else
64
+ @width = width
65
+ end
66
+
67
+ # height
68
+ if height.nil? then
69
+ if @height.nil? || @height == 0 then
70
+ frames.each do |frame|
71
+ @height = frame.height.to_i if frame.height.to_i > @height
72
+ end
73
+ end
74
+ else
75
+ @height = height
56
76
  end
57
77
  end
58
78
 
79
+ def scale_w(width)
80
+ @height = (@height.to_i * width.to_i / @width.to_i).to_i
81
+ @width = width.to_i
82
+ end
83
+
84
+ def scale_h(height)
85
+ @width = (@width.to_i * height.to_i / @height.to_i).to_i
86
+ @height = height.to_i
87
+ end
88
+
59
89
  def save_svg(path)
60
- set_size if @width.to_i == 0 || @height.to_i == 0
90
+ set_size
61
91
 
62
92
  Parser::SVGWriter.new.write(path, self)
63
93
  end
@@ -77,7 +107,7 @@ module Phantom
77
107
  end
78
108
 
79
109
  def save_apng(path)
80
- save_rasterized(path)
110
+ Parser::PNGWriter.new.write(path, self)
81
111
  end
82
112
 
83
113
  # Calculate and return total duration.
@@ -105,7 +135,39 @@ module Phantom
105
135
  end
106
136
 
107
137
  def load_from_png(path, options)
108
- load_raster(path, @frames.size)
138
+ reader = Parser::PNGReader.new(path, options)
139
+ if reader.has_animation?
140
+ @width = reader.width
141
+ @height = reader.height
142
+ @loops = reader.loops
143
+ @skip_first = reader.skip_first
144
+ end
145
+
146
+ @frames += reader.frames
147
+ end
148
+
149
+ def load_from_jpeg(path, options)
150
+ reader = Parser::JPEGReader.new(path, options)
151
+ if reader.has_animation?
152
+ @width = reader.width
153
+ @height = reader.height
154
+ @loops = reader.loops
155
+ @skip_first = reader.skip_first
156
+ end
157
+
158
+ @frames += reader.frames
159
+ end
160
+
161
+ def load_from_gif(path, option)
162
+ reader = Parser::GIFReader.new(path, option)
163
+ if reader.has_animation?
164
+ @width = reader.width
165
+ @height = reader.height
166
+ @loops = reader.loops
167
+ @skip_first = reader.skip_first
168
+ end
169
+
170
+ @frames += reader.frames
109
171
  end
110
172
 
111
173
  def load_from_json(path, options)
data/phantom_svg.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.platform = Gem::Platform::RUBY
3
3
  s.name = 'phantom_svg'
4
- s.version = '1.0.4'
4
+ s.version = '1.0.5'
5
5
  s.license = 'LGPL-3.0'
6
6
  s.summary = 'Hight end SVG manipulation tools for Ruby'
7
7
  s.description = 'Hight end SVG manipulation tools for Ruby.\n' \
@@ -19,4 +19,5 @@ Gem::Specification.new do |s|
19
19
  s.add_dependency 'cairo', '~> 1.12'
20
20
  s.add_dependency 'rapngasm', '~> 3.1'
21
21
  s.add_dependency 'rsvg2', '~> 2.2'
22
+ s.add_dependency 'rmagick', '~> 2.13'
22
23
  end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -227,8 +227,8 @@ describe Phantom::SVG::Base do
227
227
  it 'loads and saves PNG.' do
228
228
  @loader.add_frame_from_file("#{SPEC_SOURCE_DIR}/test_raster.png")
229
229
  expect(@loader.frames.size).to eq(1)
230
- expect(@loader.frames.first.width).to eq("256px")
231
- expect(@loader.frames.first.height).to eq("128px")
230
+ expect(@loader.frames.first.width).to eq('256px')
231
+ expect(@loader.frames.first.height).to eq('128px')
232
232
  expect(@loader.frames.first.viewbox.width).to eq(256)
233
233
  expect(@loader.frames.first.viewbox.height).to eq(128)
234
234
 
@@ -243,8 +243,8 @@ describe Phantom::SVG::Base do
243
243
  it 'loads and saves an irrigularly proportioned PNG' do
244
244
  @loader.add_frame_from_file("#{SPEC_SOURCE_DIR}/small_v_raster.png")
245
245
  expect(@loader.frames.size).to eq(1)
246
- expect(@loader.frames.first.width).to eq("33px")
247
- expect(@loader.frames.first.height).to eq("54px")
246
+ expect(@loader.frames.first.width).to eq('33px')
247
+ expect(@loader.frames.first.height).to eq('54px')
248
248
  expect(@loader.frames.first.viewbox.width).to eq(33)
249
249
  expect(@loader.frames.first.viewbox.height).to eq(54)
250
250
 
@@ -502,4 +502,66 @@ describe Phantom::SVG::Base do
502
502
  expect(@loader.frames[1].duration).to eq(0.05)
503
503
  end
504
504
  end
505
+
506
+ describe 'creating an animated SVG file from a JPEG files.' do
507
+ before(:all) do
508
+ @image_name = 'jpeg_test'
509
+ @source_dir = "#{SPEC_SOURCE_DIR}/#{@image_name}"
510
+ @destination_dir = SPEC_TEMP_DIR
511
+ end
512
+
513
+ before do
514
+ @loader = Phantom::SVG::Base.new
515
+ end
516
+
517
+ it "loads frames." do
518
+ test_name = 'jpeg_test'
519
+ @loader.add_frame_from_file("#{@source_dir}/*.jpg")
520
+ expect(@loader.frames.size).to eq(34)
521
+ @loader.frames.each do |frame|
522
+ expect(frame.duration.instance_of?(Float)).to eq(true)
523
+ expect(frame.width).to eq('64px')
524
+ expect(frame.height).to eq('64px')
525
+ expect(frame.surfaces).not_to be_nil
526
+ expect(frame.surfaces).not_to be_empty
527
+ expect(frame.surfaces.to_s.length).not_to eq(1)
528
+ expect(frame.namespaces).not_to be_empty
529
+ end
530
+ write_size = @loader.save_svg("#{@destination_dir}/#{test_name}.svg")
531
+ expect(write_size).not_to eq(0)
532
+ write_size = @loader.save_apng("#{@destination_dir}/#{test_name}.png")
533
+ expect(write_size).not_to eq(0)
534
+ end
535
+ end
536
+
537
+ describe 'creating an animated SVG file from a GIF files.' do
538
+ before(:all) do
539
+ @image_name = 'apngasm'
540
+ @source = "#{SPEC_SOURCE_DIR}/#{@image_name}.gif"
541
+ @destination_dir = SPEC_TEMP_DIR
542
+ end
543
+
544
+ before do
545
+ @loader = Phantom::SVG::Base.new
546
+ end
547
+
548
+ it "loads frames / saves to SVG and PNG" do
549
+ test_name = 'gif_test'
550
+ @loader.add_frame_from_file(@source)
551
+ expect(@loader.frames.size).to eq(34)
552
+ @loader.frames.each do |frame|
553
+ expect(frame.duration.instance_of?(Float)).to eq(true)
554
+ expect(frame.width).to eq('64px')
555
+ expect(frame.height).to eq('64px')
556
+ expect(frame.surfaces).not_to be_nil
557
+ expect(frame.surfaces).not_to be_empty
558
+ expect(frame.surfaces.to_s.length).not_to eq(1)
559
+ expect(frame.namespaces).not_to be_empty
560
+ end
561
+ write_size = @loader.save_svg("#{@destination_dir}/#{test_name}.svg")
562
+ expect(write_size).not_to eq(0)
563
+ write_size = @loader.save_apng("#{@destination_dir}/#{test_name}.png")
564
+ expect(write_size).not_to eq(0)
565
+ end
566
+ end
505
567
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phantom_svg
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rika Yoshida
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-09-19 00:00:00.000000000 Z
13
+ date: 2014-10-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: cairo
@@ -54,6 +54,20 @@ dependencies:
54
54
  - - "~>"
55
55
  - !ruby/object:Gem::Version
56
56
  version: '2.2'
57
+ - !ruby/object:Gem::Dependency
58
+ name: rmagick
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '2.13'
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '2.13'
57
71
  description: Hight end SVG manipulation tools for Ruby.\nIncludes chained keyframe
58
72
  generation, (A)PNG conversion and more.
59
73
  email: info@genshin.org
@@ -71,16 +85,56 @@ files:
71
85
  - README.md
72
86
  - lib/phantom/frame.rb
73
87
  - lib/phantom/parser/abstract_animation_reader.rb
88
+ - lib/phantom/parser/abstract_image_reader.rb
89
+ - lib/phantom/parser/abstract_image_writer.rb
90
+ - lib/phantom/parser/gif_reader.rb
91
+ - lib/phantom/parser/jpeg_reader.rb
74
92
  - lib/phantom/parser/json_animation_reader.rb
75
- - lib/phantom/parser/raster.rb
93
+ - lib/phantom/parser/png_reader.rb
94
+ - lib/phantom/parser/png_writer.rb
76
95
  - lib/phantom/parser/svg_reader.rb
77
96
  - lib/phantom/parser/svg_writer.rb
78
97
  - lib/phantom/parser/xml_animation_reader.rb
79
98
  - lib/phantom/svg.rb
80
99
  - lib/phantom_svg.rb
81
100
  - phantom_svg.gemspec
101
+ - spec/images/apngasm.gif
82
102
  - spec/images/apngasm.png
83
103
  - spec/images/compiled.svg
104
+ - spec/images/jpeg_test/1.jpg
105
+ - spec/images/jpeg_test/10.jpg
106
+ - spec/images/jpeg_test/11.jpg
107
+ - spec/images/jpeg_test/12.jpg
108
+ - spec/images/jpeg_test/13.jpg
109
+ - spec/images/jpeg_test/14.jpg
110
+ - spec/images/jpeg_test/15.jpg
111
+ - spec/images/jpeg_test/16.jpg
112
+ - spec/images/jpeg_test/17.jpg
113
+ - spec/images/jpeg_test/18.jpg
114
+ - spec/images/jpeg_test/19.jpg
115
+ - spec/images/jpeg_test/2.jpg
116
+ - spec/images/jpeg_test/20.jpg
117
+ - spec/images/jpeg_test/21.jpg
118
+ - spec/images/jpeg_test/22.jpg
119
+ - spec/images/jpeg_test/23.jpg
120
+ - spec/images/jpeg_test/24.jpg
121
+ - spec/images/jpeg_test/25.jpg
122
+ - spec/images/jpeg_test/26.jpg
123
+ - spec/images/jpeg_test/27.jpg
124
+ - spec/images/jpeg_test/28.jpg
125
+ - spec/images/jpeg_test/29.jpg
126
+ - spec/images/jpeg_test/3.jpg
127
+ - spec/images/jpeg_test/30.jpg
128
+ - spec/images/jpeg_test/31.jpg
129
+ - spec/images/jpeg_test/32.jpg
130
+ - spec/images/jpeg_test/33.jpg
131
+ - spec/images/jpeg_test/34.jpg
132
+ - spec/images/jpeg_test/4.jpg
133
+ - spec/images/jpeg_test/5.jpg
134
+ - spec/images/jpeg_test/6.jpg
135
+ - spec/images/jpeg_test/7.jpg
136
+ - spec/images/jpeg_test/8.jpg
137
+ - spec/images/jpeg_test/9.jpg
84
138
  - spec/images/plain.svg
85
139
  - spec/images/small_v_raster.png
86
140
  - spec/images/test_frames/0.svg
@@ -132,8 +186,43 @@ signing_key:
132
186
  specification_version: 4
133
187
  summary: Hight end SVG manipulation tools for Ruby
134
188
  test_files:
189
+ - spec/images/apngasm.gif
135
190
  - spec/images/apngasm.png
136
191
  - spec/images/compiled.svg
192
+ - spec/images/jpeg_test/1.jpg
193
+ - spec/images/jpeg_test/10.jpg
194
+ - spec/images/jpeg_test/11.jpg
195
+ - spec/images/jpeg_test/12.jpg
196
+ - spec/images/jpeg_test/13.jpg
197
+ - spec/images/jpeg_test/14.jpg
198
+ - spec/images/jpeg_test/15.jpg
199
+ - spec/images/jpeg_test/16.jpg
200
+ - spec/images/jpeg_test/17.jpg
201
+ - spec/images/jpeg_test/18.jpg
202
+ - spec/images/jpeg_test/19.jpg
203
+ - spec/images/jpeg_test/2.jpg
204
+ - spec/images/jpeg_test/20.jpg
205
+ - spec/images/jpeg_test/21.jpg
206
+ - spec/images/jpeg_test/22.jpg
207
+ - spec/images/jpeg_test/23.jpg
208
+ - spec/images/jpeg_test/24.jpg
209
+ - spec/images/jpeg_test/25.jpg
210
+ - spec/images/jpeg_test/26.jpg
211
+ - spec/images/jpeg_test/27.jpg
212
+ - spec/images/jpeg_test/28.jpg
213
+ - spec/images/jpeg_test/29.jpg
214
+ - spec/images/jpeg_test/3.jpg
215
+ - spec/images/jpeg_test/30.jpg
216
+ - spec/images/jpeg_test/31.jpg
217
+ - spec/images/jpeg_test/32.jpg
218
+ - spec/images/jpeg_test/33.jpg
219
+ - spec/images/jpeg_test/34.jpg
220
+ - spec/images/jpeg_test/4.jpg
221
+ - spec/images/jpeg_test/5.jpg
222
+ - spec/images/jpeg_test/6.jpg
223
+ - spec/images/jpeg_test/7.jpg
224
+ - spec/images/jpeg_test/8.jpg
225
+ - spec/images/jpeg_test/9.jpg
137
226
  - spec/images/plain.svg
138
227
  - spec/images/small_v_raster.png
139
228
  - spec/images/test_frames/0.svg
@@ -1,115 +0,0 @@
1
- require 'rapngasm'
2
- require 'rsvg2'
3
- require 'tmpdir'
4
- require 'cairo'
5
-
6
- require_relative '../frame.rb'
7
-
8
- module Phantom
9
- module SVG
10
- module Parser
11
- module Raster
12
- def load_raster(path, id)
13
- apngasm = APNGAsm.new
14
- apngasm.disassemble(path)
15
-
16
- if apngasm.frame_count == 1
17
- @frames << create_frame_from_png(path, id)
18
- else
19
- create_frame_from_apng(apngasm, id)
20
- end
21
-
22
- apngasm.reset
23
- end
24
-
25
- def create_frame_from_png(path, id, duration = nil)
26
- pixbuf = Gdk::Pixbuf.new(path)
27
-
28
- frame = Phantom::SVG::Frame.new( { width: pixbuf.width, height: pixbuf.height } )
29
- frame.width = "#{pixbuf.width}px"
30
- frame.height = "#{pixbuf.height}px"
31
- frame.surfaces = create_surfaces(path, pixbuf.width, pixbuf.height)
32
- frame.duration = duration unless duration.nil?
33
- frame.namespaces = {
34
- 'xmlns' => 'http://www.w3.org/2000/svg',
35
- 'xlink' => 'http://www.w3.org/1999/xlink'
36
- }
37
-
38
- frame
39
- end
40
-
41
- def create_frame_from_apng(apngasm, id)
42
- png_frames = apngasm.get_frames
43
- width = 0
44
- height = 0
45
- Dir::mktmpdir(nil, File.dirname(__FILE__)) do |dir|
46
- apngasm.save_pngs(dir)
47
- png_frames.each_with_index do |png_frame, i|
48
- width = png_frame.width if width < png_frame.width
49
- height = png_frame.height if height < png_frame.height
50
- duration = png_frame.delay_numerator.to_f / png_frame.delay_denominator.to_f
51
- @frames << create_frame_from_png("#{dir}/#{i}.png", id, duration)
52
- end
53
- end
54
- @width = "#{width}px"
55
- @height = "#{height}px"
56
- @loops = apngasm.get_loops
57
- @skip_first = apngasm.is_skip_first
58
- end
59
-
60
- def create_surfaces(path, width, height)
61
- bin = File.binread(path)
62
- base64 = [bin].pack('m')
63
-
64
- image = REXML::Element.new('image')
65
- image.add_attributes(
66
- 'width' => width,
67
- 'height' => height,
68
- 'xlink:href' => "data:image/png;base64,#{base64}"
69
- )
70
-
71
- [image]
72
- end
73
-
74
- def save_rasterized(path)
75
- set_size if @width.to_i == 0 || @height.to_i == 0
76
-
77
- apngasm = APNGAsm.new
78
- apngasm.set_loops(@loops)
79
- apngasm.set_skip_first(@skip_first)
80
-
81
- Dir::mktmpdir(nil, File.dirname(__FILE__)) do |dir|
82
- @frames.each_with_index do |frame, i|
83
- create_tmp_file("#{dir}/tmp#{i}", frame)
84
- apngasm.add_frame_file("#{dir}/tmp#{i}.png", frame.duration.to_f * 1000, 1000)
85
- end
86
- end
87
-
88
- result = apngasm.assemble(path)
89
- apngasm.reset
90
-
91
- result
92
- end
93
-
94
- def create_tmp_file(path, frame)
95
- save_svg_frame("#{path}.svg", frame, @width, @height)
96
- convert_to_png(path, frame)
97
- end
98
-
99
- def convert_to_png(path, frame)
100
- handle = RSVG::Handle.new_from_file("#{path}.svg")
101
-
102
- w = @width.to_i
103
- h = @height.to_i
104
- surface = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, w, h)
105
- context = Cairo::Context.new(surface)
106
- context.scale(w / handle.dimensions.width, h / handle.dimensions.height)
107
- context.render_rsvg_handle(handle)
108
-
109
- surface.write_to_png("#{path}.png")
110
- surface.finish
111
- end
112
- end
113
- end
114
- end
115
- end