class Prawn::SVG::Elements::TextComponent

Constants

PositionsList
Printable

Attributes

commands[R]

Public Instance Methods

apply() click to toggle source
# File lib/prawn/svg/elements/text_component.rb, line 30
def apply
  raise SkipElementQuietly if computed_properties.display == "none"

  font = select_font
  apply_font(font) if font

  # text_anchor isn't a Prawn option; we have to do some math to support it
  # and so we handle this in Prawn::SVG::Interface#rewrite_call_arguments
  opts = {
    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)

  add_call_and_enter 'character_spacing', spacing

  @commands.each do |command|
    case command
    when Printable
      apply_text(command.text, opts)
    when self.class
      add_call 'save'
      command.apply_step(calls)
      add_call 'restore'
    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.
  add_call 'noop' if calls.empty?
end
parse() click to toggle source
# File lib/prawn/svg/elements/text_component.rb, line 7
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']

  @commands = []

  text_children.each do |child|
    if child.node_type == :text
      append_text(child)
    else
      case child.name
      when 'tspan', 'tref'
        append_child(child)
      else
        warnings << "Unknown tag '#{child.name}' inside text tag; ignoring"
      end
    end
  end
end

Protected Instance Methods

append_child(child) click to toggle source
# File lib/prawn/svg/elements/text_component.rb, line 82
def append_child(child)
  new_state = state.dup
  new_state.text = PositionsList.new([], [], [], [], [], state.text)

  element = self.class.new(document, child, calls, new_state)
  @commands << element
  element.parse_step
end
append_text(child) click to toggle source
# File lib/prawn/svg/elements/text_component.rb, line 69
def append_text(child)
  if state.preserve_space
    text = child.value.tr("\n\t", ' ')
  else
    text = child.value.tr("\n", '').tr("\t", ' ')
    leading = text[0] == ' '
    trailing = text[-1] == ' '
    text = text.strip.gsub(/ {2,}/, ' ')
  end

  @commands << Printable.new(self, text, leading, trailing)
end
apply_font(font) click to toggle source
# File lib/prawn/svg/elements/text_component.rb, line 183
def apply_font(font)
  add_call 'font', font.name, style: font.subfamily
end
apply_text(text, opts) click to toggle source
# File lib/prawn/svg/elements/text_component.rb, line 91
def apply_text(text, opts)
  while text != ""
    x = y = dx = dy = rotate = nil
    remaining = rotation_remaining = false

    list = state.text
    while list
      shifted = list.x.shift
      x ||= shifted
      shifted = list.y.shift
      y ||= shifted
      shifted = list.dx.shift
      dx ||= shifted
      shifted = list.dy.shift
      dy ||= shifted

      shifted = list.rotation.length > 1 ? list.rotation.shift : list.rotation.first
      if shifted && rotate.nil?
        rotate = shifted
        remaining ||= list.rotation != [0]
      end

      remaining ||= list.x.any? || list.y.any? || list.dx.any? || list.dy.any? || (rotate && rotate != 0)
      rotation_remaining ||= list.rotation.length > 1
      list = list.parent
    end

    opts[:at] = [x || :relative, y || :relative]
    opts[:offset] = [dx || 0, dy || 0]

    if rotate && rotate != 0
      opts[:rotate] = -rotate
    else
      opts.delete(:rotate)
    end

    if remaining
      add_call 'draw_text', text[0..0], opts.dup
      text = text[1..-1]
    else
      add_call 'draw_text', text, opts.dup

      # we can get to this path with rotations still pending
      # solve this by shifting them out by the number of
      # characters we've just drawn
      shift = text.length - 1
      if rotation_remaining && shift > 0
        list = state.text
        while list
          count = [shift, list.rotation.length - 1].min
          list.rotation.shift(count) if count > 0
          list = list.parent
        end
      end

      break
    end
  end
end
find_referenced_element() click to toggle source
# File lib/prawn/svg/elements/text_component.rb, line 160
def find_referenced_element
  href = attributes['xlink:href']

  if href && href[0..0] == '#'
    element = document.elements_by_id[href[1..-1]]
    element if element.name == 'text'
  end
end
select_font() click to toggle source
# File lib/prawn/svg/elements/text_component.rb, line 169
def select_font
  font_families = [computed_properties.font_family, document.fallback_font_name]
  font_style = :italic if computed_properties.font_style == 'italic'
  font_weight = Prawn::SVG::Font.weight_for_css_font_weight(computed_properties.font_weight)

  font_families.compact.each do |name|
    font = document.font_registry.load(name, font_weight, font_style)
    return font if font
  end

  warnings << "Font family '#{computed_properties.font_family}' style '#{computed_properties.font_style}' is not a known font, and the fallback font could not be found."
  nil
end
text_children() click to toggle source
# File lib/prawn/svg/elements/text_component.rb, line 151
def text_children
  if name == 'tref'
    reference = find_referenced_element
    reference ? reference.source.children : []
  else
    source.children
  end
end