class Prawn::SVG::Elements::Text < Prawn::SVG::Elements::Base def parse case attributes['xml:space'] when 'preserve' state.preserve_space = true when 'default' state.preserve_space = false end @relative = state.text_relative || false if attributes['x'] || attributes['y'] @relative = false @x_positions = attributes['x'].split(COMMA_WSP_REGEXP).collect {|n| document.x(n)} if attributes['x'] @y_positions = attributes['y'].split(COMMA_WSP_REGEXP).collect {|n| document.y(n)} if attributes['y'] end @x_positions ||= state.text_x_positions || [document.x(0)] @y_positions ||= state.text_y_positions || [document.y(0)] end def apply raise SkipElementQuietly if computed_properties.display == "none" font = select_font apply_font(font) if font add_call_and_enter "text_group" if name == 'text' if attributes['dx'] || attributes['dy'] add_call_and_enter "translate", document.distance(attributes['dx'] || 0), -document.distance(attributes['dy'] || 0) end # 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 : document.points(spacing) add_call_and_enter 'character_spacing', spacing source.children.each do |child| if child.node_type == :text text = child.value.strip.gsub(state.preserve_space ? /[\n\t]/ : /\s+/, " ") while text != "" opts[:at] = [@x_positions.first, @y_positions.first] if @x_positions.length > 1 || @y_positions.length > 1 add_call 'draw_text', text[0..0], opts.dup text = text[1..-1] @x_positions.shift if @x_positions.length > 1 @y_positions.shift if @y_positions.length > 1 else add_call @relative ? 'relative_draw_text' : 'draw_text', text, opts.dup @relative = true break end end elsif child.name == "tspan" add_call 'save' new_state = state.dup new_state.text_x_positions = @x_positions new_state.text_y_positions = @y_positions new_state.text_relative = @relative Prawn::SVG::Elements::Text.new(document, child, calls, new_state).process add_call 'restore' else warnings << "Unknown tag '#{child.name}' inside text tag; ignoring" 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 private 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 def apply_font(font) add_call 'font', font.name, style: font.subfamily end end