You are here

fillpdf.module in FillPDF 6

Allows mappings of PDFs to site content

File

fillpdf.module
View source
<?php

/**
 * @file
 * Allows mappings of PDFs to site content
 */
define("DEFAULT_SERVLET_URL", variable_get('fillpdf_remote_protocol', 'http') . "://" . variable_get('fillpdf_remote_endpoint', "fillpdf-service.com/xmlrpc.php"));
module_load_include('inc', 'fillpdf', 'fillpdf.admin');
if (module_exists('webform')) {

  //@@TODO refactor this, will post the webform_tokens module on D.O
  module_load_include('inc', 'fillpdf', 'webform_tokens');
}

/**
 * Implementation of hook_help().
 */
function fillpdf_help($path, $arg) {
  switch ($path) {
    case 'admin/help#fillpdf':
      $content = _fillpdf_get_file_contents(drupal_get_path('module', 'fillpdf') . '/README.txt');
      $content = '<pre>' . check_plain($content) . '</pre>';
      return $content;
    case 'admin/content/fillpdf':
      if (module_exists('help')) {
        return t('See the !link for an explanation on downloading these forms to PDF', array(
          '!link' => l(t('Documentation'), 'admin/help/fillpdf'),
        ));
      }
      else {
        return t('Activate the help module if you need an ' . 'explanation on downloading these forms to PDF.');
      }
  }
}

/**
 * Implementation of hook_menu().
 */
function fillpdf_menu() {
  $access = array(
    'administer pdfs',
  );
  $items = array();

  // fillpdf&fid=10&nids[]=1&webforms[0][nid]=2&webforms[0][sid]=3
  $items['fillpdf'] = array(
    'page callback' => 'fillpdf_parse_uri',
    // Can't use access callback.  We need the arguments, but they're passed as $GET.  Will access-check in fillpdf_merge_pdf
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
  );

  // ------- Config ---------------------------
  $items['admin/settings/fillpdf'] = array(
    'title' => 'Fill PDF Settings',
    'description' => 'Configure Fill PDF Servelet Information',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fillpdf_settings',
    ),
    'access arguments' => $access,
  );

  // --------- Form ------------------------
  $items['admin/content/fillpdf'] = array(
    'title' => 'Fill PDF',
    'description' => 'Manage your PDFs',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fillpdf_forms',
    ),
    'access arguments' => $access,
  );
  $items['admin/content/fillpdf/%'] = array(
    'title' => 'Edit PDF Form',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fillpdf_form_edit',
      3,
    ),
    'access arguments' => $access,
  );
  $items['admin/content/fillpdf/%/delete'] = array(
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fillpdf_form_delete_confirm',
      3,
    ),
    'access arguments' => $access,
    'type' => MENU_CALLBACK,
  );
  $items['admin/content/fillpdf/%/export'] = array(
    'title' => t('Export Fill PDF Field Mappings'),
    'page callback' => 'fillpdf_form_export',
    'page arguments' => array(
      3,
    ),
    'access arguments' => $access,
  );
  $items['admin/content/fillpdf/%/import'] = array(
    'title' => t('Import Fill PDF Field Mappings'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fillpdf_form_import_form',
      3,
    ),
    'access arguments' => $access,
  );

  // --------- Fields ------------------------
  $items['admin/content/fillpdf/%/add'] = array(
    'title' => 'Add field',
    'page callback' => 'fillpdf_field',
    'page arguments' => array(
      4,
      3,
    ),
    'access arguments' => $access,
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/content/fillpdf/%/edit/%'] = array(
    'page callback' => 'fillpdf_field',
    'page arguments' => array(
      4,
      3,
      5,
    ),
    'access arguments' => $access,
    'type' => MENU_CALLBACK,
  );
  $items['admin/content/fillpdf/%/delete/%'] = array(
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fillpdf_field_delete_confirm',
      3,
      5,
    ),
    'access arguments' => $access,
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implementation of hook_perm().
 */
function fillpdf_perm() {
  return array(
    'administer pdfs',
    'publish own pdfs',
    'publish all pdfs',
  );
}

/**
 * Gets a link to the prinable PDF, merged with the passed-in data
 * @param array/int $nids or $nid, if you pass in one value it will merge with that node.
 *  If array, it will merge with multiple nodes, with later nids overriding previous ones.
 * @param array $webforms Array of webforms, of this strucure: array('nid'=>1, 'sid'=>1)
 * @param bool $sample True if you want to populate the form with its own field-names (to get a gist of PDF)
 */
function fillpdf_pdf_link($fid, $nids = null, $webform_arr = null, $sample = false) {
  if (is_array($nids)) {
    $nids_uri = '&nids[]=' . implode('&nids[]=', $nids);
  }
  elseif (isset($nids)) {
    $nids_uri = "&nids[]={$nids}";
  }
  if (is_array($webform_arr)) {
    if ($webform_arr['nid']) {

      // didn't pass in as array(array('nid','sid'))
      $webform_arr = array(
        $webform_arr,
      );
    }
    foreach ($webform_arr as $key => $webform) {
      $webforms_uri .= "&webforms[{$key}][nid]={$webform['nid']}";
    }
    $webforms_uri .= $webform['sid'] ? "&webforms[{$key}][sid]={$webform['sid']}" : "";
  }
  $sample = $sample ? '&sample=true' : '';
  return url('', array(
    'absolute' => true,
  )) . "fillpdf?fid={$fid}{$nids_uri}{$webforms_uri}{$sample}";
}

/**
 * Get the data and form that need to be merged, from the $_GET, and print the PDF
 * @seealso fillpdf_pdf_link for $_GET params
 */
function fillpdf_parse_uri() {
  $sample = $_GET['sample'];

  // is this just the PDF populated with sample data?
  $fid = $_GET['fid'];
  $nids = $webforms = array();
  $force_download = FALSE;
  $flatten = TRUE;
  if ($_GET['nid'] || $_GET['nids']) {
    $nids = $_GET['nid'] ? array(
      $_GET['nid'],
    ) : $_GET['nids'];
  }
  if ($_GET['webform'] || $_GET['webforms']) {
    $webforms = $_GET['webform'] ? array(
      $_GET['webform'],
    ) : $_GET['webforms'];
  }
  if (isset($_GET['download']) && (int) $_GET['download'] == 1) {
    $force_download = TRUE;
  }
  if (isset($_GET['flatten']) && (int) $_GET['flatten'] == 0) {
    $flatten = FALSE;
  }
  fillpdf_merge_pdf($fid, $nids, $webforms, $sample, $force_download, FALSE, $flatten);
}

/**
 * Constructs a page from scratch (pdf content-type) and sends it to the
 * browser or saves it, depending on if a custom path is configured or not.
 *
 * @param $fid
 *   The integer ID of the PDF.
 * @param $nids
 *   Array of integer IDs of the CCK nodes from which to draw data.
 * @param $webform_arr
 *   Array of integer IDs of the Webform nodes from which to draw data.
 * @param $sample
 *   If "true" (exact string), each field will be filled with its field name.
 * @param $force_download
 *   Boolean. If TRUE, always send a PDF to the browser, even if a
 *   destination_path is set for the PDF.
 * @param $skip_access_check
 *   Boolean. If TRUE, do not do any access checks. Allow the user to download
 *   any PDF with data from any node. Only use when access checks are being
 *   done some other way.
 * @param $flatten
 *   Boolean. If TRUE, flatten the PDF so that fields cannot be edited.
 *   Otherwise leave fields editable.
 *
 * @return
 *   Nothing.
 *
 * @seealso fillpdf_pdf_link()
 * for $_GET params
 */
function fillpdf_merge_pdf($fid, $nids = null, $webform_arr = null, $sample = null, $force_download = FALSE, $skip_access_check = FALSE, $flatten = TRUE) {

  // Case 1: No $fid
  if (is_null($fid)) {
    drupal_set_message('Fillpdf Form ID required to print a PDF', 'warning');
    drupal_goto();
  }
  $fillpdf_info = db_fetch_object(db_query("SELECT title, url, destination_path, replacements, destination_redirect FROM {fillpdf_forms} WHERE fid=%d", $fid));

  // Case 1.5: $fid is not valid.
  if ($fillpdf_info === FALSE) {
    drupal_set_message(t('Non-existent Fill PDF Form ID.'), 'error');
    drupal_not_found();
    exit;
  }
  $fillpdf_info->replacements = _fillpdf_replacements_to_array($fillpdf_info->replacements);
  global $user;
  $nodes = $webforms = array();

  // Nodes
  if (is_array($nids)) {
    foreach ($nids as $nid) {
      $nodes[] = node_load($nid);
    }
  }

  // Webforms
  if (module_exists('webform') && is_array($webform_arr)) {

    // Load the submissions inc depending on webform-2.x or webform-3.x
    $webform_info_file = drupal_parse_info_file(drupal_get_path('module', 'webform') . '/webform.info');
    if (strstr($webform_info_file['version'], "6.x-2") === FALSE) {
      module_load_include('inc', 'webform', 'includes/webform.submissions');
    }
    else {
      module_load_include('inc', 'webform', 'webform_submissions');
    }
    foreach ($webform_arr as $webform) {
      if (!$webform['sid']) {

        // user didn't specify submission-id, meaning they want most recent
        $webform['sid'] = db_result(db_query('select sid from {webform_submissions}
          where nid=%d and uid=%d order by submitted desc', $webform['nid'], $user->uid));
      }
      $webforms[] = array(
        'webform' => node_load($webform['nid']),
        'submission' => webform_get_submission($webform['nid'], $webform['sid']),
      );
    }
  }

  // Are we skipping the access check? This may be done when the PDF is generated
  // programmatically. Use wisely.
  if ($skip_access_check !== TRUE) {
    if (!fillpdf_merge_pdf_access($nodes, $webforms)) {
      drupal_access_denied();
      module_invoke_all('exit');
      exit;
    }
  }
  $fields = $token_objects = $image_data = array();
  $query = db_query("SELECT * FROM {fillpdf_fields} WHERE fid=%d", $fid);
  while ($obj = db_fetch_object($query)) {
    $obj->replacements = _fillpdf_replacements_to_array($obj->replacements);

    // Keep track of whether we're dealing with an image or not
    $transform_string = FALSE;

    // Fill a sample PDF & return
    if ($sample == 'true') {
      $fields[$obj->pdf_key] = $obj->pdf_key;

      // If sampling, return to the form edit page
      $_REQUEST['destination'] = "admin/content/fillpdf/{$fid}";
    }
    else {

      // multiple nids, #516840
      // we want the last nid in $_GET to override previous ones (aka, of fillpdf?nids[]=1&nids[]=2, 2 wins)
      $nodes = array_reverse($nodes);
      $webforms = array_reverse($webforms);

      // --- node token replacements
      if (!empty($nodes)) {
        foreach ($nodes as $node) {
          $token_objects['node'] = $node;
          $token = token_replace($obj->value, $type = 'node', $object = $node);
          if ($token && $token != $obj->value) {
            break;
          }
        }
        $transform_string = TRUE;
      }

      // If they're populating with an Image field
      if (strstr($obj->value, 'filefield-fid]')) {
        module_load_include('inc', 'filefield', 'field_file');
        $filefield = field_file_load($token);
        $file_bytes = _fillpdf_get_file_contents($filefield['filepath']);
        $str = base64_encode($file_bytes);
        $transform_string = FALSE;
        $fields[$obj->pdf_key] = '{image}' . realpath($filefield['filepath']);
        $image_path_info = pathinfo(realpath($filefield['filepath']));

        // Store the image data to transmit to the remote service if necessary
        $image_data[$obj->pdf_key] = array(
          'data' => base64_encode(file_get_contents(realpath($filefield['filepath']))),
          'filenamehash' => md5($image_path_info['filename']) . '.' . $image_path_info['extension'],
        );
      }
      else {
      }

      // /--- node token replacements ---
      // --- webform token replacements
      if (!empty($webforms)) {
        foreach ($webforms as $webform) {
          $token_objects['webform'] = $webform['submission'];
          $token = token_replace($obj->value, $type = 'webform', $webform['submission']);
          if ($token) {
            break;
          }
        }
        $transform_string = TRUE;
      }

      // /--- webform token replacements ---
      if ($transform_string) {

        // Replace <br /> occurrences with newlines
        $str = preg_replace('|<br />|', '
', $token);
        $str = _fillpdf_transform_field_value($str, $fillpdf_info->replacements, $obj->replacements);
        $fields[$obj->pdf_key] = $str;
      }
    }
  }
  $pdf_data = _fillpdf_get_file_contents($fillpdf_info->url, "<front>");
  $fillpdf_remote_service = variable_get('fillpdf_remote_service', TRUE);
  $fillpdf_local_service = variable_get('fillpdf_local_service', TRUE);

  // use fillpdf-service.com's xmlrpc service (must be registered)
  if ($fillpdf_remote_service) {
    $api_key = variable_get('fillpdf_api_key', '0');
    $result = _fillpdf_xmlrpc_request(DEFAULT_SERVLET_URL, 'merge_pdf_v3', base64_encode($pdf_data), $fields, $api_key, $flatten, $image_data);
    if ($result->error == true) {
      drupal_goto();
    }

    //after setting error message
    $data = base64_decode($result->data);
  }
  elseif ($fillpdf_local_service) {
    $require = drupal_get_path('module', 'fillpdf') . '/lib/JavaBridge/java/Java.inc';
    require_once $require;
    try {
      $fillpdf = new java('com.ocdevel.FillpdfService', base64_encode($pdf_data), 'bytes');
      foreach ($fields as $key => $field) {
        if (substr($field, 0, 7) == '{image}') {

          // Remove {image} marker.
          $image_bytes = substr($field, 7);
          $fillpdf
            ->image($key, $image_bytes, "file");
        }
        else {
          $fillpdf
            ->text($key, $field);
        }
      }
    } catch (JavaException $e) {
      drupal_set_message(java_truncate((string) $e), 'error');
      drupal_goto();

      //after setting error message
    }
    try {
      if ($flatten) {
        $data = java_values(base64_decode($fillpdf
          ->toByteArray()));
      }
      else {
        $data = java_values(base64_decode($fillpdf
          ->toByteArrayUnflattened()));
      }
    } catch (JavaException $e) {
      drupal_set_message(java_truncate((string) $e), 'error');
      drupal_goto();

      //after setting error message
    }
  }
  else {
    $data = fillpdf_execute_merge('pdftk', $fields, $fillpdf_info, 'url', $flatten);
  }

  // Log this, could be useful
  watchdog('fillpdf', 'User %user has generated form %form for node %node.', array(
    '%user' => $user->name,
    '%form' => $fillpdf_info->title,
    '%node' => $node->title,
  ));

  // Generate the filename of downloaded PDF from title of the PDF set in
  // admin/content/fillpdf/%fid
  $output_name = _fillpdf_process_filename($fillpdf_info->title, $token_objects);
  if (!empty($fillpdf_info->destination_path)) {
    $destination_path = _fillpdf_process_destination_path($fillpdf_info->destination_path, $token_objects);
    $path_exists = file_check_directory($destination_path, FILE_CREATE_DIRECTORY);
    if ($path_exists === FALSE) {
      watchdog('fillpdf', "The path %destination_path does not exist and could not be\n        automatically created. Therefore, the previous submission was not saved. If\n        the URL contained download=1, then the PDF was still sent to the user's browser.\n        If the destination path looks wrong and you have used tokens, check that you have\n        used the correct token and that it is available to Fill PDF at the time of PDF\n        generation.", array(
        '%destination_path' => $destination_path,
      ));
    }
    else {

      // Full steam ahead!
      $saved_file_path = file_save_data($data, $destination_path . "/{$output_name}", FILE_EXISTS_RENAME);
    }
    if ($force_download === FALSE) {
      if (isset($_GET['destination']) === FALSE) {

        // Should we send the user directly to the saved PDF? If so, do that.
        if ($fillpdf_info->destination_redirect) {
          drupal_goto(file_create_url($saved_file_path));
        }
      }

      // Allow the "destination" query string parameter to be used
      // e.g. fillpdf?nid=1&fid=1&destination=node/1
      // If no destination is provided, drupal_goto() will send the
      // user to the front page.
      drupal_goto();
    }
  }
  drupal_set_header('Pragma: public');
  drupal_set_header('Expires: 0');
  drupal_set_header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  drupal_set_header('Content-type:application/pdf');
  drupal_set_header('Content-Length: ' . strlen($data));
  drupal_set_header('Content-disposition:attachment; filename="' . $output_name . '"');
  drupal_set_header('Content-Transfer-Encoding: binary');
  echo $data;
  exit;
}

/**
 * Make sure the user has access to data they want to populate the PDF
 */
function fillpdf_merge_pdf_access($nodes = array(), $webforms = array()) {
  if (user_access('administer pdfs') || user_access('publish all pdfs')) {
    return TRUE;
  }
  if (!user_access('publish own pdfs')) {
    return FALSE;
  }
  global $user;
  if (empty($webforms)) {
    foreach ($nodes as $node) {

      // own node?
      if (!node_access('view', $node) || $node->uid != $user->uid) {
        return FALSE;
      }
    }
  }
  else {
    foreach ($webforms as $webform) {

      // In this case, we only care that they can view the Webform
      if (!node_access('view', node_load($webform['webform']->nid))) {
        return FALSE;
      }
    }
  }

  // Own webform submission?
  if (!empty($webforms)) {
    if (!($user->uid && (user_access('access own webform submissions') || user_access('access webform results') || user_access('access webform submissions')))) {
      return FALSE;
    }
    foreach ($webforms as $webform) {
      if (!webform_submission_access($webform['webform'], $webform['submission'], 'view')) {
        return FALSE;
      }
    }
  }
  return TRUE;
}
function _fillpdf_process_filename($original, $token_objects) {

  // Replace tokens *before* sanitization
  if (!empty($token_objects)) {
    if (isset($token_objects['node'])) {
      $type = 'node';
    }
    elseif (isset($token_objects['webform'])) {
      $type = 'webform';
    }
    $original = token_replace($original, $type, $token_objects[$type]);
  }
  $output_name = str_replace(' ', '_', $original);
  $output_name = preg_replace('/\\.pdf$/i', '', $output_name);
  $output_name = preg_replace('/[^a-zA-Z0-9_.-]+/', '', $output_name) . '.pdf';
  return $output_name;
}

/**
 * Utility function to allow other functions to merge PDFs with the various methods in a consistent way.
 * @param string $method The service or program being used. Possible values: local, remote, pdftk. Currently, only pdftk is supported.
 * @param array $fields The fields to merge into the PDF. Should be retrieved from the {fillpdf_fields} table.
 * @param mixed $fillpdf When in URL mode, this is the record from {fillpdf_forms}. When in Stream mode, this is the PDF data.
 * @param 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.
 */
function fillpdf_execute_merge($method, $fields, $fillpdf, $mode = 'url', $flatten = TRUE) {
  $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_save_data($fillpdf, file_directory_temp() . '/pdf_data.pdf', FILE_EXISTS_RENAME);
      break;
  }
  switch ($method) {
    case 'pdftk':
      module_load_include('inc', 'fillpdf', 'xfdf');

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

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

      // Now feed this to pdftk and save the result to a variable
      $data = shell_exec('pdftk ' . escapeshellarg($filename) . ' fill_form ' . escapeshellarg($xfdffile) . ' output - ' . ($flatten ? 'flatten ' : '') . 'drop_xfa');
      if ($data === NULL) {
        drupal_set_message(t('pdftk not properly installed. No PDF generated.'), 'error');
      }
      file_delete($xfdffile);
      if ($mode == 'stream') {
        file_delete($filename);
      }
      break;
  }
  if ($data) {
    return $data;
  }
  else {
    return FALSE;
  }
}

/**
 * This function generates the form fields from the specified PDF.  It (1) sends a request to the iText
 * servlet to parse the specified PDF, (2) iText returns an XML response with fields-mappings, this module
 * parses the XML response & contsructs the fields.
 */
function fillpdf_parse_pdf($fid) {
  $filename = db_result(db_query("SELECT url FROM {fillpdf_forms} WHERE fid=%d", $fid));
  $content = _fillpdf_get_file_contents($filename, '<front>');
  $fillpdf_remote_service = variable_get('fillpdf_remote_service', true);
  $fillpdf_local_service = variable_get('fillpdf_local_service', TRUE);

  // use fillpdf-service.com's xmlrpc service (must be registered)
  if ($fillpdf_remote_service) {
    $result = _fillpdf_xmlrpc_request(DEFAULT_SERVLET_URL, 'parse_pdf_fields', base64_encode($content));
    if ($result->error == true) {
      drupal_goto("admin/content/fillpdf");
    }

    //after setting error message
    $fields = $result->data;
  }
  elseif ($fillpdf_local_service) {
    $require = drupal_get_path('module', 'fillpdf') . '/lib/JavaBridge/java/Java.inc';
    require_once $require;
    try {
      $fillpdf = new java('com.ocdevel.FillpdfService', base64_encode($content), 'bytes');
      $fields = java_values($fillpdf
        ->parse());
    } catch (JavaException $e) {
      drupal_set_message(java_truncate((string) $e), 'error');
      drupal_goto("admin/content/fillpdf");

      //after setting error message
    }
  }
  else {
    $fields = fillpdf_execute_parse('pdftk', $filename);
  }

  //create fields
  foreach ((array) $fields as $key => $arr) {
    if ($arr['type']) {

      // Don't store "container" fields
      $arr['name'] = str_replace('&#0;', '', $arr['name']);

      // pdftk sometimes inserts random &#0; markers - strip these out. NOTE: This may break forms that actually DO contain this pattern, but 99%-of-the-time functionality is better than merge failing due to improper parsing.
      $field = new stdClass();
      $field->fid = $fid;
      $field->pdf_key = $arr['name'];
      $field->label = $arr['name'];
      drupal_write_record('fillpdf_fields', $field);
    }
  }
}

/**
 * Utility function to allow other functions to parse PDFs with the various methods in a consistent way.
 *
 * @param string $method The service or program being used. Possible values: local, remote, pdftk. Currently, only pdftk is supported.
 * @param mixed $fillpdf When in URL mode, this is the filename to the PDF to parse. When in Stream mode, this is the PDF data.
 * @param 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.
 */
function fillpdf_execute_parse($method, $fillpdf, $mode = 'url') {
  switch ($mode) {
    case 'url':
      $filename = $fillpdf;
      break;
    case 'stream':
      $filename = file_save_data($fillpdf, file_directory_temp() . '/pdf_data.pdf', FILE_EXISTS_RENAME);
      break;
  }

  // Use exec() to call pdftk (because it will be easier to go line-by-line parsing the output) and pass $content via stdin. Retrieve the fields with dump_data_fields.
  $output = array();
  $status = NULL;
  exec('pdftk ' . escapeshellarg($filename) . ' dump_data_fields', $output, $status);
  if (in_array($status, array(
    126,
    127,
  ))) {
    drupal_set_message(t('pdftk not properly installed.'), 'error');
    return array();
  }
  elseif (count($output) === 0) {
    drupal_set_message(t('PDF does not contain fillable fields.'), 'warning');
    return array();
  }

  // Build a simple map of dump_data_fields keys to our own array keys
  $data_fields_map = array(
    'FieldType' => 'type',
    'FieldName' => 'name',
    'FieldFlags' => 'flags',
    'FieldJustification' => 'justification',
  );

  // Build the fields array
  $fields = array();
  $fieldindex = -1;
  foreach ($output as $line => $lineitem) {
    if ($lineitem == '---') {
      $fieldindex++;
      continue;
    }

    // Separate the data key from the data value
    $linedata = explode(':', $lineitem);
    if (in_array($linedata[0], array_keys($data_fields_map))) {
      $fields[$fieldindex][$data_fields_map[$linedata[0]]] = trim($linedata[1]);
    }
  }
  if ($mode == 'stream') {
    file_delete($filename);
  }
  return $fields;
}
function _fillpdf_get_file_contents($filepath, $error_goto = null) {
  if ($error_goto && !file_exists($filepath)) {
    drupal_set_message("{$filepath} does not exist.  Check your\n      filesystem settings, as well as http://drupal.org/node/764936", 'error');
    drupal_goto($error_goto);
  }
  $handle = fopen($filepath, "r");
  $content = fread($handle, filesize($filepath));
  fclose($handle);
  return $content;
}
function _fillpdf_xmlrpc_request($url, $method) {
  $args = func_get_args();
  $result = call_user_func_array('xmlrpc', $args);
  $ret = new stdClass();
  if (isset($result['error'])) {
    drupal_set_message($result['error'], 'error');
    $ret->error = true;
  }
  else {
    if ($result == false || xmlrpc_error()) {
      $error = xmlrpc_error();
      $ret->error = true;
      drupal_set_message("There was a problem contacting the Fill PDF service.\n        It maybe be down, or you may not have internet access.  [ERROR {$error->code}: {$error->message}]", 'error');
    }
    else {
      $ret->data = $result['data'];
      $ret->error = false;
    }
  }
  return $ret;
}
function fillpdf_get_fields($fid) {
  $result = db_query('select * from {fillpdf_fields} where fid = %d', $fid);
  $return = array();
  while ($result_array = db_fetch_array($result)) {
    $return[$result_array['pdf_key']] = array(
      'label' => $result_array['label'],
      'value' => $result_array['value'],
    );
  }
  return $return;
}
function _fillpdf_process_destination_path($destination_path, $token_objects) {

  // Two formats of $destination_path are possible:
  //   1) /absolute/path/to/directory
  //   2) path/below/files/directory
  // So, first: Does it begin with a forward slash?
  $orig_path = $destination_path;
  $destination_path = trim($orig_path);

  // Replace any applicable tokens
  $types = array();
  if (isset($token_objects['node'])) {
    $types[] = 'node';
  }
  elseif (isset($token_objects['webform'])) {
    $types[] = 'webform';
  }
  if (substr($destination_path, 0, 1) == '/') {

    // No further modifications needed
  }
  else {

    // Slap on the files directory in front and return it
    $destination_path = file_directory_path() . "/{$destination_path}";
  }
  foreach ($types as $type) {
    $destination_path = token_replace($destination_path, $type, $token_objects[$type]);
  }
  return $destination_path;
}
function _fillpdf_replacements_to_array($replacements) {
  $standardized_replacements = str_replace(array(
    "\r\n",
    "\r",
  ), "\n", $replacements);
  $lines = explode("\n", $standardized_replacements);
  $return = array();
  foreach ($lines as $replacement) {
    if (!empty($replacement)) {
      $split = explode('|', $replacement);
      $return[$split[0]] = preg_replace('|<br />|', '
', $split[1]);
    }
  }
  return $return;
}

/**
 * Apply any field value transformations defined via the UI.
 * Note that the replacement arguments need to already have been run through
 *   _fillpdf_replacements_to_array().
 * @see _fillpdf_replacements_to_array().
 */
function _fillpdf_transform_field_value($value, $pdf_replacements, $field_replacements) {
  if (empty($pdf_replacements) && empty($field_replacements)) {
    return $value;
  }
  elseif (!empty($field_replacements) && isset($field_replacements[$value])) {
    return $field_replacements[$value];
  }
  elseif (!empty($pdf_replacements) && isset($pdf_replacements[$value])) {
    return $pdf_replacements[$value];
  }
  else {
    return $value;
  }
}

Functions

Namesort descending Description
fillpdf_execute_merge Utility function to allow other functions to merge PDFs with the various methods in a consistent way.
fillpdf_execute_parse Utility function to allow other functions to parse PDFs with the various methods in a consistent way.
fillpdf_get_fields
fillpdf_help Implementation of hook_help().
fillpdf_menu Implementation of hook_menu().
fillpdf_merge_pdf Constructs a page from scratch (pdf content-type) and sends it to the browser or saves it, depending on if a custom path is configured or not.
fillpdf_merge_pdf_access Make sure the user has access to data they want to populate the PDF
fillpdf_parse_pdf This function generates the form fields from the specified PDF. It (1) sends a request to the iText servlet to parse the specified PDF, (2) iText returns an XML response with fields-mappings, this module parses the XML response & contsructs the…
fillpdf_parse_uri Get the data and form that need to be merged, from the $_GET, and print the PDF @seealso fillpdf_pdf_link for $_GET params
fillpdf_pdf_link Gets a link to the prinable PDF, merged with the passed-in data
fillpdf_perm Implementation of hook_perm().
_fillpdf_get_file_contents
_fillpdf_process_destination_path
_fillpdf_process_filename
_fillpdf_replacements_to_array
_fillpdf_transform_field_value Apply any field value transformations defined via the UI. Note that the replacement arguments need to already have been run through _fillpdf_replacements_to_array().
_fillpdf_xmlrpc_request

Constants

Namesort descending Description
DEFAULT_SERVLET_URL @file Allows mappings of PDFs to site content