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