tombruijn/kotoba

View on GitHub
lib/kramdown/converter/prawn.rb

Summary

Maintainability
B
4 hrs
Test Coverage
module Kramdown::Converter
  class Prawn < Base
    include Kotoba::Formatting

    # Prawn document, is called to add content
    attr_reader :prawn
    # Integer value that counts the number of paragraphs in one section
    # @see convert_p
    attr_reader :paragraph_count, :heading_count

    def initialize(root, options, prawn)
      super(root, options)
      @prawn = prawn
      reset_paragraph_count!
      @heading_count = Hash.new { |hash, key| hash[key] = 0 }
    end

    def self.convert(tree, options = {}, prawn)
      options = options.merge(tree.options[:options] || {})
      converter = new(tree, ::Kramdown::Options.merge(options), prawn)
      result = converter.convert(tree)
      result.encode!(tree.options[:encoding]) if result.respond_to?(:encode!)
      [result, converter.warnings]
    end

    def convert(el, options = {})
      send("convert_#{el.type}", el, options)
    end

    def convert_blank(el, options = {})
    end

    def convert_text(el, options = {})
      el.value
    end

    def convert_p(el, inherited_style = {})
      @paragraph_count += 1

      style = if inherited_style.empty?
        style_for_paragraph(@paragraph_count)
      else
        reset_paragraph_count!
        inherited_style
      end
      style.merge!(count: inherited_style[:count] || @paragraph_count)
      write_text(to_text(el, style), style)
    end

    def convert_codespan(el, options = {})
      style = style_for(:code)
      inline_format(to_text(el, style), style)
    end

    def convert_codeblock(el, options = {})
      style = style_for(:code)
      write_text el.value, style
    end

    def convert_blockquote(el, options = {})
      style = style_for(:quote)
      convert_children(el.children, style)
    end

    def convert_header(el, options = {})
      level = el.options[:level]
      style = style_for(:heading, level)
      @heading_count[level] += 1
      text = to_text(el, style.merge(count: @heading_count[level]))
      write_text text, style

      prawn.register_heading(
        name: strip_tags(text),
        level: level,
        page: prawn.page_count
      )
    end

    def convert_hr(el, options = {})
      prawn.stroke_horizontal_rule
    end

    def convert_ul(el, options = {})
      @ol_index = 0
      style = style_for(el.type == :ol ? :ordered_list : :unordered_list)
      convert_children(el.children, style)
    end
    alias :convert_ol :convert_ul
    alias :convert_dl :convert_ul

    def convert_li(el, style = {})
      @ol_index += 1
      convert_children(el.children, style.merge(count: @ol_index))
    end
    alias :convert_dd :convert_li

    def convert_dt(el, options = {})
    end

    def convert_html_element(el, style = {})
      content_tag to_text(el, style), (el.value || el.type), el.attr
    end
    alias :convert_strong :convert_html_element
    alias :convert_em :convert_html_element

    def convert_xml_comment(el, options = {})
      raise "XML comment not supported: #{el.inspect}"
    end
    alias :convert_xml_pi :convert_xml_comment

    def convert_table(el, options = {})
      raise "Table not supported: #{el.inspect}"
    end
    alias :convert_thead :convert_table
    alias :convert_tbody :convert_table
    alias :convert_tfoot :convert_table
    alias :convert_tr :convert_table

    def convert_td(el, options = {})
      raise "Table cell not supported: #{el.inspect}"
    end

    def convert_comment(el, options = {})
      raise "Comment not supported: #{el.inspect}"
    end

    def convert_br(el, options = {})
      raise "BR not supported: #{el.inspect}"
    end

    def convert_a(el, options = {})
      "<link href='#{URI.escape(el.attr["href"], /'/)}'>"\
      "#{convert_children(el.children).join}</link>"
    end

    def convert_img(el, options = {})
      raise "Image not supported: #{el.inspect}"
    end

    def convert_footnote(el, options = {})
      raise "Footnote not supported: #{el.inspect}"
    end

    def convert_raw(el, options = {})
      raise "Raw not supported: #{el.inspect}"
    end

    def convert_entity(el, options = {})
      ::Kramdown::Utils::Entities.entity(el.value.to_s).char
    end
    alias :convert_smart_quote :convert_entity

    def convert_typographic_sym(el, options = {})
      # http://kramdown.rubyforge.org/syntax.html#typographic-symbols
      # raise "Typographic Symbol not supported: #{el.inspect}"
    end

    def convert_math(el, options = {})
      raise "Math not supported: #{el.inspect}"
    end

    def convert_abbreviation(el, options = {})
      raise "Abbreviation not supported: #{el.inspect}"
    end

    def convert_root(el, options = {})
      convert_children(el.children)
    end

    protected

    def to_text(el, style)
      prefix = format_prefix(style[:prefix], style)
      text = el.children.empty? ? el.value : convert_children(el.children).join
      "#{prefix}#{text}"
    end

    def write_text(text, style)
      prawn.font style[:font] do
        prawn.text text, style
      end
    end

    def convert_children(children, options = {})
      results = []
      children.each do |child|
        results << convert(child, options)
      end
      results
    end

    # Returns the layout configuration for a specific element type
    #
    # @param element [Symbol] element type
    # @param selector [Object] sub selector for type
    #   (e.g. 1 for heading level 1)
    # @return [Object] a Kotoba::Layout subclass
    #
    def layout_for(element, selector = nil)
      layout = Kotoba.config.layout_for_page(prawn.page_number)
      if selector
        layout.send(element, selector)
      else
        layout.send(element)
      end
    end

    def style_for(element, selector = nil)
      layout_for(element, selector).to_h.merge(inline_format: true)
    end

    def style_for_paragraph(i)
      style = style_for(:default)
      paragraph = layout_for(:paragraph)
      style.merge!(paragraph.to_h) if paragraph.indent?(i)
      style
    end

    def reset_paragraph_count!
      @paragraph_count = 0
    end
  end
end