|
|
@ -96,6 +96,13 @@ class Zend_Pdf |
|
|
|
const PDF_HEADER = "%PDF-1.4\n%\xE2\xE3\xCF\xD3\n"; |
|
|
|
|
|
|
|
/** |
|
|
|
* Form field options |
|
|
|
*/ |
|
|
|
const PDF_FORM_FIELD_READONLY = 1; |
|
|
|
const PDF_FORM_FIELD_REQUIRED = 2; |
|
|
|
const PDF_FORM_FIELD_NOEXPORT = 4; |
|
|
|
|
|
|
|
/** |
|
|
|
* Pages collection |
|
|
|
* |
|
|
|
* @todo implement it as a class, which supports ArrayAccess and Iterator interfaces, |
|
|
@ -199,7 +206,6 @@ class Zend_Pdf |
|
|
|
*/ |
|
|
|
protected $_parser; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* List of inheritable attributesfor pages tree |
|
|
|
* |
|
|
@ -208,6 +214,13 @@ class Zend_Pdf |
|
|
|
protected static $_inheritableAttributes = array('Resources', 'MediaBox', 'CropBox', 'Rotate'); |
|
|
|
|
|
|
|
/** |
|
|
|
* List of form fields |
|
|
|
* |
|
|
|
* @var array - Associative array, key: name of form field, value: Zend_Pdf_Element |
|
|
|
*/ |
|
|
|
protected $_formFields = array(); |
|
|
|
|
|
|
|
/** |
|
|
|
* True if the object is a newly created PDF document (affects save() method behavior) |
|
|
|
* False otherwise |
|
|
|
* |
|
|
@ -328,6 +341,8 @@ class Zend_Pdf |
|
|
|
|
|
|
|
$this->_loadNamedDestinations($this->_trailer->Root, $this->_parser->getPDFVersion()); |
|
|
|
$this->_loadOutlines($this->_trailer->Root); |
|
|
|
$this->_loadJavaScript($this->_trailer->Root); |
|
|
|
$this->_loadFormFields($this->_trailer->Root); |
|
|
|
|
|
|
|
if ($this->_trailer->Info !== null) { |
|
|
|
$this->properties = $this->_trailer->Info->toPhp(); |
|
|
@ -575,6 +590,130 @@ class Zend_Pdf |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Load JavaScript |
|
|
|
* |
|
|
|
* Populates the _javaScript string, for later use of getJavaScript method. |
|
|
|
* |
|
|
|
* @param Zend_Pdf_Element_Reference $root Document catalog entry |
|
|
|
*/ |
|
|
|
protected function _loadJavaScript(Zend_Pdf_Element_Reference $root) |
|
|
|
{ |
|
|
|
if (null === $root->Names || null === $root->Names->JavaScript |
|
|
|
|| null === $root->Names->JavaScript->Names |
|
|
|
) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
foreach ($root->Names->JavaScript->Names->items as $item) { |
|
|
|
if ($item instanceof Zend_Pdf_Element_Reference |
|
|
|
&& $item->S->value === 'JavaScript' |
|
|
|
) { |
|
|
|
$this->_javaScript[] = $item->JS->value; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Load form fields |
|
|
|
* |
|
|
|
* Populates the _formFields array, for later lookup of fields by name |
|
|
|
* |
|
|
|
* @param Zend_Pdf_Element_Reference $root Document catalog entry |
|
|
|
*/ |
|
|
|
protected function _loadFormFields(Zend_Pdf_Element_Reference $root) |
|
|
|
{ |
|
|
|
if ($root->AcroForm === null || $root->AcroForm->Fields === null) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
foreach ($root->AcroForm->Fields->items as $field) { |
|
|
|
/* We only support fields that are textfields and have a name */ |
|
|
|
if ($field->FT && $field->FT->value == 'Tx' && $field->T |
|
|
|
&& $field->T !== null |
|
|
|
) { |
|
|
|
$this->_formFields[$field->T->value] = $field; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (!$root->AcroForm->NeedAppearances |
|
|
|
|| !$root->AcroForm->NeedAppearances->value |
|
|
|
) { |
|
|
|
/* Ask the .pdf viewer to generate its own appearance data, so we do not have to */ |
|
|
|
$root->AcroForm->add( |
|
|
|
new Zend_Pdf_Element_Name('NeedAppearances'), |
|
|
|
new Zend_Pdf_Element_Boolean(true) |
|
|
|
); |
|
|
|
$root->AcroForm->touch(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Retrieves a list with the names of the AcroForm textfields in the PDF |
|
|
|
* |
|
|
|
* @return array of strings |
|
|
|
*/ |
|
|
|
public function getTextFieldNames() |
|
|
|
{ |
|
|
|
return array_keys($this->_formFields); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Sets the value of an AcroForm text field |
|
|
|
* |
|
|
|
* @param string $name Name of textfield |
|
|
|
* @param string $value Value |
|
|
|
* @throws Zend_Pdf_Exception if the textfield does not exist in the pdf |
|
|
|
*/ |
|
|
|
public function setTextField($name, $value) |
|
|
|
{ |
|
|
|
if (!isset($this->_formFields[$name])) { |
|
|
|
throw new Zend_Pdf_Exception( |
|
|
|
"Field '$name' does not exist or is not a textfield" |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
/** @var Zend_Pdf_Element $field */ |
|
|
|
$field = $this->_formFields[$name]; |
|
|
|
$field->add( |
|
|
|
new Zend_Pdf_Element_Name('V'), new Zend_Pdf_Element_String($value) |
|
|
|
); |
|
|
|
$field->touch(); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Sets the properties for an AcroForm text field |
|
|
|
* |
|
|
|
* @param string $name |
|
|
|
* @param mixed $bitmask |
|
|
|
* @throws Zend_Pdf_Exception |
|
|
|
*/ |
|
|
|
public function setTextFieldProperties($name, $bitmask) |
|
|
|
{ |
|
|
|
if (!isset($this->_formFields[$name])) { |
|
|
|
throw new Zend_Pdf_Exception( |
|
|
|
"Field '$name' does not exist or is not a textfield" |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
$field = $this->_formFields[$name]; |
|
|
|
$field->add( |
|
|
|
new Zend_Pdf_Element_Name('Ff'), |
|
|
|
new Zend_Pdf_Element_Numeric($bitmask) |
|
|
|
); |
|
|
|
$field->touch(); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Marks an AcroForm text field as read only |
|
|
|
* |
|
|
|
* @param string $name |
|
|
|
*/ |
|
|
|
public function markTextFieldAsReadOnly($name) |
|
|
|
{ |
|
|
|
$this->setTextFieldProperties($name, self::PDF_FORM_FIELD_READONLY); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Orginize pages to tha pages tree structure. |
|
|
|
* |
|
|
|
* @todo atomatically attach page to the document, if it's not done yet. |
|
|
@ -1387,17 +1526,84 @@ class Zend_Pdf |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Sets the document-level JavaScript |
|
|
|
* |
|
|
|
* Resets and appends |
|
|
|
* |
|
|
|
* @param string|array $javaScript |
|
|
|
*/ |
|
|
|
public function setJavaScript($javaScript) |
|
|
|
{ |
|
|
|
$this->resetJavaScript(); |
|
|
|
|
|
|
|
$this->addJavaScript($javaScript); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Resets the document-level JavaScript |
|
|
|
*/ |
|
|
|
public function resetJavaScript() |
|
|
|
{ |
|
|
|
$this->_javaScript = null; |
|
|
|
|
|
|
|
$root = $this->_trailer->Root; |
|
|
|
if (null === $root->Names || null === $root->Names->JavaScript) { |
|
|
|
return; |
|
|
|
} |
|
|
|
$root->Names->JavaScript = null; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Set the document-level JavaScript |
|
|
|
* Appends JavaScript to the document-level JavaScript |
|
|
|
* |
|
|
|
* @param string $javascript |
|
|
|
* @param string|array $javaScript |
|
|
|
* @throws Zend_Pdf_Exception |
|
|
|
*/ |
|
|
|
public function setJavaScript($javascript) |
|
|
|
public function addJavaScript($javaScript) |
|
|
|
{ |
|
|
|
$this->_javaScript = $javascript; |
|
|
|
if (empty($javaScript)) { |
|
|
|
throw new Zend_Pdf_Exception( |
|
|
|
'JavaScript must be a non empty string or array of strings' |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
if (!is_array($javaScript)) { |
|
|
|
$javaScript = array($javaScript); |
|
|
|
} |
|
|
|
|
|
|
|
if (null === $this->_javaScript) { |
|
|
|
$this->_javaScript = $javaScript; |
|
|
|
} else { |
|
|
|
$this->_javaScript = array_merge($this->_javaScript, $javaScript); |
|
|
|
} |
|
|
|
|
|
|
|
if (!empty($this->_javaScript)) { |
|
|
|
$items = array(); |
|
|
|
|
|
|
|
foreach ($this->_javaScript as $javaScript) { |
|
|
|
$jsCode = array( |
|
|
|
'S' => new Zend_Pdf_Element_Name('JavaScript'), |
|
|
|
'JS' => new Zend_Pdf_Element_String($javaScript) |
|
|
|
); |
|
|
|
$items[] = new Zend_Pdf_Element_String('EmbeddedJS'); |
|
|
|
$items[] = $this->_objFactory->newObject( |
|
|
|
new Zend_Pdf_Element_Dictionary($jsCode) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
$jsRef = $this->_objFactory->newObject( |
|
|
|
new Zend_Pdf_Element_Dictionary( |
|
|
|
array('Names' => new Zend_Pdf_Element_Array($items)) |
|
|
|
) |
|
|
|
); |
|
|
|
|
|
|
|
if (null === $this->_trailer->Root->Names) { |
|
|
|
$this->_trailer->Root->Names = new Zend_Pdf_Element_Dictionary(); |
|
|
|
} |
|
|
|
$this->_trailer->Root->Names->JavaScript = $jsRef; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Convert date to PDF format (it's close to ASN.1 (Abstract Syntax Notation |
|
|
|