Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Repeater should be added before any other content (or allow it) #1135

Open
estani opened this issue Nov 20, 2019 · 13 comments
Open

Repeater should be added before any other content (or allow it) #1135

estani opened this issue Nov 20, 2019 · 13 comments

Comments

@estani
Copy link

estani commented Nov 20, 2019

We use different background images for different pages, adding it with a repeater this way:

repeat(->(pg) { pg >= 2 }) do
      blend_mode(:Overlay) do
        load_background_image
      end
    end

We need to do it in Overlay mode, because the text is rendered behind the image. The overlay affects the rendering so we want to get rid of it.

I think what is happening is that the repeat block (or stamp) is called after the page has been rendered, not before.

@pointlessone
Copy link
Member

Repeaters are ran right before the document gets rendered. That is, after all content has been placed. That is by design.

Is there a reason you can not render images before placing the content?

@estani
Copy link
Author

estani commented Dec 8, 2019

You mean outside of a repeater? (because as you said, repeaters are called "after all content is placed", so rendering images "before" means not using a repeater, right?)
That will require some callback, after a new page gets rendered. Pretty much like a repeater... hence that's what the PR is about.
Is there another way to get an image pasted depending on the page number "before" the content is placed that I'm not aware of? (perhaps that was your question? In that case the reason I can't do it, is because I don't know how).

To summarize the problem again: A repeater is like a stamp (it is implemented like it). Now you might want to have a stamp behind some text or above, the later is what's implemented, for the former there's the PR.

I'll be glad to make changes if that's required. I did try to follow the code logic as good as I could, but I'm by no means a prawn expert...

@pointlessone
Copy link
Member

There are a number of solutions.

  1. You can use document background:

    Prawn::Document.generate "test.pdf", background: 'background.png' do
      text 'foo'
    
      start_new_page
      text 'bar'
    
      start_new_page
      text 'baz'
    end
  2. You can use on_page_create callback:

    Prawn::Document.generate "test.pdf" do
      on_page_create do
        image 'data/images/prawn.png'
      end
    
      text 'foo'
    
      start_new_page
      text 'bar'
    
      start_new_page
      text 'baz'
    end

    However note that callback is not called for the first page of the document.

@estani
Copy link
Author

estani commented Jul 30, 2020

If I understood your code and comment correctly, this would not work:

  1. cannot use different backgrounds for different pages
  2. cannot add a background to the first page (from your comment)

From your comments the best option would be to call on_page_create for the first page too (and by the way pass some context information, like the page count)

@pointlessone
Copy link
Member

It's not quite possible to call on_page_create because the first page is created before it can be defined. As a work around you can pain the background on the first page manually.

As for the page number, it's accessible via page_number method.

Here's an example:

Prawn::Document.generate "test.pdf" do
  # This is a proc that draws background
  page_bg = proc do
    image 'background.png'
    text_box page_number.to_s # Show the page number
  end

  on_page_create(&page_bg) # Set the proc as callback

  # Manually call the background drawing proc on the first page
  # callback is called in a `float` to not disturb the flow of the page, here we do it for the same reason
  float { page_bg.call }
  move_down 20 
  text 'foo'

  start_new_page
  move_down 20
  text 'bar'

  start_new_page
  move_down 20
  text 'baz'
end

@grncdr
Copy link

grncdr commented Oct 7, 2021

It's not quite possible to call on_page_create because the first page is created before it can be defined. As a work around you can pain the background on the first page manually.

According to the docs you can also pass the :skip_page_creation option to #initialize. The below is not tested but should do what you're looking for:

Prawn::Document.generate "test.pdf", skip_page_creation: true do
  on_page_create do
     # add image based on current page_number
   end
   
   start_new_page
   # continue creating the document
end

@buncis
Copy link

buncis commented Nov 17, 2021

I have similar problem

I have an image that has a header and background for the page

  repeat(:all) do
    on_page_create do
      image "app/assets/images/header_pdf.png", :width => bounds.width
    end
  end

the problem with this is the image will be put out as foreground instead background

the workaround are using on_page_create problem with on_page_create is its not executed on first page

so you will need to add it manualy

so the complete solution are
draw the image header/background manually for the first page then use the on_page_create

    image "app/assets/images/header_pdf.png", :width => bounds.width
    repeat(:all) do 
      on_page_create do
        image "app/assets/images/header_pdf.png", :width => bounds.width
      end
    end

my current problem is move_cursor_to not working inside on_page_create do

@pointlessone
Copy link
Member

I don't think placing on_page_create into a repeater achieves anything useful.

I agree that this is a confusing behaviour but it's a nature of Prawn. You can think of Prawn as being a streaming library. Document is constructed as the code executes. The fact that you can jump around pages doesn't change this fact.

Streaming nature dictates how the document is created and in which order it is being constructed. For historical reasons (mostly) Prawn creates first page on initialization. That is before any on_page_create callback could be defined.

One work around might be to pass skip_page_creation: true option to Prawn document constructor. That will still create a page but it won't be added to the document sequence. You'll have an opportunity to define the callback but you'll have to manually add the first page.

require 'open-uri'

Prawn::Document.generate("test.pdf", skip_page_creation: true) do
  on_page_create do
    image URI.open("https://prawnpdf.org/images/prawn.png")
  end

  1.upto(3) do |n|
    start_new_page
    text "Page #{n}\n" * 10
  end
end

This produces a document with 3 pages each having an image in the background and the text in the foreground.

test.pdf

@buncis
Copy link

buncis commented Nov 17, 2021

yeah I have refine my code( I just read it somewhere that on_page_create is internal api and repeater is the public api)

the only problem that still exist to me is

how to make the text started at certain cursor (below the repeated image ideally)?

I could use move_cursor_to if I use start_new_page manually

problem is when the page creation is created by flowing content the cursor is not moved at all in that case

@pointlessone
Copy link
Member

You can try setting margins to leave enough space for your header image.

@buncis
Copy link

buncis commented Nov 18, 2021

the only way to setup margin is only by giving parameter to Prawn::Document.generate() and start_new_page() right?

so for you guys that kinda have similar problemo
here are my final solutions

  def initialize(freight, view)
    super(page_size: "A4", top_margin: 120)
    @freight = freight
    @view = view 
    font_size 11
    # stroke_bounds
    # stroke_axis
    image "app/assets/images/header_pdf.png", width: bounds.width, at: [0, bounds.height+110]
    on_page_create do
      image "app/assets/images/header_pdf.png", width: bounds.width, at: [0, bounds.height+110]
    end
    invoice_info
    order_info
    customer_info
    invoice_table
    if cursor < 278
      start_new_page
    end
    total_table

    if page_count > 1
      page_numbering
    end
  end

extra notes for u guys that confused about conversion points and measurements

the doc said default margin is 0.5 inch what number is 0.5 in the parameter? the number is 36 because 0.5 *72 is 36 as in this
https://github.com/prawnpdf/prawn/blob/006b3a5d5aa1a650e196f08108e777d2f70d156b/lib/prawn/measurements.rb

@pointlessone
Copy link
Member

the only way to setup margin is only by giving parameter to Prawn::Document.generate() and start_new_page() right?

You can also use bounding_box with a block or assign bounds on the document.

@buncis
Copy link

buncis commented Nov 18, 2021

thank you so much for helping and maintaining prawn

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

4 participants