class Prawn::SVG::CSS::Stylesheets

Attributes

css_parser[R]
media[R]
root[R]

Public Class Methods

new(css_parser, root, media = :all) click to toggle source
# File lib/prawn/svg/css/stylesheets.rb, line 5
def initialize(css_parser, root, media = :all)
  @css_parser = css_parser
  @root = root
  @media = media
end

Public Instance Methods

load() click to toggle source
# File lib/prawn/svg/css/stylesheets.rb, line 11
def load
  load_style_elements
  xpath_styles = gather_xpath_styles
  associate_xpath_styles_with_elements(xpath_styles)
end

Private Instance Methods

associate_xpath_styles_with_elements(xpath_styles) click to toggle source
# File lib/prawn/svg/css/stylesheets.rb, line 48
def associate_xpath_styles_with_elements(xpath_styles)
  element_styles = {}

  xpath_styles.each do |xpath, declarations, _|
    REXML::XPath.match(root, xpath).each do |element|
      (element_styles[element] ||= []).concat declarations
    end
  end

  element_styles
end
calculate_specificity(selector) click to toggle source
# File lib/prawn/svg/css/stylesheets.rb, line 115
def calculate_specificity(selector)
  selector.reduce([0, 0, 0]) do |(a, b, c), element|
    [
      a + (element[:id] || []).length,
      b + (element[:class] || []).length + (element[:attribute] || []).length + (element[:pseudo_class] || []).length,
      c + (element[:name] && element[:name] != "*" ? 1 : 0)
    ]
  end
end
css_selector_to_xpath(selector) click to toggle source
# File lib/prawn/svg/css/stylesheets.rb, line 64
def css_selector_to_xpath(selector)
  selector.map do |element|
    pseudo_classes = Set.new(element[:pseudo_class])

    result = case element[:combinator]
             when :child
               '/'
             when :adjacent
               pseudo_classes << 'first-child'
               '/following-sibling::'
             when :siblings
               '/following-sibling::'
             else
               '//'
             end

    result << element[:name] if element[:name]
    result << ((element[:class] || []).map { |name| "[contains(concat(' ',@class,' '), ' #{name} ')]" }.join)
    result << ((element[:id] || []).map { |name| "[@id='#{name}']" }.join)

    (element[:attribute] || []).each do |key, operator, value|
      case operator
      when nil
        result << "[@#{key}]"
      when "="
        result << "[@#{key}=#{xpath_quote value}]"
      when "^="
        result << "[starts-with(@#{key}, #{xpath_quote value})]"
      when "$="
        result << "[substring(@#{key}, string-length(@#{key}) - #{value.length - 1}) = #{xpath_quote value})]"
      when "*="
        result << "[contains(@#{key}, #{xpath_quote value})]"
      when "~="
        result << "[contains(concat(' ',@#{key},' '), #{xpath_quote " #{value} "})]"
      when "|="
        result << "[contains(concat('-',@#{key},'-'), #{xpath_quote "-#{value}-"})]"
      end
    end

    pseudo_classes.each do |pc|
      case pc
      when "first-child" then result << "[1]"
      when "last-child"  then result << "[last()]"
      when /^nth-child\((\d+)\)$/ then result << "[#{$1}]"
      end
    end

    result
  end.join
end
gather_xpath_styles() click to toggle source
# File lib/prawn/svg/css/stylesheets.rb, line 26
def gather_xpath_styles
  xpath_styles = []
  order = 0

  css_parser.each_rule_set(media) do |rule_set, _|
    declarations = []
    rule_set.each_declaration { |*data| declarations << data }

    rule_set.selectors.each do |selector_text|
      selector = Prawn::SVG::CSS::SelectorParser.parse(selector_text)
      xpath = css_selector_to_xpath(selector)
      specificity = calculate_specificity(selector)
      specificity << order
      order += 1

      xpath_styles << [xpath, declarations, specificity]
    end
  end

  xpath_styles.sort_by(&:last)
end
load_style_elements() click to toggle source
# File lib/prawn/svg/css/stylesheets.rb, line 19
def load_style_elements
  REXML::XPath.match(root, '//style').each do |source|
    data = source.texts.map(&:value).join
    css_parser.add_block!(data)
  end
end
xpath_quote(value) click to toggle source
# File lib/prawn/svg/css/stylesheets.rb, line 60
def xpath_quote(value)
  %{"#{value.gsub('\\', '\\\\').gsub('"', '\\"')}"} if value
end