vendor/setasign/fpdi/src/FpdfTplTrait.php line 398

Open in your IDE?
  1. <?php
  2. /**
  3.  * This file is part of FPDI
  4.  *
  5.  * @package   setasign\Fpdi
  6.  * @copyright Copyright (c) 2024 Setasign GmbH & Co. KG (https://www.setasign.com)
  7.  * @license   http://opensource.org/licenses/mit-license The MIT License
  8.  */
  9. namespace setasign\Fpdi;
  10. /**
  11.  * Trait FpdfTplTrait
  12.  *
  13.  * This trait adds a templating feature to FPDF and tFPDF.
  14.  */
  15. trait FpdfTplTrait
  16. {
  17.     /**
  18.      * Data of all created templates.
  19.      *
  20.      * @var array
  21.      */
  22.     protected $templates = [];
  23.     /**
  24.      * The template id for the currently created template.
  25.      *
  26.      * @var null|int
  27.      */
  28.     protected $currentTemplateId;
  29.     /**
  30.      * A counter for template ids.
  31.      *
  32.      * @var int
  33.      */
  34.     protected $templateId 0;
  35.     /**
  36.      * Set the page format of the current page.
  37.      *
  38.      * @param array $size An array with two values defining the size.
  39.      * @param string $orientation "L" for landscape, "P" for portrait.
  40.      * @throws \BadMethodCallException
  41.      */
  42.     public function setPageFormat($size$orientation)
  43.     {
  44.         if ($this->currentTemplateId !== null) {
  45.             throw new \BadMethodCallException('The page format cannot be changed when writing to a template.');
  46.         }
  47.         if (!\in_array($orientation, ['P''L'], true)) {
  48.             throw new \InvalidArgumentException(\sprintf(
  49.                 'Invalid page orientation "%s"! Only "P" and "L" are allowed!',
  50.                 $orientation
  51.             ));
  52.         }
  53.         $size $this->_getpagesize($size);
  54.         if (
  55.             $orientation != $this->CurOrientation
  56.             || $size[0] != $this->CurPageSize[0]
  57.             || $size[1] != $this->CurPageSize[1]
  58.         ) {
  59.             // New size or orientation
  60.             if ($orientation === 'P') {
  61.                 $this->$size[0];
  62.                 $this->$size[1];
  63.             } else {
  64.                 $this->$size[1];
  65.                 $this->$size[0];
  66.             }
  67.             $this->wPt $this->$this->k;
  68.             $this->hPt $this->$this->k;
  69.             $this->PageBreakTrigger $this->$this->bMargin;
  70.             $this->CurOrientation $orientation;
  71.             $this->CurPageSize $size;
  72.             $this->PageInfo[$this->page]['size'] = array($this->wPt$this->hPt);
  73.         }
  74.     }
  75.     /**
  76.      * Draws a template onto the page or another template.
  77.      *
  78.      * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
  79.      * aspect ratio.
  80.      *
  81.      * @param mixed $tpl The template id
  82.      * @param array|float|int $x The abscissa of upper-left corner. Alternatively you could use an assoc array
  83.      *                           with the keys "x", "y", "width", "height", "adjustPageSize".
  84.      * @param float|int $y The ordinate of upper-left corner.
  85.      * @param float|int|null $width The width.
  86.      * @param float|int|null $height The height.
  87.      * @param bool $adjustPageSize
  88.      * @return array The size
  89.      * @see FpdfTplTrait::getTemplateSize()
  90.      */
  91.     public function useTemplate($tpl$x 0$y 0$width null$height null$adjustPageSize false)
  92.     {
  93.         if (!isset($this->templates[$tpl])) {
  94.             throw new \InvalidArgumentException('Template does not exist!');
  95.         }
  96.         if (\is_array($x)) {
  97.             unset($x['tpl']);
  98.             \extract($xEXTR_IF_EXISTS);
  99.             /** @noinspection NotOptimalIfConditionsInspection */
  100.             /** @phpstan-ignore function.alreadyNarrowedType  */
  101.             if (\is_array($x)) {
  102.                 $x 0;
  103.             }
  104.         }
  105.         $template $this->templates[$tpl];
  106.         $originalSize $this->getTemplateSize($tpl);
  107.         $newSize $this->getTemplateSize($tpl$width$height);
  108.         if ($adjustPageSize) {
  109.             $this->setPageFormat($newSize$newSize['orientation']);
  110.         }
  111.         $this->_out(
  112.         // reset standard values, translate and scale
  113.             \sprintf(
  114.                 'q 0 J 1 w 0 j 0 G 0 g %.4F 0 0 %.4F %.4F %.4F cm /%s Do Q',
  115.                 ($newSize['width'] / $originalSize['width']),
  116.                 ($newSize['height'] / $originalSize['height']),
  117.                 $x $this->k,
  118.                 ($this->$y $newSize['height']) * $this->k,
  119.                 $template['id']
  120.             )
  121.         );
  122.         return $newSize;
  123.     }
  124.     /**
  125.      * Get the size of a template.
  126.      *
  127.      * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
  128.      * aspect ratio.
  129.      *
  130.      * @param mixed $tpl The template id
  131.      * @param float|int|null $width The width.
  132.      * @param float|int|null $height The height.
  133.      * @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P)
  134.      */
  135.     public function getTemplateSize($tpl$width null$height null)
  136.     {
  137.         if (!isset($this->templates[$tpl])) {
  138.             return false;
  139.         }
  140.         if ($width === null && $height === null) {
  141.             $width $this->templates[$tpl]['width'];
  142.             $height $this->templates[$tpl]['height'];
  143.         } elseif ($width === null) {
  144.             $width $height $this->templates[$tpl]['width'] / $this->templates[$tpl]['height'];
  145.         }
  146.         if ($height === null) {
  147.             $height $width $this->templates[$tpl]['height'] / $this->templates[$tpl]['width'];
  148.         }
  149.         if ($height <= 0. || $width <= 0.) {
  150.             throw new \InvalidArgumentException('Width or height parameter needs to be larger than zero.');
  151.         }
  152.         return [
  153.             'width' => $width,
  154.             'height' => $height,
  155.             => $width,
  156.             => $height,
  157.             'orientation' => $width $height 'L' 'P'
  158.         ];
  159.     }
  160.     /**
  161.      * Begins a new template.
  162.      *
  163.      * @param float|int|null $width The width of the template. If null, the current page width is used.
  164.      * @param float|int|null $height The height of the template. If null, the current page height is used.
  165.      * @param bool $groupXObject Define the form XObject as a group XObject to support transparency (if used).
  166.      * @return int A template identifier.
  167.      */
  168.     public function beginTemplate($width null$height null$groupXObject false)
  169.     {
  170.         if ($width === null) {
  171.             $width $this->w;
  172.         }
  173.         if ($height === null) {
  174.             $height $this->h;
  175.         }
  176.         $templateId $this->getNextTemplateId();
  177.         // initiate buffer with current state of FPDF
  178.         $buffer "2 J\n"
  179.             \sprintf('%.2F w'$this->LineWidth $this->k) . "\n";
  180.         if ($this->FontFamily) {
  181.             $buffer .= \sprintf("BT /F%d %.2F Tf ET\n"$this->CurrentFont['i'], $this->FontSizePt);
  182.         }
  183.         if ($this->DrawColor !== '0 G') {
  184.             $buffer .= $this->DrawColor "\n";
  185.         }
  186.         if ($this->FillColor !== '0 g') {
  187.             $buffer .= $this->FillColor "\n";
  188.         }
  189.         if ($groupXObject && \version_compare('1.4'$this->PDFVersion'>')) {
  190.             $this->PDFVersion '1.4';
  191.         }
  192.         $this->templates[$templateId] = [
  193.             'objectNumber' => null,
  194.             'id' => 'TPL' $templateId,
  195.             'buffer' => $buffer,
  196.             'width' => $width,
  197.             'height' => $height,
  198.             'groupXObject' => $groupXObject,
  199.             'state' => [
  200.                 'x' => $this->x,
  201.                 'y' => $this->y,
  202.                 'AutoPageBreak' => $this->AutoPageBreak,
  203.                 'bMargin' => $this->bMargin,
  204.                 'tMargin' => $this->tMargin,
  205.                 'lMargin' => $this->lMargin,
  206.                 'rMargin' => $this->rMargin,
  207.                 'h' => $this->h,
  208.                 'hPt' => $this->hPt,
  209.                 'w' => $this->w,
  210.                 'wPt' => $this->wPt,
  211.                 'FontFamily' => $this->FontFamily,
  212.                 'FontStyle' => $this->FontStyle,
  213.                 'FontSizePt' => $this->FontSizePt,
  214.                 'FontSize' => $this->FontSize,
  215.                 'underline' => $this->underline,
  216.                 'TextColor' => $this->TextColor,
  217.                 'DrawColor' => $this->DrawColor,
  218.                 'FillColor' => $this->FillColor,
  219.                 'ColorFlag' => $this->ColorFlag
  220.             ]
  221.         ];
  222.         $this->SetAutoPageBreak(false);
  223.         $this->currentTemplateId $templateId;
  224.         $this->$height;
  225.         $this->hPt $height $this->k;
  226.         $this->$width;
  227.         $this->wPt $width $this->k;
  228.         $this->SetXY($this->lMargin$this->tMargin);
  229.         $this->SetRightMargin($this->$width $this->rMargin);
  230.         return $templateId;
  231.     }
  232.     /**
  233.      * Ends a template.
  234.      *
  235.      * @return bool|int|null A template identifier.
  236.      */
  237.     public function endTemplate()
  238.     {
  239.         if ($this->currentTemplateId === null) {
  240.             return false;
  241.         }
  242.         $templateId $this->currentTemplateId;
  243.         $template $this->templates[$templateId];
  244.         $state $template['state'];
  245.         $this->SetXY($state['x'], $state['y']);
  246.         $this->tMargin $state['tMargin'];
  247.         $this->lMargin $state['lMargin'];
  248.         $this->rMargin $state['rMargin'];
  249.         $this->$state['h'];
  250.         $this->hPt $state['hPt'];
  251.         $this->$state['w'];
  252.         $this->wPt $state['wPt'];
  253.         $this->SetAutoPageBreak($state['AutoPageBreak'], $state['bMargin']);
  254.         $this->FontFamily $state['FontFamily'];
  255.         $this->FontStyle $state['FontStyle'];
  256.         $this->FontSizePt $state['FontSizePt'];
  257.         $this->FontSize $state['FontSize'];
  258.         $this->TextColor $state['TextColor'];
  259.         $this->DrawColor $state['DrawColor'];
  260.         $this->FillColor $state['FillColor'];
  261.         $this->ColorFlag $state['ColorFlag'];
  262.         $this->underline $state['underline'];
  263.         $fontKey $this->FontFamily $this->FontStyle;
  264.         if ($fontKey) {
  265.             $this->CurrentFont =& $this->fonts[$fontKey];
  266.         } else {
  267.             unset($this->CurrentFont);
  268.         }
  269.         $this->currentTemplateId null;
  270.         return $templateId;
  271.     }
  272.     /**
  273.      * Get the next template id.
  274.      *
  275.      * @return int
  276.      */
  277.     protected function getNextTemplateId()
  278.     {
  279.         return $this->templateId++;
  280.     }
  281.     /* overwritten FPDF methods: */
  282.     /**
  283.      * @inheritdoc
  284.      */
  285.     public function AddPage($orientation ''$size ''$rotation 0)
  286.     {
  287.         if ($this->currentTemplateId !== null) {
  288.             throw new \BadMethodCallException('Pages cannot be added when writing to a template.');
  289.         }
  290.         parent::AddPage($orientation$size$rotation);
  291.     }
  292.     /**
  293.      * @inheritdoc
  294.      */
  295.     public function Link($x$y$w$h$link)
  296.     {
  297.         if ($this->currentTemplateId !== null) {
  298.             throw new \BadMethodCallException('Links cannot be set when writing to a template.');
  299.         }
  300.         parent::Link($x$y$w$h$link);
  301.     }
  302.     /**
  303.      * @inheritdoc
  304.      */
  305.     public function SetLink($link$y 0$page = -1)
  306.     {
  307.         if ($this->currentTemplateId !== null) {
  308.             throw new \BadMethodCallException('Links cannot be set when writing to a template.');
  309.         }
  310.         return parent::SetLink($link$y$page);
  311.     }
  312.     /**
  313.      * @inheritdoc
  314.      */
  315.     public function SetDrawColor($r$g null$b null)
  316.     {
  317.         parent::SetDrawColor($r$g$b);
  318.         if ($this->page === && $this->currentTemplateId !== null) {
  319.             $this->_out($this->DrawColor);
  320.         }
  321.     }
  322.     /**
  323.      * @inheritdoc
  324.      */
  325.     public function SetFillColor($r$g null$b null)
  326.     {
  327.         parent::SetFillColor($r$g$b);
  328.         if ($this->page === && $this->currentTemplateId !== null) {
  329.             $this->_out($this->FillColor);
  330.         }
  331.     }
  332.     /**
  333.      * @inheritdoc
  334.      */
  335.     public function SetLineWidth($width)
  336.     {
  337.         parent::SetLineWidth($width);
  338.         if ($this->page === && $this->currentTemplateId !== null) {
  339.             $this->_out(\sprintf('%.2F w'$width $this->k));
  340.         }
  341.     }
  342.     /**
  343.      * @inheritdoc
  344.      */
  345.     public function SetFont($family$style ''$size 0)
  346.     {
  347.         parent::SetFont($family$style$size);
  348.         if ($this->page === && $this->currentTemplateId !== null) {
  349.             $this->_out(\sprintf('BT /F%d %.2F Tf ET'$this->CurrentFont['i'], $this->FontSizePt));
  350.         }
  351.     }
  352.     /**
  353.      * @inheritdoc
  354.      */
  355.     public function SetFontSize($size)
  356.     {
  357.         parent::SetFontSize($size);
  358.         if ($this->page === && $this->currentTemplateId !== null) {
  359.             $this->_out(sprintf('BT /F%d %.2F Tf ET'$this->CurrentFont['i'], $this->FontSizePt));
  360.         }
  361.     }
  362.     protected function _putimages()
  363.     {
  364.         parent::_putimages();
  365.         foreach ($this->templates as $key => $template) {
  366.             $this->_newobj();
  367.             $this->templates[$key]['objectNumber'] = $this->n;
  368.             $this->_put('<</Type /XObject /Subtype /Form /FormType 1');
  369.             $this->_put(\sprintf(
  370.                 '/BBox[0 0 %.2F %.2F]',
  371.                 $template['width'] * $this->k,
  372.                 $template['height'] * $this->k
  373.             ));
  374.             $this->_put('/Resources 2 0 R'); // default resources dictionary of FPDF
  375.             if ($this->compress) {
  376.                 $buffer \gzcompress($template['buffer']);
  377.                 $this->_put('/Filter/FlateDecode');
  378.             } else {
  379.                 $buffer $template['buffer'];
  380.             }
  381.             $this->_put('/Length ' \strlen($buffer));
  382.             if ($template['groupXObject']) {
  383.                 $this->_put('/Group <</Type/Group/S/Transparency>>');
  384.             }
  385.             $this->_put('>>');
  386.             $this->_putstream($buffer);
  387.             $this->_put('endobj');
  388.         }
  389.     }
  390.     /**
  391.      * @inheritdoc
  392.      */
  393.     protected function _putxobjectdict()
  394.     {
  395.         foreach ($this->templates as $key => $template) {
  396.             $this->_put('/' $template['id'] . ' ' $template['objectNumber'] . ' 0 R');
  397.         }
  398.         parent::_putxobjectdict();
  399.     }
  400.     /**
  401.      * @inheritdoc
  402.      */
  403.     public function _out($s)
  404.     {
  405.         if ($this->currentTemplateId !== null) {
  406.             $this->templates[$this->currentTemplateId]['buffer'] .= $s "\n";
  407.         } else {
  408.             parent::_out($s);
  409.         }
  410.     }
  411. }