Rails prawn chart not rendered completely at the bottom of the page

979 views Asked by At

I am creating a pdf report in order to show some data using the "squid" gem. This would allow me to display charts in my pdf. The only issue i found is that when the chart does not fit at the bottom of the page then it looks rendered partially which does not look good at all. Any idea how can i fix this?

enter image description here

Here is the code i am using to render the charts

require 'squid'

class SurveyPdf < Prawn::Document
  def initialize(survey, view)
    super()
    font "#{Rails.root}/app/assets/fonts/roboto-condensed.ttf"

    @survey = survey
    @view = view
    questions
  end

  def questions
    @survey.questions.each do |question|
      text "#{question.title}", size: 20
      text "Answers #{question.answers.size}", size: 15
      if ["single", "select"].include? question.question_type.prefix
        if question.answers.choice_counter.any?
          chart choices: question.answers.choice_counter
        end
      end
      if question.question_type.prefix == "image"
        if question.answers.image_counter.any?
          chart images: question.answers.image_counter
        end
      end
      if question.question_type.prefix == "multiple"
        if question.answers.multiple_choice_counter.any?
          chart choices: question.answers.multiple_choice_counter
        end
      end
      if question.question_type.prefix == "raiting"
        move_down 5
        if question.answers.any?
          text_box "Average rating", size: 12, width: 120, :at => [0,  cursor - 2]
          text_box "#{average_rating(question.answers.rating_average)}", size: 12, width: 120, :at => [4 * 30,  cursor - 2]
        else
          text_box "Average rating", size: 12, width: 120, :at => [0,  cursor - 2]
          text_box "0", size: 12, width: 120, :at => [4 * 30,  cursor - 2]
        end
      end
    end
  end
end
2

There are 2 answers

2
engineersmnky On BEST ANSWER

To answer the question in your comment as to determining page size I will run through a few useful methods too long for a comment:

d = Prawn::Document.new
d.y #full page height
d.margin_box.bottom #actually top since prawn starts at the bottom
d.margin_box.absolute_bottom #actual top with margins included
d.margin_box.top #usable page height
d.margin_box.absolute_top #same as #y 
d.cursor #where you are vertically on the page

So you can use some basic math to determine fit:

#this is all chart does excepting chart calls #draw 
#which we don't want to do until we determine if it fits 
c = Squid::Graph.new(d, choices: question.answers.choice_counter) 

#determine if the chart will fit vertically 
#if not start a new page and move to the top
unless d.cursor + c.height < d.margin_box.top
   d.start_new_page
   d.move_cursor_to(0)
end

#draw the chart onto the appropriate page
c.draw

Hope this helps in some way

7
BigRon On

For a similar issue I used the prawn-grouping gem

It pre-renders whatever you place in a group block to test whether it fits on the current page. If not, it skips to the next page and renders.

In your case you would do something like:

def questions
  @survey.questions.each do |question|
    group :too_tall => lambda { start_new_page } do |g|
      g.text "#{question.title}", size: 20
      g.text "Answers #{question.answers.size}", size: 15
      if ["single", "select"].include? question.question_type.prefix
        if question.answers.choice_counter.any?
          g.chart choices: question.answers.choice_counter
        end
      end
      if question.question_type.prefix == "image"
        if question.answers.image_counter.any?
          g.chart images: question.answers.image_counter
        end
      end
      if question.question_type.prefix == "multiple"
        if question.answers.multiple_choice_counter.any?
          g.chart choices: question.answers.multiple_choice_counter
        end
      end
      if question.question_type.prefix == "raiting"
        move_down 5
        if question.answers.any?
          g.text_box "Average rating", size: 12, width: 120, :at => [0,  cursor - 2]
          g.text_box "#{average_rating(question.answers.rating_average)}", size: 12, width: 120, :at => [4 * 30,  cursor - 2]
        else
          g.text_box "Average rating", size: 12, width: 120, :at => [0,  cursor - 2]
          g.text_box "0", size: 12, width: 120, :at => [4 * 30,  cursor - 2]
        end
      end
    end
  end
end

disclaimer: I've never used squid so the only piece I'm not sure of is g.chart let me know if you have issues there and I will try to figure it out.

Update for squid

The prawn-grouping gem doesn't know about the squid methods (like chart). So we can extract the logic from the prawn-grouping gem and add it directly in your survey_pdf.rb. Copy lines 7-63 from this file, and remove prawn-grouping gem from your app.

if you are curious why this works...

Squid uses the Prawn::Document.extensions method to force Prawn::Document to inherit the squid methods. You can see that in the squid gem code here on line 37.

For prawn-grouping to work it creates a new Prawn::Document as part of the group method. You can see that here on line 55. The problem was that the Prawn::Document instantiated via the prawn-grouping gem wasn't inheriting the squid methods... but your SurveyPdf instance of Prawn::Document does inherit the squid methods, so by adding the grouping logic into your SurveyPdf class, now the Prawn::Document instantiated in your group method will work.