diff --git a/README.md b/README.md
index 19bd0a9..d902013 100644
--- a/README.md
+++ b/README.md
@@ -70,8 +70,9 @@ prawn-svg supports most but not all of the full SVG 1.1 specification. It curre
- <style> (see CSS section below)
- - `` referencing a JPEG or PNG image, with `http:`, `https:`, `data:image/jpeg;base64`, `data:image/png;base64` and `file:` schemes
- (`file:` is disabled by default for security reasons, see Options section above)
+ - `` referencing a JPEG, PNG, or SVG image, with `http:`, `https:`, `data:image/jpeg;base64`,
+ `data:image/png;base64`, `data:image/svg+xml;base64` and `file:` schemes (`file:` is disabled by default for
+ security reasons, see Options section above)
- <clipPath>
diff --git a/lib/prawn-svg.rb b/lib/prawn-svg.rb
index ca977dd..69cbcbd 100644
--- a/lib/prawn-svg.rb
+++ b/lib/prawn-svg.rb
@@ -21,6 +21,7 @@
require 'prawn/svg/pathable'
require 'prawn/svg/elements'
require 'prawn/svg/extension'
+require 'prawn/svg/renderer'
require 'prawn/svg/interface'
require 'prawn/svg/css/font_family_parser'
require 'prawn/svg/css/selector_parser'
diff --git a/lib/prawn/svg/document.rb b/lib/prawn/svg/document.rb
index b71ef3d..41fb229 100644
--- a/lib/prawn/svg/document.rb
+++ b/lib/prawn/svg/document.rb
@@ -16,7 +16,7 @@ class Prawn::SVG::Document
:element_styles,
:color_mode
- def initialize(data, bounds, options, font_registry: nil, css_parser: CssParser::Parser.new)
+ def initialize(data, bounds, options, font_registry: nil, css_parser: CssParser::Parser.new, attribute_overrides: {})
@root = REXML::Document.new(data).root
if @root.nil?
@@ -41,7 +41,10 @@ def initialize(data, bounds, options, font_registry: nil, css_parser: CssParser:
enable_file_with_root: options[:enable_file_requests_with_root]
)
- @sizing = Prawn::SVG::Calculators::DocumentSizing.new(bounds, @root.attributes)
+ attributes = @root.attributes.dup
+ attribute_overrides.each { |key, value| attributes.add(REXML::Attribute.new(key, value)) }
+
+ @sizing = Prawn::SVG::Calculators::DocumentSizing.new(bounds, attributes)
calculate_sizing(requested_width: options[:width], requested_height: options[:height])
@element_styles = Prawn::SVG::CSS::Stylesheets.new(css_parser, root).load
diff --git a/lib/prawn/svg/elements/image.rb b/lib/prawn/svg/elements/image.rb
index a4e6f92..8349d32 100644
--- a/lib/prawn/svg/elements/image.rb
+++ b/lib/prawn/svg/elements/image.rb
@@ -3,29 +3,32 @@ class FakeIO
def initialize(data)
@data = data
end
+
def read
@data
end
- def rewind
- end
+
+ def rewind; end
end
+ ImageData = Struct.new(:dimensions, :document)
+
def parse
require_attributes 'width', 'height'
- raise SkipElementQuietly if state.computed_properties.display == "none"
+ raise SkipElementQuietly if state.computed_properties.display == 'none'
@url = href_attribute
- if @url.nil?
- raise SkipElementError, "image tag must have an href or xlink:href"
- end
+ raise SkipElementError, 'image tag must have an href or xlink:href' if @url.nil?
x = x(attributes['x'] || 0)
y = y(attributes['y'] || 0)
width = x_pixels(attributes['width'])
height = y_pixels(attributes['height'])
+ preserveAspectRatio = attributes['preserveAspectRatio']
raise SkipElementQuietly if width.zero? || height.zero?
+
require_positive_value width, height
@image = begin
@@ -34,7 +37,9 @@ def parse
raise SkipElementError, "Error retrieving URL #{@url}: #{e.message}"
end
- @aspect = Prawn::SVG::Calculators::AspectRatio.new(attributes['preserveAspectRatio'], [width, height], image_dimensions(@image))
+ @image_data = process_image(@image, width, height, preserveAspectRatio)
+
+ @aspect = Prawn::SVG::Calculators::AspectRatio.new(preserveAspectRatio, [width, height], @image_data.dimensions)
@clip_x = x
@clip_y = y
@@ -49,15 +54,21 @@ def parse
def apply
if @aspect.slice?
- add_call "save"
- add_call "rectangle", [@clip_x, @clip_y], @clip_width, @clip_height
- add_call "clip"
+ add_call 'save'
+ add_call 'rectangle', [@clip_x, @clip_y], @clip_width, @clip_height
+ add_call 'clip'
end
- options = {:width => @width, :height => @height, :at => [@x, @y]}
+ if (document = @image_data.document)
+ add_call_and_enter 'translate', @x, @y
+ add_call 'svg:render_sub_document', document
+ else
+ options = { width: @width, height: @height, at: [@x, @y] }
+
+ add_call 'image', FakeIO.new(@image), options
+ end
- add_call "image", FakeIO.new(@image), options
- add_call "restore" if @aspect.slice?
+ add_call 'restore' if @aspect.slice?
end
def bounding_box
@@ -66,15 +77,38 @@ def bounding_box
protected
- def image_dimensions(data)
- unless (handler = find_image_handler(data))
- raise SkipElementError, 'Unsupported image type supplied to image tag'
+ def process_image(data, width, height, preserveAspectRatio)
+ if (handler = find_image_handler(data))
+ image = handler.new(data)
+ ImageData.new([image.width.to_f, image.height.to_f], nil)
+
+ elsif potentially_svg?(data)
+ document = Prawn::SVG::Document.new(
+ data, [width, height], { width: width, height: height },
+ attribute_overrides: { 'preserveAspectRatio' => preserveAspectRatio }
+ )
+
+ dimensions = [document.sizing.output_width, document.sizing.output_height]
+ ImageData.new(dimensions, document)
+
+ else
+ raise_invalid_image_type
end
- image = handler.new(data)
- [image.width.to_f, image.height.to_f]
+ rescue Prawn::SVG::Document::InvalidSVGData
+ raise_invalid_image_type
end
def find_image_handler(data)
- Prawn.image_handler.find(data) rescue nil
+ Prawn.image_handler.find(data)
+ rescue StandardError
+ nil
+ end
+
+ def potentially_svg?(data)
+ data.include?('