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.
- checksums.yaml +4 -4
- data/lib/phantom/frame.rb +1 -1
- data/lib/phantom/parser/abstract_image_reader.rb +34 -0
- data/lib/phantom/parser/abstract_image_writer.rb +20 -0
- data/lib/phantom/parser/gif_reader.rb +66 -0
- data/lib/phantom/parser/jpeg_reader.rb +61 -0
- data/lib/phantom/parser/png_reader.rb +98 -0
- data/lib/phantom/parser/png_writer.rb +57 -0
- data/lib/phantom/parser/svg_reader.rb +4 -21
- data/lib/phantom/parser/svg_writer.rb +3 -6
- data/lib/phantom/svg.rb +78 -16
- data/phantom_svg.gemspec +2 -1
- data/spec/images/apngasm.gif +0 -0
- data/spec/images/jpeg_test/1.jpg +0 -0
- data/spec/images/jpeg_test/10.jpg +0 -0
- data/spec/images/jpeg_test/11.jpg +0 -0
- data/spec/images/jpeg_test/12.jpg +0 -0
- data/spec/images/jpeg_test/13.jpg +0 -0
- data/spec/images/jpeg_test/14.jpg +0 -0
- data/spec/images/jpeg_test/15.jpg +0 -0
- data/spec/images/jpeg_test/16.jpg +0 -0
- data/spec/images/jpeg_test/17.jpg +0 -0
- data/spec/images/jpeg_test/18.jpg +0 -0
- data/spec/images/jpeg_test/19.jpg +0 -0
- data/spec/images/jpeg_test/2.jpg +0 -0
- data/spec/images/jpeg_test/20.jpg +0 -0
- data/spec/images/jpeg_test/21.jpg +0 -0
- data/spec/images/jpeg_test/22.jpg +0 -0
- data/spec/images/jpeg_test/23.jpg +0 -0
- data/spec/images/jpeg_test/24.jpg +0 -0
- data/spec/images/jpeg_test/25.jpg +0 -0
- data/spec/images/jpeg_test/26.jpg +0 -0
- data/spec/images/jpeg_test/27.jpg +0 -0
- data/spec/images/jpeg_test/28.jpg +0 -0
- data/spec/images/jpeg_test/29.jpg +0 -0
- data/spec/images/jpeg_test/3.jpg +0 -0
- data/spec/images/jpeg_test/30.jpg +0 -0
- data/spec/images/jpeg_test/31.jpg +0 -0
- data/spec/images/jpeg_test/32.jpg +0 -0
- data/spec/images/jpeg_test/33.jpg +0 -0
- data/spec/images/jpeg_test/34.jpg +0 -0
- data/spec/images/jpeg_test/4.jpg +0 -0
- data/spec/images/jpeg_test/5.jpg +0 -0
- data/spec/images/jpeg_test/6.jpg +0 -0
- data/spec/images/jpeg_test/7.jpg +0 -0
- data/spec/images/jpeg_test/8.jpg +0 -0
- data/spec/images/jpeg_test/9.jpg +0 -0
- data/spec/phantom/svg_spec.rb +66 -4
- metadata +92 -3
- data/lib/phantom/parser/raster.rb +0 -115
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6580bef212ec7f4f1d43dc3405e0243b95ae9fd
|
4
|
+
data.tar.gz: 11c27a5533bb0c7ae2e05f95cc1782878d4200fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17d4cdb9a98c7aa7e9397fb1ddae4ab4370b375de8f703252e1022b9067aff5cca3966054b7493be1eef64e7d5f5c0f1dd17792cdcdbd5d68a6d0e541901e621
|
7
|
+
data.tar.gz: fd59d981ca5e03034e333f8586d130114e9b35b886f5e172b6f78abefe384ac0602cc665359f4df2344f4b84621a6b60255365a99eec5c9325879d933bf76fb6
|
data/lib/phantom/frame.rb
CHANGED
@@ -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?
|
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?
|
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
|
3
|
-
require_relative 'parser/
|
4
|
-
require_relative 'parser/
|
5
|
-
require_relative 'parser/
|
6
|
-
require_relative 'parser/
|
7
|
-
require_relative 'parser/
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
+
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
|
data/spec/phantom/svg_spec.rb
CHANGED
@@ -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(
|
231
|
-
expect(@loader.frames.first.height).to eq(
|
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(
|
247
|
-
expect(@loader.frames.first.height).to eq(
|
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
|
+
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-
|
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/
|
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
|