prawn icon indicating copy to clipboard operation
prawn copied to clipboard

Rescue after Exception leaves the document in an invalid state

Open cdekker opened this issue 9 years ago • 4 comments

In a project of mine I allow my users to create a template that will be used to generate PDFs using Prawn.

A template has one or more 'blocks' that are evaluated sequentially while writing the PDF. There are different kind of blocks I allow my users to create, such as simple text, floating boxes, images and tables.

  blocks.each do |block|
    begin
      case block.type_id
        when TemplateBlock::TYPE_TEXT
          render_block_text(block)
        when TemplateBlock::TYPE_SPACE
          render_block_space(block)
        when TemplateBlock::TYPE_BLOCK
          render_block_block(block)
        when TemplateBlock::TYPE_LOGO
          render_block_logo(block)
        when TemplateBlock::TYPE_TABLE
          render_block_table(block)
      end
    rescue
    end
  end

Since I cannot validate 100% what the users enters without restricting freedom, it sometimes happens that a specific block cannot be rendered correctly. For example, I had a user who wanted to render a logo block (image) repeated on every page, which was larger than the page itself. This resulted in a PDF::Core::Errors::EmptyGraphicStateStack exception which was correctly caught (and silently ignored) by the rescue block listed in the sample above.

However, I expected the rest of the blocks after the failed one to render correctly, but this was not the case. All blocks after the one who failed were simply not displayed on the final render. Is this a bug, or am I doing something wrong?

Inside render_block_logo the Prawn method image is called as follows. This is also the line where the exception occurs on:

  repeat :all do
    image block.payload.path, opts
  end

To reiterate, this works fine when all attributes are within reasonable limits. I want to simply fail silently and skip blocks where 'unrenderable' demands are made by my users.

cdekker avatar Jan 09 '16 21:01 cdekker

Still running into this problem from time to time. Also happens when a bounding box has insufficient size for the content. However, I can't seem to figure out why my exception handling block will not pick it up. Even when I specifically catch the PDF::Core::Errors::EmptyGraphicStateStack exception

cdekker avatar Mar 08 '16 08:03 cdekker

@cdekker Could you please provide a minimal example that reproduces the issue?

pointlessone avatar Mar 08 '16 08:03 pointlessone

The exception not being handled was an error on my part. It occured in a part that was outside of my block. However, I reproduced the other issue where a CORRECTLY caught exception causes the rest of the document to not be rendered.

Quick example of 3 blocks:

  • first is fine
  • second gives exception
  • third is fine but doesn't get rendered after caught exception
  def test
    doc = Prawn::Document.new(:page_size => 'A4', :page_layout => :portrait)

    begin
      # Text wont fit. Pushed to next page
      doc.bounding_box [50, 50], :width  => 50, :height => 50 do
        doc.text 'Looooooooooooooooooooooooooong text' * 10
      end
    rescue
    end

    begin
      # Text wont fit. Repeat every page. EXCEPTION!
      doc.repeat :all do
        doc.bounding_box [200, 50], :width  => 50, :height => 50 do
          doc.text 'Looooooooooooooooooooooooooong text' * 10
        end
      end
    rescue
    end

    begin
      # Standard fitting text in box, repeated
      # WILL NOT BE RENDERED BECAUSE OF PREVIOUS (CAUGHT) EXCEPTION
      doc.repeat :all do
        doc.bounding_box [350, 50], :width  => 50, :height => 50 do
          doc.text 'fitting!'
        end
      end
    rescue
    end

    send_data doc.render, type: :pdf, disposition: 'inline'
  end

cdekker avatar Mar 08 '16 09:03 cdekker

@cdekker Thank you. We will look into it a bit later.

pointlessone avatar Mar 08 '16 09:03 pointlessone