Using lists in prawn

后端 未结 7 1013
深忆病人
深忆病人 2021-02-13 15:53

Im using prawn to create pdfs that contain much data in table format and some lists. The problem with the lists is that Im just using text as lists because there is no semantic

相关标签:
7条回答
  • 2021-02-13 15:56

    An excellent solution that respects the cursor position as well as render like a true list with a small number of lines of code is:

    items = ["first","second","third"]
    def bullet_list(items)
      start_new_page if cursor < 50
      items.each do |item|
        text_box "•", at: [13, cursor]
        indent(30) do
          text item
        end
      end
    end
    

    The start_new_page clause covers scenarios where the bullet line item may need to go onto the next page. This maintains keeping the bullet with the bullet content.

    Example PDF Rendering Screenshot:

    Example Rendered List

    0 讨论(0)
  • 2021-02-13 15:56

    Just did this for a customer. For everybody who wants to render preformatted html containing ul / ol lists:

    def render_html_text(text, pdf)
      #render text (indented if inside ul)
      indent = 0 #current indentation (absolute, e.g. n*indent_delta for level n)
      indent_delta = 10 #indentation step per list level
      states = [] #whether we have an ol or ul at level n
      indices = [] #remembers at which index the ol list at level n, currently is
    
      #while there is another list tag do
      #  => starting position of list tag is at i
      #  render everything that comes before the tag
      #  cut everything we have rendered from the whole text
      #end
      while (i = text.index /<\/?[ou]l>/) != nil do
        part = text[0..i-1]
        if indent == 0 #we're not in a list, but at the top level
          pdf.text part, :inline_format => true
        else
          pdf.indent indent do
            #render all the lis
            part.gsub(/<\/li>/, '').split('<li>').each do |item|
              next if item.blank? #split may return some ugly start and end blanks
    
              item_text = if states.last == :ul
                            "• #{item}"
                          else # :ol
                            indices[indices.length-1] = indices.last + 1
                            "#{indices.last}. #{item}"
                          end
    
              pdf.text item_text, :inline_format => true
            end
          end
        end
    
        is_closing = text[i+1] == '/' #closing tag?
        if is_closing
          indent -= indent_delta
          i += '</ul>'.length
    
          states.pop
          indices.pop
        else
          pdf.move_down 10 if indent == 0
    
          type_identifier = text[i+1] #<_u_l> or <_o_l>
          states << if type_identifier == 'u'
                      :ul
                    elsif type_identifier == 'o'
                      :ol
                    else
                      raise "what means type identifier '#{type_identifier}'?"
                    end
          indices << 0
    
          indent += indent_delta
          i += '<ul>'.length
        end
    
        text = text[i..text.length-1] #cut the text we just rendered
      end
    
      #render the last part
      pdf.text text, :inline_format => true unless text.blank?
    end
    
    0 讨论(0)
  • 2021-02-13 16:03

    To create a bullet with Adobe's built in font, use \u2022.

    \u2022 This will be the first bullet item
    \u2022 blah blah blah
    

    Prawn supports symbols (aka glyphs) with WinAnsi codes and these must be encoded as UTF-8. See this post for more details: https://groups.google.com/forum/#!topic/prawn-ruby/axynpwaqK1g

    The Prawn manual has a complete list of the glyphs that are supported.

    0 讨论(0)
  • 2021-02-13 16:07

    I just had a similar problem and solved it within Prawn a slightly different way than using a table:

    ["Item 1","Item 2","Item 3"].each() do |list-item|
    
      #create a bounding box for the list-item label
      #float it so that the cursor doesn't move down
      float do
        bounding_box [15,cursor], :width => 10 do
          text "•"
        end
      end
    
      #create a bounding box for the list-item content
      bounding_box [25,cursor], :width => 600 do
        text list-item
      end
    
      #provide a space between list-items
      move_down(5)
    
    end
    

    This could obviously be extended (for example, you could do numbered lists with an each_with_index() rather than each()). It also allows for arbitrary content in the bounding box (which isn't allowed in tables).

    0 讨论(0)
  • 2021-02-13 16:08

    Prawn was a good PDF library but the problem is its own view system. There is Prawn-format but is not maintained anymore.

    I suggest to use WickedPDF, it allows you to include simple ERB code in your PDF.

    Using Prawn: another dirty and ugly solution is a two column table without border, first column contains list-bullet, second column text:

    table([ ["•", "First Element"],
            ["•", "Second Element"],
            ["•", "Third Element"] ])
    
    0 讨论(0)
  • 2021-02-13 16:08

    I think a better approach is pre-processing the HTML string using Nokogiri, leaving only basics tags that Prawn could manage with "inline_format" option, as in this code:

    def self.render_html_text(instr)
       # Replacing <p> tag
       outstr = instr.gsub('<p>',"\n")
       outstr.gsub!('</p>',"\n")
       # Replacing <ul> & <li> tags
       doc = Nokogiri::HTML(outstr)
       doc.search('//ul').each do |ul|
         content = Nokogiri::HTML(ul.inner_html).xpath('//li').map{|n| "• #{n.inner_html}\n"}.join
         ul.replace(content)
       end
       #removing some <html><body> tags inserted by Nokogiri
       doc.at_xpath('//body').inner_html
    end
    
    0 讨论(0)
提交回复
热议问题