Programmatically fill PDF

Created on 6 June 2023, over 1 year ago
Updated 9 July 2023, over 1 year ago

Hi,

I am building an app where the workflow is:

original_node (done by site manager) -> webform_submission (done by user) - >fillpdf -> pdf file attached to new_node (available to user)

I need to:

  1. Programmatically create a template using a pdf that is attached to the original_node, or;

    Use the pdf file attached to the original_node as a template; and,
  2. Attach the filled PDF to new_node

The original_node fields and fillpdf fields definitions are always the same, but different original_nodes will have different pdf files attached to them. I need for each of these pdf files to be available as a template for use to create new_nodes.

Option 1 would be fired when original_node is saved; option 2 would be used as a part of a queue controller (as would the template form option 1), to create new_node.

Thanks!

💬 Support request
Status

Fixed

Version

5.0

Component

Code

Created by

🇳🇿New Zealand glennnz

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @glennnz
  • 🇳🇿New Zealand glennnz

    I've changed my UX for now, requiring the PDF template files to be uploaded first, then referencing them in my original_node.

    Now I can, within my QueueWorker, create the url for the filled PDF; this is working fine.

    Now I need to attach that filled PDF to the new_node; how do I do that? So far as I can see, unless I have a browser point to that URL, the filled PDF won't be created. I need to create it from my QueueWorker during a cron run, and attach it it new_node.

    Thanks.

  • 🇳🇿New Zealand glennnz

    I'm making some progress here, just by using a RedirectResponse, but I suspect there's a better way to do it...

    Now I'm trying to loop through several forms and create several PDF files, all with data from the same webform submission.

    My code is:

        // Create the documents with FillPDF
        $fillpdf_urls = array(
          ('https://example.com/fillpdf?fid=' . $fid_1 . '&entity_type=webform_submission&entity_id=' . $sid . '&download=0&flatten=1'),
          ('https://example.com/fillpdf?fid=' . $fid_2 . '&entity_type=webform_submission&entity_id=' . $sid . '&download=0&flatten=1'),
          ('https://example.com/fillpdf?fid=' . $fid_3 . '&entity_type=webform_submission&entity_id=' . $sid . '&download=0&flatten=1')
        );
        foreach($fillpdf_urls as $fillpdf_url) {
          $response = new RedirectResponse($fillpdf_url);
          $request = \Drupal::request();
          // Save the session so things like messages get saved.
          $request->getSession()->save();
          $response->prepare($request);
          // Make sure to trigger kernel events.
          \Drupal::service('kernel')->terminate($request, $response);
          $response->send();
        }
    

    This only creates the first one; I think because a redirection is occuring at the first $response->send().

    How can I get this loop to work?

    Thanks

  • 🇳🇿New Zealand glennnz

    Just found this error:

    Failed to start the session because headers have already been sent by "/container/application/public/vendor/symfony/http-foundation/Response.php" at line 384.

    This indicates that my RedirectResponse will never work to create more than one.

    Help please!

    Thanks

  • 🇨🇦Canada Liam Morland Ontario, CA 🇨🇦

    Once you send a redirect, the browser will proceed to request the new URL. A single request cannot send multiple redirects. If you need to create multiple PDFs, don't use the fill URL; go directly to the code that generates the PDF.

  • 🇳🇿New Zealand glennnz

    Liam, is this the link_manipulator service?

  • 🇳🇿New Zealand glennnz

    Liam,

    I'm trying:

          // Create the documents with FillPDF
          $query = [
            'fid' => $fid,
            'entity_type' => 'webform_submission',
            'entity_id' => $sid,
            'download' => '0',
            'flatten' => '1',
          ];
          $url = Url::fromRoute('fillpdf.populate_pdf', [], ['query' => $query]);
          $context = $this->linkManipulator->parseLink($url);
    

    This is returning Call to a member function parseLink() on null, where am I going wrong here?

    Thanks.

  • 🇨🇦Canada Liam Morland Ontario, CA 🇨🇦

    Wherever you are running this, $this->linkManipulator is NULL. If you want to use ::parseLink(), it needs to be an object that implements FillPdfLinkManipulatorInterface.

    But you probably don't want to do that. If you want to fill a PDF, you need to find the function that does that. Look at what functions get run when there is a request to route fillpdf.populate_pdf. Don't replicate the request, look further in the code for a function that actually does the filling.

  • 🇳🇿New Zealand glennnz

    When I use fillpdf.populate_pdf I get an error.

    My code within my Controller's method is:

    <?php
    
    namespace Drupal\example_module\Controller;
    
    use Drupal\Core\Controller\ControllerBase;
    use \Drupal\Core\Url;
    use Drupal\node\Entity\Node;
    use Drupal\webform\Entity\WebformSubmission;
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\HttpFoundation\Request;
    use Drupal\fillpdf\Component\Utility\FillPdf;
    use Drupal\file\Entity\File;
    use Drupal\fillpdf\Entity\FillPdfForm;
    use Drupal\fillpdf\TokenResolver;
    
    class TestController extends ControllerBase {
    	
      public function testfillpdf() {
    
        <-- a bunch of stuff to get the webform data-->
    
        $fillpdf_route = Url::fromRoute('fillpdf.populate_pdf', [], [
          'query' => [
            'fid' => $template,
            'entity_id'=>"webform_submission:{$sid}",
            'download'=>0,
            'flatten'=>1,
          ],
        ]);
        $this->drupalGet($fillpdf_route);
      }
    }
    

    I get error, "You have requested a non-existent service "fillpdf.populate_pdf"". I can't see what I'm doing different to public function testPdfPopulation() in PdfWebformPopulationTest.php.

    Thanks

  • 🇨🇦Canada Liam Morland Ontario, CA 🇨🇦

    Don't copy the test. Look at what happens when a request come in to a FillPDF fill URL. Something will handle the request and pass it on to the code that actually fills the PDF. You can call that code directly. I don't remember what it is called.

  • Status changed to Fixed over 1 year ago
  • 🇳🇿New Zealand glennnz

    Code that works:

      $backend_proxy=\Drupal::service('fillpdf.backend_proxy');
      $fillpdf_form = FillPdfForm::load($fid);
      $populated_pdf = $backend_proxy->merge($fillpdf_form, $entities);
      $filename = ('custom-filename.pdf');
      $destination = 'private://' . $filename;
      $file_entity = \Drupal::service('file.repository')->writeData($populated_pdf, $destination, FileSystemInterface::EXISTS_REPLACE);
    
  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024