【问题标题】:Using lists in prawn在对虾中使用列表
【发布时间】:2012-05-09 09:43:45
【问题描述】:

我正在使用 prawn 创建包含大量表格格式数据和一些列表的 pdf。列表的问题在于,我只是将文本用作列表,因为没有与 ul > li 列表等效的语义,就像我在 webfrontend 中使用它们一样。所以这些列表是不合理的。使用多条线的列表点看起来令人毛骨悚然,因为我不适合列表图标。如何在虾中实现看起来不像垃圾的列表?

【问题讨论】:

  • 我只是尝试将 iplement 列表作为文本,但只要评论超过一行,它就会看起来很糟糕。

标签: ruby-on-rails prawn


【解决方案1】:

Prawn 是一个不错的 PDF 库,但问题在于它自己的视图系统。有Prawn-format,但不再维护。

我建议使用WickedPDF,它允许您在 PDF 中包含简单的 ERB 代码。

使用 Prawn:另一个又脏又丑的解决方案是一个两列表格无边框,第一列包含列表项目符号,第二列文本:

table([ ["•", "First Element"],
        ["•", "Second Element"],
        ["•", "Third Element"] ])

【讨论】:

  • 太棒了!您还可以使用cell_style: { borders: [] } 作为选项来消除边框并使其看起来像一个实际列表。
【解决方案2】:

我刚刚遇到了类似的问题,并在 Prawn 中以与使用表格略有不同的方式解决了它:

["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

这显然可以扩展(例如,您可以使用 each_with_index() 而不是 each() 来创建编号列表)。它还允许在边界框中包含任意内容(在表格中是不允许的)。

【讨论】:

  • 此解决方案在PDF文档多页时存在问题:当文本转到下一页时,它会显示在底部。
【解决方案3】:

一个很好的解决方案,它尊重光标位置并像一个真正的列表一样用少量代码呈现:

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

start_new_page 子句涵盖项目符号行项目可能需要进入下一页的情况。这使项目符号与项目符号内容保持一致。

示例 PDF 呈现屏幕截图:

【讨论】:

    【解决方案4】:

    要使用 Adob​​e 的内置字体创建项目符号,请使用 \u2022。

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

    Prawn 支持带有 WinAnsi 代码的符号(也称为字形),这些符号必须编码为 UTF-8。更多详情见此帖:https://groups.google.com/forum/#!topic/prawn-ruby/axynpwaqK1g

    Prawn 手册有完整的受支持字形列表。

    【讨论】:

    • 为什么这个答案没有得到更多的认可? @Powers——我自己就用过这个,效果很好。应该说你的语法假设用双引号封装。
    【解决方案5】:

    刚刚为客户做了这件事。对于所有想要呈现包含 ul / ol 列表的预格式化 html 的人:

    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
    

    【讨论】:

    • 谢谢,这对我真的很有用!!可能的改进:String#blank? 不是纯 Ruby。使用.strip == '' 作为快速修复。在渲染项目符号项时使用#{item.strip},否则我的项目符号后会出现换行(可能是因为我的 HTML 标记中的换行)
    【解决方案6】:

    一种解决方法是创建一种类似于 crm 答案的方法。不同的是,当文本转到另一个页面时它不会中断,您也可以有多个级别。

    def bullet_item(level = 1, string)
        indent (15 * level), 0 do
            text "• " + string
        end
    end
    

    只需像这样调用这个方法:

    bullet_item(1, "Text for bullet point 1")
    bullet_item(2, "Sub point")
    

    随意重构。

    【讨论】:

      【解决方案7】:

      我认为更好的方法是预处理使用 Nokogiri 的 HTML 字符串,只留下 Prawn 可以使用“inline_format”选项管理的基本标签,如下代码所示:

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-03-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多