lib/phantom/parser/png_reader.rb
require 'rapngasm'
require 'rsvg2'
require 'tmpdir'
require 'rexml/document'
require_relative '../frame.rb'
require_relative 'abstract_image_reader.rb'
module Phantom
module SVG
module Parser
# PNG reader.
class PNGReader < AbstractImageReader
# Read png file from path.
def read(path, _options = {})
reset
return if path.nil? || path.empty?
apngasm = APNG::APNGAsm.new
apngasm.disassemble(path)
if apngasm.frame_count == 1
read_png(path)
else
read_apng(apngasm)
end
end
private
def read_png(path)
frame = create_frame(path)
@frames << frame
@width = "#{frame.width}"
@height = "#{frame.height}"
end
def read_apng(apngasm)
@width = @height = 0
Dir.mktmpdir(nil, File.dirname(__FILE__)) do |dir|
set_frame(apngasm, dir)
end
@width = "#{@width}px"
@height = "#{@height}px"
@loops = apngasm.get_loops
@skip_first = apngasm.is_skip_first
@has_animation = true
end
def set_frame(apngasm, dir)
# Create temporary file.
apngasm.save_pngs(dir)
# Create frames.
apngasm.get_frames.each_with_index do |png_frame, index|
@width = png_frame.width if @width < png_frame.width
@height = png_frame.height if @height < png_frame.height
duration = png_frame.delay_numerator.to_f / png_frame.delay_denominator.to_f
@frames << create_frame("#{dir}/#{index}.png", duration)
end
end
# Create frame.
def create_frame(path, duration = nil)
pixbuf = GdkPixbuf::Pixbuf.new(file: path)
frame = set_param(path, pixbuf, duration)
frame
end
def set_param(path, pixbuf, duration)
frame = Phantom::SVG::Frame.new
frame.width = "#{pixbuf.width}px"
frame.height = "#{pixbuf.height}px"
frame.viewbox.set_from_text("0 0 #{pixbuf.width} #{pixbuf.height}")
frame.surfaces = create_surfaces(path, pixbuf.width, pixbuf.height)
frame.duration = duration unless duration.nil?
frame.namespaces = { 'xmlns' => 'http://www.w3.org/2000/svg',
'xlink' => 'http://www.w3.org/1999/xlink' }
frame
end
# Create surfaces.
def create_surfaces(path, width, height)
bin = File.binread(path)
base64 = [bin].pack('m')
image = REXML::Element.new('image')
image.add_attributes(
'width' => width,
'height' => height,
'xlink:href' => "data:image/png;base64,#{base64}"
)
[image]
end
end # class PNGReader
end # Parser
end # SVG
end # Phantom