Skip to content

Commit

Permalink
Deep duplicate calls when making a copy of the call stack
Browse files Browse the repository at this point in the history
Some prawn methods mutate the arguments passed in to them, so to combot that
we need to deep duplicate the arguments whenever we duplicate the call stacks.

This fixes the reported problem in #49.
  • Loading branch information
Roger Nesbitt committed May 2, 2016
1 parent 9930fae commit ee8622d
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 1 deletion.
2 changes: 2 additions & 0 deletions lib/prawn/svg/elements.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module Prawn::SVG::Elements
COMMA_WSP_REGEXP = /(?:\s+,?\s*|,\s*)/
end

require 'prawn/svg/elements/call_duplicator'

%w(base depth_first_base root container viewport style text text_component line polyline polygon circle ellipse rect path use image gradient marker ignored).each do |filename|
require "prawn/svg/elements/#{filename}"
end
Expand Down
4 changes: 3 additions & 1 deletion lib/prawn/svg/elements/base.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
class Prawn::SVG::Elements::Base
extend Forwardable

include Prawn::SVG::Elements::CallDuplicator

include Prawn::SVG::Calculators::Pixels

include Prawn::SVG::Attributes::Transform
Expand Down Expand Up @@ -106,7 +108,7 @@ def append_calls_to_parent
end

def add_calls_from_element(other)
@calls.concat other.base_calls
@calls.concat duplicate_calls(other.base_calls)
end

def new_call_context_from_base
Expand Down
37 changes: 37 additions & 0 deletions lib/prawn/svg/elements/call_duplicator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#
# Unfortunately, prawn mutates arguments passed in to it.
# When we make a copy of one of the call stacks, we need to make a deep
# duplicate of it so that the first time prawn mutates the arguments, it
# won't affect the subsequent calls.
#
module Prawn::SVG::Elements::CallDuplicator
private

def duplicate_calls(calls)
calls.map { |call| duplicate_call(call) }
end

def duplicate_call(call)
[call[0], duplicate_array(call[1]), duplicate_calls(call[2])]
end

def duplicate_array(array)
array.map do |value|
case value
when Array then duplicate_array(value)
when Hash then duplicate_hash(value)
else value
end
end
end

def duplicate_hash(hash)
hash.each.with_object({}) do |(key, value), result|
result[key] = case value
when Array then duplicate_array(value)
when Hash then duplicate_hash(value)
else value
end
end
end
end

0 comments on commit ee8622d

Please sign in to comment.