Add Push-Buttons
This demo adds two push-buttons to an existing PDF document.
For this we created a more or less simple class, that extends the Widget annotation class, to represent a push button field.
PHP
<?php use com\setasign\SetaPDF\Demos\Annotation\Widget\Pushbutton; // load and register the autoload function require_once '../../../../../bootstrap.php'; // if we have a post request, just dump the data if (count($_POST) > 0) { $writer = new \SetaPDF_Core_Writer_Http(); $document = new \SetaPDF_Core_Document($writer); $canvas = $document->getCatalog()->getPages()->create('a4')->getCanvas(); $text = new \SetaPDF_Core_Text_Block(\SetaPDF_Core_Font_Standard_Courier::create($document), 12); $text->setText(print_r($_POST, true)); $text->draw($canvas, 0, $canvas->getHeight() - $text->getHeight()); $document->save()->finish(); die(); } // if the demo is executed show a download link if (!isset($_GET['dl'])) { echo '<a href="?dl=1">download</a> PDF and open in a viewer that supports PDF forms.'; die(); } // require the pushbutton class require_once('../../../../../classes/Annotation/Widget/Pushbutton.php'); //$pdfFile = $assetsDirectory . '/pdfs/tektown/Order-Form.pdf'; $pdfFile = $assetsDirectory . '/pdfs/tektown/Subscription-tekMag.pdf'; $writer = new \SetaPDF_Core_Writer_Http('push-buttons.pdf', false); $document = \SetaPDF_Core_Document::loadByFilename($pdfFile, $writer); // let's get the page to which we want to add the button to $pages = $document->getCatalog()->getPages(); $page = $pages->getPage(1); $width = 100; $height = 20; // right top $x = $page->getCropBox()->getUrx() - $width - 5; $y = $page->getCropBox()->getUrY() - $height - 5; // Create a pushbutton instance $pb = new Pushbutton([$x, $y, $x + $width, $y + $height], 'submit btn', $document); $pb->setCaption('Submit'); $pb->setFontSize(12); $pb->setTextColor([0]); $font = \SetaPDF_Core_Font_Standard_Helvetica::create($document); $pb->setFont($font); // Define the border and style $pb->getBorderStyle() ->setWidth(1) ->setStyle(\SetaPDF_Core_Document_Page_Annotation_BorderStyle::BEVELED); // Set some appearance characteristics $pb->getAppearanceCharacteristics(true) ->setBorderColor([.6]) ->setBackgroundColor([.9]); // Create a SubmitForm action $target = 'http' . (isset($_SERVER['HTTPS']) ? 's' : '') . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME']; $action = new \SetaPDF_Core_Document_Action_SubmitForm($target); $action->setFlags( \SetaPDF_Core_Document_Action_SubmitForm::FLAG_EXPORT_FORMAT | /* HTTP POST */ \SetaPDF_Core_Document_Action_SubmitForm::FLAG_INCLUDE_NO_VALUE_FIELDS /* Send also empty fields */ ); // Attach the action to the button $pb->setAction($action); // Let's add the button to the pages annotation and the AcroForm array $acroForm = $document->getCatalog()->getAcroForm(); $fields = $acroForm->getFieldsArray(true); $annotations = $page->getAnnotations(); $fields->push($annotations->add($pb)); // Add a snd button which fills out the form with dummy values // left top $x = $page->getCropBox()->getLlx() + 5; $y = $page->getCropBox()->getUrY() - $height - 5; $pb = new Pushbutton([$x, $y, $x + $width, $y + $height], 'random data btn', $document); $pb->setCaption('Set values'); $pb->setFontSize(12); $pb->setTextColor([0]); $pb->setFont($font); // Define the border and style $pb->getBorderStyle() ->setWidth(1) ->setStyle(\SetaPDF_Core_Document_Page_Annotation_BorderStyle::BEVELED); // Set some appearance characteristics $pb->getAppearanceCharacteristics(true) ->setBorderColor([.6]) ->setBackgroundColor([.9]); // create a JavaScript that fills the fields with random data $javaScript = <<<JS var nFields = this.numFields; var t = app.thermometer; t.duration = nFields; t.begin(); var name, field; for (var i = 0; i < nFields; i++) { name = this.getNthFieldName(i); field = this.getField(name); switch (field.type) { case "text": field.value = name + " " + Math.floor(Math.random() * 10); break; case "checkbox": field.checkThisBox(0); break; case "radiobutton": var values = field.exportValues; field.value = values[Math.floor(Math.random() * (values.length - 1))]; break; } t.value = i; } t.end(); JS; // create the action $action = new \SetaPDF_Core_Document_Action_JavaScript($javaScript); // add it to the button $pb->setAction($action); // add the button to the annotations array and fields array $fields->push($annotations->add($pb)); // send the document to the client $document->save()->finish();
PHP
<?php namespace com\setasign\SetaPDF\Demos\Annotation\Widget; /** * Example class representing a push-button. */ class Pushbutton extends \SetaPDF_Core_Document_Page_Annotation_Widget { /** * @var \SetaPDF_Core_Document */ protected $_document; /** * Creates a new button field in a specific document * * @param array|\SetaPDF_Core_Type_AbstractType|\SetaPDF_Core_Type_Dictionary|\SetaPDF_Core_Type_IndirectObjectInterface $objectOrDictionary * @param $fieldName * @param \SetaPDF_Core_Document $document */ public function __construct($objectOrDictionary, $fieldName, \SetaPDF_Core_Document $document) { $this->_document = $document; parent::__construct($objectOrDictionary); $dict = $this->getDictionary(); $dict['FT'] = new \SetaPDF_Core_Type_Name('Btn'); $this->setFieldFlags(0x010000); // pushbutton -> 17 $acroForm = $document->getCatalog()->getAcroForm(); $acroForm->addDefaultEntriesAndValues(); // Ensure unique field name $fieldNames = []; foreach ($acroForm->getTerminalFieldsObjects() as $terminalObject) { /** @var string $name */ $name = \SetaPDF_Core_Document_Catalog_AcroForm::resolveFieldName($terminalObject->ensure()); $fieldNames[$name] = $name; } $i = 1; $oFieldName = $fieldName; /** @var string $fieldName */ $fieldName = str_replace('.', '_', $fieldName); while (isset($fieldNames[$fieldName])) { $fieldName = $oFieldName . '_' . ($i++); } $dict['T'] = new \SetaPDF_Core_Type_String(\SetaPDF_Core_Encoding::toPdfString($fieldName)); } /** * Set the button caption * * @param $caption * @param string $encoding */ public function setCaption($caption, $encoding = 'UTF-8') { /** @var \SetaPDF_Core_Document_Page_Annotation_AppearanceCharacteristics $appCharacteristics */ $appCharacteristics = $this->getAppearanceCharacteristics(true); $dict = $appCharacteristics->getDictionary(); $dict['CA'] = new \SetaPDF_Core_Type_String(\SetaPDF_Core_Encoding::toPdfString($caption, $encoding)); } /** * Set the font * * @param \SetaPDF_Core_Font_FontInterface $font * @throws \SetaPDF_FormFiller_Field_Exception */ public function setFont(\SetaPDF_Core_Font_FontInterface $font) { $daValues = $this->_getDaValues(); $writer = new \SetaPDF_Core_Writer(); \SetaPDF_Core_Type_Name::writePdfString($writer, $this->_document->getCatalog()->getAcroForm()->addResource($font)); $daValues['fontSize']->writePdfString($writer, $daValues['fontSize']->getValue()); $writer->write(' Tf'); $daValues['color']->draw($writer, false); $this->_annotationDictionary['DA'] = new \SetaPDF_Core_Type_String($writer); } /** * Get the font * * @return \SetaPDF_Core_Font * @throws \SetaPDF_FormFiller_Field_Exception */ public function getFont() { $daValues = $this->_getDaValues(); $fonts = $this->_document->getCatalog()->getAcroForm()->getDefaultResources(true, \SetaPDF_Core_Resource::TYPE_FONT); return \SetaPDF_Core_Font::get($fonts->getValue($daValues['fontName']->getValue())); } /** * Set the font size * * @param int|float $fontSize * @throws \SetaPDF_FormFiller_Field_Exception */ public function setFontSize($fontSize) { $daValues = $this->_getDaValues(); $writer = new \SetaPDF_Core_Writer(); $daValues['fontSize'] = new \SetaPDF_Core_Type_Numeric($fontSize); \SetaPDF_Core_Type_Name::writePdfString($writer, $daValues['fontName']->getValue()); \SetaPDF_Core_Type_Numeric::writePdfString($writer, $daValues['fontSize']->getValue()); $writer->write(' Tf'); $daValues['color']->draw($writer, false); $this->_annotationDictionary['DA'] = new \SetaPDF_Core_Type_String($writer); } /** * Set the text color * * @param int|float|string|array|\SetaPDF_Core_Type_Array|\SetaPDF_Core_DataStructure_Color $color * @throws \SetaPDF_FormFiller_Field_Exception */ public function setTextColor($color) { if (!$color instanceof \SetaPDF_Core_DataStructure_Color) { $color = \SetaPDF_Core_DataStructure_Color::createByComponents($color); } $daValues = $this->_getDaValues(); $writer = new \SetaPDF_Core_Writer(); \SetaPDF_Core_Type_Name::writePdfString($writer, $daValues['fontName']->getValue()); \SetaPDF_Core_Type_Numeric::writePdfString($writer, $daValues['fontSize']->getValue()); $writer->write(' Tf'); $color->draw($writer, false); $this->_annotationDictionary['DA'] = new \SetaPDF_Core_Type_String($writer); } /** * Get default appearance values * * @return array * @throws \SetaPDF_FormFiller_Field_Exception */ protected function _getDaValues() { $da = \SetaPDF_Core_Type_Dictionary_Helper::resolveAttribute($this->_annotationDictionary, 'DA'); $da = $da ? $da : \SetaPDF_Core_Type_Dictionary_Helper::resolveAttribute( $this->_document->getCatalog()->getAcroForm()->getDictionary(), 'DA' ); if (!$da) { throw new \SetaPDF_FormFiller_Field_Exception('No DA key found.'); } $fontName = $fontSize = $color = null; $parser = new \SetaPDF_Core_Parser_Content($da->getValue()); $parser->registerOperator('Tf', static function($params) use (&$fontName, &$fontSize) { $fontName = $params[0]; $fontSize = $params[1]; }); $parser->registerOperator(['g', 'rg', 'k'], static function($params) use (&$color) { $color = \SetaPDF_Core_DataStructure_Color::createByComponents($params); }); $parser->process(); return [ 'fontName' => $fontName, 'fontSize' => $fontSize, 'color' => $color ]; } /** * Creates the appearance of the button */ protected function _createAppearance() { $document = $this->_document; $width = $this->getWidth(); $height = $this->getHeight(); $n = $this->getAppearance('N'); if (!$n) { $n = \SetaPDF_Core_XObject_Form::create($document, [0, 0, $width, $height]); } $this->setAppearance($n); $canvas = $n->getCanvas(); $appearanceCharacteristics = $this->getAppearanceCharacteristics(); $borderStyle = $this->getBorderStyle(); $borderWidth = 0; $_borderStyle = \SetaPDF_Core_Document_Page_Annotation_BorderStyle::SOLID; if ($borderStyle) { $_borderStyle = $borderStyle->getStyle(); $borderWidth = $borderStyle->getWidth(); } if ($borderWidth == 0 && $appearanceCharacteristics && $appearanceCharacteristics->getBorderColor() !== null) { $borderWidth = 1; } // Handle Rotation $rotation = $appearanceCharacteristics ? $appearanceCharacteristics->getRotation() : 0; if ($rotation != 0) { $rotation %= 360; if ($rotation < 0) { $rotation += 360; } $r = \deg2rad($rotation); $a = $d = \cos($r); $b = \sin($r); $c = -$b; $e = 0; $f = 0; if ($a == -1) { $e = $width; $f = $height; } if ($b == 1) { $e = $height; } if ($c == 1) { $f = $width; } $n->getObject()->ensure()->getValue()->offsetSet('Matrix', new \SetaPDF_Core_Type_Array([ new \SetaPDF_Core_Type_Numeric($a), new \SetaPDF_Core_Type_Numeric($b), new \SetaPDF_Core_Type_Numeric($c), new \SetaPDF_Core_Type_Numeric($d), new \SetaPDF_Core_Type_Numeric($e), new \SetaPDF_Core_Type_Numeric($f) ])); } // Draw Background $backgroundColor = $appearanceCharacteristics ? $appearanceCharacteristics->getBackgroundColor() : null; if ($backgroundColor) { $backgroundColor->draw($canvas, false); $canvas->draw()->rect(0, 0, $width, $height, \SetaPDF_Core_Canvas_Draw::STYLE_FILL); } // Draw Border: $borderColor = $appearanceCharacteristics ? $appearanceCharacteristics->getBorderColor() : null; // It is possible to have no border but only a border style! // Beveld or Inset if ($_borderStyle === \SetaPDF_Core_Document_Page_Annotation_BorderStyle::BEVELED || $_borderStyle === \SetaPDF_Core_Document_Page_Annotation_BorderStyle::INSET) { $colorLtValue = 1; //' 1 g'; if ($_borderStyle === \SetaPDF_Core_Document_Page_Annotation_BorderStyle::INSET) { $colorLtValue = .5; // ' 0.5 g'; } /** * This color adjustment is not needed for list boxes. * The effect will only occur if the field is active * All other fields will use this effect. */ if ( $_borderStyle === \SetaPDF_Core_Document_Page_Annotation_BorderStyle::BEVELED && $backgroundColor ) { $tmpColor = clone $backgroundColor; $tmpColor->adjustAllComponents(-0.250977); $colorRb = $tmpColor; } else { $colorRb = new \SetaPDF_Core_DataStructure_Color_Gray(.75); } // Draw the inner border $canvas->saveGraphicState(); // q \SetaPDF_Core_DataStructure_Color_Gray::writePdfString($canvas, $colorLtValue, false); $_borderWidth = $borderWidth * 2; $canvas->path() ->moveTo($x = $_borderWidth / 2, $y = $height-$_borderWidth / 2) ->lineTo($x = $width - $x, $y) ->lineTo($x -= $_borderWidth / 2, $y -= $_borderWidth / 2) ->lineTo($x = $_borderWidth, $y) ->lineTo($x, $y = $_borderWidth) ->lineTo($x /= 2, $y /= 2) ->close() ->fill(); $colorRb->draw($canvas, false); $canvas->path() ->moveTo($x, $y) ->lineTo($x *= 2, $y *= 2) ->lineTo($x = $width - $x, $y) ->lineTo($x, $y += $height - $_borderWidth * 2) ->lineTo($x += $_borderWidth / 2, $y += $_borderWidth / 2) ->lineTo($x, $_borderWidth / 2) ->close() ->fill(); $canvas->restoreGraphicState(); // Q } if ($borderColor) { $canvas->path()->setLineWidth($borderWidth); $borderColor->draw($canvas, true); // Dashed if ($_borderStyle === \SetaPDF_Core_Document_Page_Annotation_BorderStyle::DASHED) { $canvas->path()->setDashPattern($borderStyle->getDashPattern()); } // Draw border // NOT underline if ($_borderStyle !== \SetaPDF_Core_Document_Page_Annotation_BorderStyle::UNDERLINE) { $canvas->draw()->rect( $borderWidth * .5, $borderWidth * .5, $width - $borderWidth, $height - $borderWidth ); // underline } else { $y = $borderWidth / 2; $canvas->draw()->line(0, $y, $width, $y); } } $daValues = $this->_getDaValues(); $font = $this->getFont(); $textBlock = new \SetaPDF_Core_Text_Block($font, null); $borderDoubled = ( $_borderStyle === \SetaPDF_Core_Document_Page_Annotation_BorderStyle::BEVELED || $_borderStyle === \SetaPDF_Core_Document_Page_Annotation_BorderStyle::INSET ); $offset = max(1, $borderWidth * ($borderDoubled ? 2 : 1)) * 2; /** @var \SetaPDF_Core_Document_Page_Annotation_AppearanceCharacteristics $appCharacteristics */ $appCharacteristics = $this->getAppearanceCharacteristics(true); $dict = $appCharacteristics->getDictionary(); $textBlock->setText(\SetaPDF_Core_Encoding::convertPdfString( $dict->getValue('CA')->getValue(), 'UTF-16BE' ), 'UTF-16BE'); $textBlock->setPadding($offset); $fontSize = $daValues['fontSize']->getValue(); if ($fontSize == 0) { $textBlock->setFontSize(12); $textWidthAt12Points = $textBlock->getTextWidth(); $fontSize = ($width - $offset * 2) / $textWidthAt12Points * 12; $textBlock->setFontSize($fontSize); $textHeight = $textBlock->getTextHeight(); if ($textHeight > ($height - $offset * 2)) { // A near value... $fontSize = ($height - $offset * 2) * $fontSize / $textHeight; } } $textBlock->setWidth($width - $offset * 2); $textBlock->setFontSize($fontSize); $textBlock->setTextColor($daValues['color']); $textBlock->setAlign(\SetaPDF_Core_Text::ALIGN_CENTER); $textBlock->draw($canvas, 0, $height / 2 - $textBlock->getHeight() / 2); } /** * @return \SetaPDF_Core_Type_IndirectObjectInterface */ public function getIndirectObject(\SetaPDF_Core_Document $document = null) { $this->_createAppearance(); return parent::getIndirectObject($document); } /** * Sets a field flag * * @param integer $flags * @param boolean|null $add Add = true, remove = false, set = null */ public function setFieldFlags($flags, $add = true) { if ($add === false) { $this->unsetFieldFlags($flags); return; } $dict = \SetaPDF_Core_Type_Dictionary_Helper::resolveDictionaryByAttribute($this->_annotationDictionary, 'Ff'); if ($dict instanceof \SetaPDF_Core_Type_AbstractType) { $value = $dict->ensure()->getValue('Ff'); if ($add === true) { $value->setValue($value->getValue() | $flags); } else { $value->setValue($flags); } } else { $this->_annotationDictionary->offsetSet('Ff', new \SetaPDF_Core_Type_Numeric($flags)); } } /** * Removes a field flag * * @param integer $flags */ public function unsetFieldFlags($flags) { $dict = \SetaPDF_Core_Type_Dictionary_Helper::resolveDictionaryByAttribute($this->_annotationDictionary, 'Ff'); if ($dict instanceof \SetaPDF_Core_Type_AbstractType) { $value = $dict->ensure()->getValue('Ff'); $value->setValue($value->getValue() & ~$flags); } } /** * Returns the current field flags * * @return integer */ public function getFieldFlags() { $fieldFlags = \SetaPDF_Core_Type_Dictionary_Helper::resolveAttribute($this->_annotationDictionary, 'Ff'); if ($fieldFlags) { return $fieldFlags->getValue(); } return 0; } /** * Checks if a specific field flag is set * * @param integer $flag * @return boolean */ public function isFieldFlagSet($flag) { return ($this->getFieldFlags() & $flag) !== 0; } }