2013-09-26 3 views
0

У меня есть метод, который сканирует строку HTML и вид форматирует его prawnpdf:Rails: добавление к последнему объекту, если условия встретились

def format_for_prawn(pdf, string, colour) 
    body = Nokogiri::HTML::DocumentFragment.parse(string) 
    result = body.xpath('./*|./text()') 
    result.each do |breaker| 
    if breaker.name == "h3" 
     pdf.fill_color colour 
     pdf.text breaker.text.to_s, :size => 16 
     pdf.move_down 5 
    else 
     pdf.fill_color '#444444' 
     pdf.text breaker.text.to_s, :size => 10, :leading => 1 
     pdf.move_down 10 
    end 
    end 
end 

Он отлично работает для <h3> с. Если обнаружены теги середины абзаца <b> (или аналогичные), он начинает новый абзац, потому что именно там Nokogiri сломал строку - это правильное поведение.

Как я могу добавить выделенную строку к последней функции pdf.text вместо вызова нового pdf.text, который приводит к новому абзацу?

Я думал о создании массива из всего этого, но тогда он будет неисправен с <h3>.

Любая помощь будет оценена по достоинству.

ответ

1

Моя первая мысль была сделать отрицательный матч:

body.xpath('./node()[not(self::b)]') 

К сожалению, это было бы исключить <b>, а не игнорировать ее:

> body = Nokogiri::HTML::DocumentFragment.parse %(<h3><b>foo</b></h3><h3>bar</h3>fooz<b>baz</b>whatever); true 

> body.xpath('./node()[not(self::b)]').to_a 
[ 
    [0] <h3> 
    <b>foo</b> 
</h3>, 
    [1] <h3>bar</h3>, 
    [2] fooz, 
    [3] whatever 
] 

Таким образом, вы не будете иметь никакого выбора, кроме используя буфера, здесь: мы можем сначала выполнить итерацию по узлам, чтобы заполнить буфер, если у нас должна быть новая строка или нет, затем повторите этот буфер, чтобы ваши строки добавлены в pdf:

buffer = [] 

body.xpath('./node()').each do |node| 
    if %w[text b].include? node.name 
    # add to previous line or create one 
    buffer << [] unless buffer.count 
    buffer.last << { node: node } 
    else 
    # set content and create a new line 
    buffer << [ { node: node, title: node.name == 'h3' } ] 
    buffer << [] 
    end 
end 

# Now, each first level item in buffer is a line, 
# containing elements we just have to concatenate text of 
# to pass to `pdf#text` 
buffer.each do |line| 
    text = line.map do |part| 
    node = part[ :node ] 
    inner = node.text.to_s 
    # restore <b> tag if you want bold style in pdf 
    node.name == 'b' ? "<b>#{inner}</b>" : inner 
    end.join 

    if line.first 
    if line.first[ :title ] 
     pdf.fill_color colour 
     pdf.text text, :size => 16 
     pdf.move_down 5 
    else 
     pdf.fill_color '#444444' 
     # inline_format ensure basic html formating is used, <b> in our case 
     # See http://prawn.majesticseacreature.com/docs/0.11.1/Prawn/Text.html#method-i-text 
     pdf.text text, size: 10, leading: 1, inline_format: true 
     pdf.move_down 10 
    end 
    end  
end 

Конечно, все это означает, что вы не контролируете оригинальный html. Кроме того, вы должны разместить свои текстовые узлы внутри <p> или что-то еще, и проблем больше не будет.

+0

Большое спасибо за ваш вдумчивый ответ. Разумное звучит хорошо, я не знал, как его выполнить. Я действительно получаю текст неопределенного метода 'для #

+0

На самом деле, я просто понял, что это никогда не сработает. Я не могу отформатировать текст по-разному в одной строке без разрыва креветок. Вернемся к чертежной доске. Inline форматирование, похоже, работает только с жирным шрифтом, и ничего больше –

+0

Да, есть ошибка, 'map {| node | node.text.to_s} 'должен быть' map {| part | part [: node] .text.to_s} '. Что же касается полужирного шрифта, я не понял, что вы хотите его сохранить (не будет ли более подходящим решением html2pdf, например wicked_pdf, в этом случае?), Но вы можете воспользоваться опцией «: inline_format» креветки. Редактирование для всего этого. –

Смежные вопросы