phantom_svg 1.0.4 → 1.0.5
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.
- 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
|