13

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

4

7 回答 7

25

Prawn 是一个很好的 PDF 库,但问题在于它自己的视图系统。有虾格式,但不再维护。

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

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

table([ ["•", "First Element"],
        ["•", "Second Element"],
        ["•", "Third Element"] ])
于 2012-06-23T16:10:40.697 回答
9

我刚刚遇到了类似的问题,并在 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() 来创建编号列表)。它还允许边界框中的任意内容(在表格中是不允许的)。

于 2012-10-05T21:37:02.250 回答
5

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

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 渲染屏幕截图:

示例呈现列表

于 2014-09-11T14:57:17.813 回答
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 手册有一个完整的受支持字形列表。

于 2013-08-02T16:43:06.080 回答
4

刚刚为客户做了这个。对于想要呈现包含 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
于 2013-09-30T09:41:28.133 回答
1

一种解决方法是创建一种类似于 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")

随意重构。

于 2013-06-18T14:43:41.913 回答
1

我认为更好的方法是使用 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
于 2018-03-25T15:36:09.267 回答