PhilterPaper / PDF-Table

Official repository for PDF::Table in Perl
https://www.catskilltech.com/FreeSW/product/PDF%2DTable/title/PDF%3A%3ATable/freeSW_full
Other
10 stars 15 forks source link

use existing PDF page as template for every new page #59

Closed davewood closed 4 years ago

davewood commented 4 years ago

I have a template page and want to use it as background for every page in my created PDF

      my $pdf      = PDF::API2->new;
      my $template = PDF::API2->open('pdf/template.pdf');
      my $page     = $pdf->import_page($template, 1);
      return $table->table(
          $pdf, $page, $data,
          new_page_func => \&new_page,
          # ...
      }
  sub new_page {
      my $template = PDF::API2->open('pdf/template.pdf');
      my $page = $template->openpage(1);
      return $page;
  }
localhost:8080/api/api/orders/1/pdf: Can't call method "new_obj" on unblessed reference at .../lib/perl5/PDF/API2/Page.pm line 268.
PhilterPaper commented 4 years ago

OK, I can replicate it with the latest PDF::Table code (pre-1.000 release) as well as with the latest PDF::API2 (2.038) and PDF::Builder (pre-3.020 release). I will have to investigate it further, and hopefully fix it, before the next release of whichever package turns out to have the bug.

Add: By the way, do you know if this happened on the first page of the table, or on a continuation page (page 2)? new_page_func should only be called on a continuation page (the original parameter $page is used for the first page of the table). If there was room on the first page for the whole table, I didn't get the error, but if I reduced the start_h and next_h sizes to force a continuation page, I then get the error message. If nothing else, a workaround (until this is fixed) might be to arrange to put the whole table on the first page.

PhilterPaper commented 4 years ago

I tried it with creating a new page in new_page() and writing some stuff, rather than opening an existing page. This did not result in an error message, even with more than one page. I know there are some issues with openpage() (see PDF::API2 RT 130722 and RT 131657 in particular) -- apparently the object produced by $template->openpage(1); is somewhat different in format than that produced by $pdf->page(), and they might not be interchangeable. If this is the root cause here, this might be a long-term problem in PDF::API2/PDF::Builder, and not a quick fix in PDF::Table.

davewood commented 4 years ago

I can confirm that the error doesnt appear if the pdf is only 1 page long.

I can also confirm that the error doesnt appear if I use ->page().

for some reason $self->{' apipdf'} is not defined on the last call

https://metacpan.org/release/PDF-API2/source/lib/PDF/API2/Page.pm#L268

debugging code

  269     my $s = '';
  270     if (exists $self->{' apipdf'}) {
  271         $s.='exists';
  272         if (defined $self->{' apipdf'}) {
  273             $s.=' and defined';
  274         }
  275         else {
  276             $s.=' but not defined';
  277         }
  278     }
  279     else {
  280         $s.='does not exist';
  281     }
  282     warn $s;
  283 
  284     $self->{' apipdf'}->new_obj($obj) unless $obj->is_obj($self->{' apipdf'});
exists and defined
exists and defined
exists and defined
exists and defined
exists and defined
exists and defined
exists and defined
exists and defined
exists and defined
exists and defined
exists and defined
exists and defined
exists and defined
exists and defined
exists and defined
exists and defined
exists but not defined

no idea why sub content is called so many times but what matters is that the last call fails.

davewood commented 4 years ago

success!

the problem seems to be that openpage() doesnt add the apipdf reference.

using import_page() in place of openpage() fixes the problem.

davewood commented 4 years ago

solution

      my $pdf      = PDF::API2->new;
      my $template = PDF::API2->open('pdf/template.pdf');
      my $new_page_func = sub { return $pdf->import_page($template, 1); };

      return $table->table(
          $pdf, $page, $data,
          x => $x, start_y => $y, w => $w, start_h => $h,
          new_page_func => $new_page_func,
      );
PhilterPaper commented 4 years ago

I will have to look at whether adding $self->{' apipdf'} to the openpage() call can (and should) be done, and if that will also fix some of the other tickets against this call. Thanks for the debugging, and glad to hear that you have found a workaround!

PhilterPaper commented 4 years ago

It's looking like using import_page() is more than a workaround... it may be the only way to do it. While openpage() does also return a page object, I don't see any place where it's being inserted into the PDF (as a new last page). Note that without new_page_func, $pdf->page() is used, which returns the page object to work on and inserts it into the PDF as the new last page.

I did find something odd going on with the page returned by openpage(). Just before exiting new_page() and returning $page, I dumped $page with Data::Dumper. It looked OK, and in particular, $page->{' apipdf'} was defined and the right ref. However, back in Table.pm (table() method), the returned $page = &{ $arg{'new_page_func'} } has $page->{' apipdf'} undef (triggering the "unblessed" Perl error message). Many other elements were missing or rearranged. It is possible that NOT being properly in the PDF may have something to do with this behavior, as the $page returned when using import_page() is apparently OK.

Has anyone else seen this kind of behavior, and know how to fix it? If not, I'll just update the POD to suggest that import_page() is the best way to import a template page, and that openpage() is not suitable.

PhilterPaper commented 4 years ago

Thinking about it some more, openpage() is meant to open a page in the (object) template, but does not transfer a page to the working (output) PDF. Therefore, import_page() would always be the appropriate usage. I will close this.