You are here

function fillpdf_execute_merge in FillPDF 7

Same name and namespace in other branches
  1. 6 fillpdf.module \fillpdf_execute_merge()
  2. 7.2 fillpdf.module \fillpdf_execute_merge()

Utility to allow other functions to merge PDFs.

Utility function to allow other functions to merge PDFs with the various methods in a consistent way.

Parameters

string $method: The service or program being used. Possible values: local, remote, pdftk.

array $fields: The fields to merge into the PDF. Should be retrieved from the {fillpdf_fields} table.

mixed $fillpdf: When in URL mode, this is the record from {fillpdf_forms}. When in Stream mode, this is the PDF data.

string $mode: A special flag to control the behavior of this function. URL mode merges using a PDF on the file system and Stream mode merges using the value of $fillpdf directly. Possible values: url, stream.

Return value

bool|null|string The output of the fill method or FALSE on failure.

1 call to fillpdf_execute_merge()
fillpdf_merge_pdf in ./fillpdf.module
Constructs a page and sends it to the browser or saves it.

File

./fillpdf.module, line 1396

Code

function fillpdf_execute_merge($method, array $fields, $fillpdf, $mode = 'url', $flatten = TRUE, $image_data = array()) {
  $data = NULL;

  // Try to prepare the data so that the $method part can process it without
  // caring too much about merge tool.
  switch ($mode) {
    case 'url':
      $filename = $fillpdf->url;
      break;
    case 'stream':
      $filename = file_unmanaged_save_data($fillpdf, file_directory_temp() . '/pdf_data.pdf', FILE_EXISTS_RENAME);
      break;
    default:

      // Ensure variable is always set to something.
      $filename = $fillpdf->url;
  }
  $contents = _fillpdf_get_file_contents($filename, '<front>');
  switch ($method) {

    // FillPDF Service.
    case 'remote':

      // Anonymize image data from the fields array; we should not send the real
      // filename to FillPDF Service. We do this in the specific fill method
      // because others (e.g. local) may need the filename on the local system.
      foreach ($fields as $field_name => &$field) {
        if (!empty($image_data[$field_name])) {
          $field_path_info = pathinfo($field);
          $field = '{image}' . md5($field_path_info['filename']) . '.' . $field_path_info['extension'];
        }
      }
      unset($field);
      $api_key = variable_get('fillpdf_api_key', '0');
      $result = _fillpdf_xmlrpc_request(FILLPDF_DEFAULT_SERVLET_URL, 'merge_pdf_v3', base64_encode($contents), $fields, $api_key, $flatten, $image_data);
      if ($result->error == TRUE) {
        if ($mode === 'stream') {
          file_unmanaged_delete($filename);
        }

        // After error message set in _fillpdf_xmlrpc_request().
        return FALSE;
      }
      $data = base64_decode($result->data);
      break;

    // FillPDF LocalService.
    case 'local_service':

      // Translate passed fields into the format the API expects.
      $field_mappings = array();
      foreach ($fields as $key => $field) {
        if (strpos($field, '{image}') === 0) {

          // If this is an image, then we should check the $image_data array for
          // the actual information. We can get the extension from the
          // filenamehash parameter.
          if (!empty($image_data[$key]) && !empty($image_data[$key]['data']) && !empty($image_data[$key]['filenamehash'])) {
            $field_mappings[$key] = array(
              'type' => 'image',
              'data' => $image_data[$key]['data'],
              'extension' => pathinfo($image_data[$key]['filenamehash'], PATHINFO_EXTENSION),
            );
            continue;
          }
        }
        $field_mappings[$key] = array(
          'type' => 'text',
          'data' => $field,
        );
      }

      // Build an API request and get the REST API to handle the request.
      $request = array(
        'pdf' => base64_encode($contents),
        'flatten' => $flatten,
        'fields' => $field_mappings,
      );
      $json = drupal_json_encode($request);
      $merge_endpoint = variable_get('fillpdf_local_service_endpoint') . '/api/v1/merge';
      $result = drupal_http_request($merge_endpoint, array(
        'method' => 'POST',
        'data' => $json,
        'headers' => array(
          'Content-Type' => 'application/json',
        ),
      ));
      if ((int) $result->code !== 200) {
        if ($result->code) {
          drupal_set_message('Error ' . $result->code . '. Reason: ' . $result->error, 'error');
        }
        else {
          drupal_set_message('Error occurred merging PDF: ' . $result->error, 'error');
        }
        $fields = array();
        break;
      }
      $data = base64_decode(drupal_json_decode($result->data)['pdf']);
      if ($mode === 'stream') {
        file_unmanaged_delete($filename);
      }
      break;

    // Local JavaBridge servlet.
    case 'local':
      $require = drupal_get_path('module', 'fillpdf') . '/lib/JavaBridge/java/Java.inc';
      require_once DRUPAL_ROOT . '/' . $require;
      try {
        $fillpdf = new java('com.ocdevel.FillpdfService', base64_encode($contents), 'bytes');
        foreach ($fields as $key => $field) {
          if (substr($field, 0, 7) == '{image}') {

            // Remove {image} marker.
            $image_filepath = substr($field, 7);
            $fillpdf
              ->image($key, $image_filepath, "file");
          }
          else {
            $fillpdf
              ->text($key, $field);
          }
        }
      } catch (JavaException $e) {
        if ($mode == 'stream') {
          file_unmanaged_delete($filename);
        }
        $error = check_plain(java_truncate((string) $e));
        drupal_set_message($error, 'error');
        watchdog('fillpdf', $error, array(), WATCHDOG_ERROR);

        // After setting error message.
        return FALSE;
      }
      try {
        if ($flatten) {
          $data = java_values(base64_decode($fillpdf
            ->toByteArray()));
        }
        else {
          $data = java_values(base64_decode($fillpdf
            ->toByteArrayUnflattened()));
        }
      } catch (JavaException $e) {
        if ($mode == 'stream') {
          file_unmanaged_delete($filename);
        }
        $error = check_plain(java_truncate((string) $e));
        drupal_set_message($error, 'error');
        watchdog('fillpdf', $error, array(), WATCHDOG_ERROR);

        // After setting error message.
        return FALSE;
      }
      break;
    case 'pdftk':

      // Looks like I'm the first actually to use this! (wizonesolutions).
      module_load_include('inc', 'fillpdf', 'xfdf');
      $xfdfname = $filename . '.xfdf';
      $xfdf = create_xfdf(basename($xfdfname), $fields);

      // Generate the file.
      $xfdffile = file_unmanaged_save_data($xfdf, $xfdfname, FILE_EXISTS_RENAME);

      // Now feed this to pdftk and save the result to a variable.
      $pdftk_command = array();
      $pdftk_command[] = fillpdf_pdftk_path();
      $pdftk_command[] = escapeshellarg(drupal_realpath($filename));
      $pdftk_command[] = 'fill_form';
      $pdftk_command[] = escapeshellarg(drupal_realpath($xfdffile));
      $pdftk_command[] = 'output -';
      if ($flatten) {
        $pdftk_command[] = 'flatten';
      }
      $pdftk_command[] = 'drop_xfa';
      $pdftk_command = implode(' ', $pdftk_command);

      // Run the pdftk command and read stdout, stderr, and exit status.
      $descriptorspec = array(
        1 => array(
          'pipe',
          'w',
        ),
        2 => array(
          'pipe',
          'w',
        ),
      );
      $proc = proc_open($pdftk_command, $descriptorspec, $pipes);

      // Read stdout.
      $data = stream_get_contents($pipes[1]);
      fclose($pipes[1]);

      // Read stderr.
      $stderr = stream_get_contents($pipes[2]);
      fclose($pipes[2]);

      // Read exit status.
      $exit_status = proc_close($proc);

      // Public error message if no data returned by pdftk.
      if (!$data) {
        drupal_set_message(t('Error with pdftk. No PDF generated.'), 'error');
      }

      // Log errors when no PDF or non-zero exit status.
      if (!$data || $exit_status !== 0) {
        $message = 'Error with pdftk: Exit status: !exit_status; data length: !data_length; stderr: @stderr';
        $variables = array(
          '!exit_status' => $exit_status,
          '!data_length' => strlen($data),
          '@stderr' => $stderr,
        );
        watchdog('fillpdf', $message, $variables, WATCHDOG_ERROR);
      }
      file_unmanaged_delete($xfdffile);
      break;
    case 'test':
      $data = file_get_contents(drupal_get_path('module', 'fillpdf') . '/tests/fillpdf_test_v4.pdf');
      variable_set('fillpdf_test_last_merge_metadata', array(
        'fields' => $fields,
        'images' => $image_data,
        'flatten' => $flatten,
      ));
  }
  if ($data) {
    return $data;
  }
  else {
    return FALSE;
  }
}