lib/prawn/svg/elements/text_component.rb in prawn-svg-0.27.1 vs lib/prawn/svg/elements/text_component.rb in prawn-svg-0.28.0

- old
+ new

@@ -1,18 +1,24 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase attr_reader :commands Printable = Struct.new(:element, :text, :leading_space?, :trailing_space?) - PositionsList = Struct.new(:x, :y, :dx, :dy, :rotation, :parent) + TextState = Struct.new(:parent, :x, :y, :dx, :dy, :rotation, :spacing, :mode) def parse - state.text.x = attributes['x'].split(COMMA_WSP_REGEXP).collect {|n| x(n)} if attributes['x'] - state.text.y = attributes['y'].split(COMMA_WSP_REGEXP).collect {|n| y(n)} if attributes['y'] - state.text.dx = attributes['dx'].split(COMMA_WSP_REGEXP).collect {|n| x_pixels(n)} if attributes['dx'] - state.text.dy = attributes['dy'].split(COMMA_WSP_REGEXP).collect {|n| y_pixels(n)} if attributes['dy'] - state.text.rotation = attributes['rotate'].split(COMMA_WSP_REGEXP).collect(&:to_f) if attributes['rotate'] + if state.inside_clip_path + raise SkipElementError, "<text> elements are not supported in clip paths" + end + state.text.x = (attributes['x'] || "").split(COMMA_WSP_REGEXP).collect { |n| x(n) } + state.text.y = (attributes['y'] || "").split(COMMA_WSP_REGEXP).collect { |n| y(n) } + state.text.dx = (attributes['dx'] || "").split(COMMA_WSP_REGEXP).collect { |n| x_pixels(n) } + state.text.dy = (attributes['dy'] || "").split(COMMA_WSP_REGEXP).collect { |n| y_pixels(n) } + state.text.rotation = (attributes['rotate'] || "").split(COMMA_WSP_REGEXP).collect(&:to_f) + state.text.spacing = calculate_character_spacing + state.text.mode = calculate_text_rendering_mode + @commands = [] svg_text_children.each do |child| if child.node_type == :text append_text(child) @@ -39,15 +45,18 @@ size: computed_properties.numerical_font_size, style: font && font.subfamily, text_anchor: computed_properties.text_anchor } - spacing = computed_properties.letter_spacing - spacing = spacing == 'normal' ? 0 : pixels(spacing) + if state.text.parent + add_call_and_enter 'character_spacing', state.text.spacing unless state.text.spacing == state.text.parent.spacing + add_call_and_enter 'text_rendering_mode', state.text.mode unless state.text.mode == state.text.parent.mode + else + add_call_and_enter 'character_spacing', state.text.spacing unless state.text.spacing == 0 + add_call_and_enter 'text_rendering_mode', state.text.mode unless state.text.mode == :fill + end - add_call_and_enter 'character_spacing', spacing - @commands.each do |command| case command when Printable apply_text(command.text, opts) when self.class @@ -57,12 +66,12 @@ else raise end end - # It's possible there was no text to render. In that case, add a 'noop' so - # character_spacing doesn't blow up when it finds it doesn't have a block to execute. + # It's possible there was no text to render. In that case, add a 'noop' so character_spacing/text_rendering_mode + # don't blow up when they find they don't have a block to execute. add_call 'noop' if calls.empty? end protected @@ -79,11 +88,11 @@ @commands << Printable.new(self, text, leading, trailing) end def append_child(child) new_state = state.dup - new_state.text = PositionsList.new([], [], [], [], [], state.text) + new_state.text = TextState.new(state.text) element = self.class.new(document, child, calls, new_state) @commands << element element.parse_step end @@ -186,7 +195,31 @@ nil end def apply_font(font) add_call 'font', font.name, style: font.subfamily + end + + def calculate_text_rendering_mode + fill = computed_properties.fill != 'none' + stroke = computed_properties.stroke != 'none' + + if fill && stroke + :fill_stroke + elsif fill + :fill + elsif stroke + :stroke + else + :invisible + end + end + + def calculate_character_spacing + spacing = computed_properties.letter_spacing + spacing == 'normal' ? 0 : pixels(spacing) + end + + # overridden, we don't want to call fill/stroke as draw_text does this for us + def apply_drawing_call end end