Skip to content

Commit

Permalink
Implement correct handling of the overflow property
Browse files Browse the repository at this point in the history
SVG has a default user-agent stylesheet that makes some elements
`overflow: hidden`, deviating from the default overflow property value
of `visible`.

Previously, this behaviour had been roughly hacked in but some instances
weren't working.  For example, <svg> elements ignored overflow being set
to visible.

Now overflow is correctly handled as per spec.

Fixes #164
  • Loading branch information
mogest committed Mar 15, 2024
1 parent 8b64021 commit fc97aec
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 14 deletions.
7 changes: 7 additions & 0 deletions lib/prawn/svg/css/stylesheets.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module Prawn::SVG::CSS
class Stylesheets
USER_AGENT_STYLESHEET = 'svg, symbol, image, marker, pattern, foreignObject { overflow: hidden }'.freeze

attr_reader :css_parser, :root, :media

def initialize(css_parser, root, media = :all)
Expand All @@ -9,13 +11,18 @@ def initialize(css_parser, root, media = :all)
end

def load
load_user_agent_stylesheet
load_style_elements
xpath_styles = gather_xpath_styles
associate_xpath_styles_with_elements(xpath_styles)
end

private

def load_user_agent_stylesheet
css_parser.add_block!(USER_AGENT_STYLESHEET)
end

def load_style_elements
REXML::XPath.match(root, '//style').each do |source|
data = source.texts.map(&:value).join
Expand Down
10 changes: 10 additions & 0 deletions lib/prawn/svg/elements/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -270,4 +270,14 @@ def extract_element_from_url_id_reference(value, expected_type = nil)
def href_attribute
attributes['xlink:href'] || attributes['href']
end

def overflow_hidden?
['hidden', 'scroll'].include?(computed_properties.overflow)
end

def clone_element_source(source)
new_source = source.dup
document.element_styles[new_source] = document.element_styles[source]
new_source
end
end
3 changes: 1 addition & 2 deletions lib/prawn/svg/elements/marker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ def apply_marker(element, point: nil, angle: 0)

element.add_call 'transformation_matrix', 1, 0, 0, 1, -ref_x * sizing.x_scale, ref_y * sizing.y_scale

# `overflow: visible` must be on the <marker> element
if properties.overflow != 'visible'
if overflow_hidden?
point = [sizing.x_offset * sizing.x_scale, y(sizing.y_offset * sizing.y_scale)]
element.add_call "rectangle", point, sizing.output_width, sizing.output_height
element.add_call "clip"
Expand Down
2 changes: 1 addition & 1 deletion lib/prawn/svg/elements/use.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def apply
def process_child_elements
add_call 'save'

source = referenced_element_source.dup
source = clone_element_source(referenced_element_source)

if referenced_element_class == Prawn::SVG::Elements::Viewport
source.attributes['width'] = @width || '100%'
Expand Down
7 changes: 5 additions & 2 deletions lib/prawn/svg/elements/viewport.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ def apply
add_call 'transformation_matrix', 1, 0, 0, 1, @x, -@y
end

add_call 'rectangle', [0, y(0)], @sizing.output_width, @sizing.output_height
add_call 'clip'
if overflow_hidden?
add_call 'rectangle', [0, y(0)], @sizing.output_width, @sizing.output_height
add_call 'clip'
end

add_call 'transformation_matrix', @sizing.x_scale, 0, 0, @sizing.y_scale, 0, 0
add_call 'transformation_matrix', 1, 0, 0, 1, -@sizing.x_offset, @sizing.y_offset
end
Expand Down
2 changes: 2 additions & 0 deletions spec/prawn/svg/css/stylesheets_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
width_and_styles = result.map { |k, v| [k.attributes["width"].to_i, v] }.sort_by(&:first)

expected = [
[0, [["overflow", "hidden", false]]],
[1, [["fill", "#ff0000", false]]],
[2, [["fill", "#ff0000", false], ["fill", "#330000", false], ["fill", "#440000", false], ["fill", "#220000", false]]],
[3, [["fill", "#ff0000", false], ["fill", "#00ff00", false]]],
Expand Down Expand Up @@ -114,6 +115,7 @@
it "scans the document for style tags and adds the style information to the css parser" do
css_parser = instance_double(CssParser::Parser)

expect(css_parser).to receive(:add_block!).with("svg, symbol, image, marker, pattern, foreignObject { overflow: hidden }")
expect(css_parser).to receive(:add_block!).with("a\n before>\n x y\n inside <>&gt;\n k j\n after\nz")
expect(css_parser).to receive(:add_block!).with("hello")
allow(css_parser).to receive(:each_rule_set)
Expand Down
19 changes: 10 additions & 9 deletions spec/prawn/svg/elements/marker_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@
end

let(:document) { Prawn::SVG::Document.new(svg, [800, 600], {width: 800, height: 600}) }
let(:state) { Prawn::SVG::State.new }

let(:line_element) do
Prawn::SVG::Elements::Line.new(document, document.root.elements[2], [], state)
def new_state
state = Prawn::SVG::State.new
state.viewport_sizing = document.sizing
state
end

subject do
Prawn::SVG::Elements::Marker.new(document, document.root.elements[1], [], state)
let(:line_element) do
Prawn::SVG::Elements::Line.new(document, document.root.elements[2], [], new_state)
end

before do
state.viewport_sizing = document.sizing
subject do
Prawn::SVG::Elements::Marker.new(document, document.root.elements[1], [], new_state)
end

describe "#parse" do
Expand Down Expand Up @@ -70,8 +71,8 @@
["clip", [], {}, []],
["transformation_matrix", [0.3, 0, 0, 0.3, 0, 0], {}, []],
["transparent", [1.0, 1.0], {}, [
["stroke_color", ["000000"], {}, []],
["line_width", [100.0], {}, []],
["fill_color", ["000000"], {}, []],
["line_width", [1.0], {}, []],
["cap_style", [:butt], {}, []],
["join_style", [:miter], {}, []],
["undash", [], {}, []],
Expand Down

0 comments on commit fc97aec

Please sign in to comment.