* @copyright 2011-2026 Nicola Asuni - Tecnick.com LTD * @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE) * @link https://github.com/tecnickcom/tc-lib-pdf-page * * This file is part of tc-lib-pdf-page software library. */ namespace Com\Tecnick\Pdf\Page; use Com\Tecnick\Color\Pdf as Color; use Com\Tecnick\Pdf\Page\Exception as PageException; /** * Com\Tecnick\Pdf\Page\Box * * @since 2011-05-23 * @category Library * @package PdfPage * @author Nicola Asuni * @copyright 2011-2026 Nicola Asuni - Tecnick.com LTD * @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE) * @link https://github.com/tecnickcom/tc-lib-pdf-page * * @phpstan-type PageBci array{ * 'color': string, * 'width': float, * 'style': string, * 'dash': array, * } * * @phpstan-type PageBox array{ * 'llx': float, * 'lly': float, * 'urx': float, * 'ury': float, * 'bci'?: PageBci, * } * * @phpstan-type MarginData array{ * 'CB': float, * 'CT': float, * 'FT': float, * 'HB': float, * 'PB': float, * 'PL': float, * 'PR': float, * 'PT': float, * } * * @phpstan-type RegionData array{ * 'RB': float, * 'RH': float, * 'RL': float, * 'RR': float, * 'RT': float, * 'RW': float, * 'RX': float, * 'RY': float, * 'x' : float, * 'y' : float, * } * * @phpstan-type NoWriteArea array{ * 'side'?: string, * 'xt'?: float, * 'yt'?: float, * 'xb'?: float, * 'yb'?: float, * 'x'?: float, * 'y'?: float, * 'w'?: float, * 'h'?: float, * } * * * @phpstan-type TransitionData array{ * 'B': bool, * 'D': int, * 'Di': string|int, * 'Dm': string, * 'Dur': float, * 'M': string, * 'S': string, * 'SS': float, * } * * @phpstan-type PageData array{ * 'annotrefs': array, * 'autobreak': bool, * 'box': array, * 'columns': int, * 'content': array, * 'content_mark': array, * 'ContentHeight': float, * 'ContentWidth': float, * 'FooterHeight': float, * 'HeaderHeight': float, * 'currentRegion': int, * 'format': string, * 'group': int, * 'height': float, * 'margin': MarginData, * 'n': int, * 'num': int, * 'orientation': string, * 'pagenum': int, * 'pheight': float, * 'pid': int, * 'pwidth': float, * 'region': array, * 'rotation': int, * 'time': int, * 'transition': TransitionData, * 'width': float, * 'zoom': float, * } * * @phpstan-type PageInputData array{ * 'annotrefs'?: array, * 'autobreak'?: bool, * 'box'?: array, * }, * }>, * 'columns'?: int, * 'content'?: array|string, * 'content_mark'?: array, * 'ContentHeight'?: float, * 'ContentWidth'?: float, * 'FooterHeight'?: float, * 'HeaderHeight'?: float, * 'currentRegion'?: int, * 'format'?: string, * 'group'?: int, * 'height'?: float, * 'margin'?: array{ * 'booklet'?: bool, * 'CB'?: float, * 'CT'?: float, * 'FT'?: float, * 'HB'?: float, * 'PB'?: float, * 'PL'?: float, * 'PR'?: float, * 'PT'?: float, * }, * 'n'?: int, * 'num'?: int, * 'orientation'?: string, * 'pagenum'?: int, * 'pheight'?: float, * 'pid'?: int, * 'pwidth'?: float, * 'region'?: array, * 'rotation'?: int, * 'time'?: int, * 'transition'?: array{ * 'B'?: bool, * 'D'?: int, * 'Di'?: string|int, * 'Dm'?: string, * 'Dur'?: float, * 'M'?: string, * 'S'?: string, * 'SS'?: float, * }, * 'width'?: float, * 'zoom'?: float, * } */ abstract class Box extends \Com\Tecnick\Pdf\Page\Mode { /** * Unit of measure conversion ratio. */ protected float $kunit = 1.0; /** * Color object. */ protected Color $col; /** * Page box names. * * @var array */ public const BOX = [ 'MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox', ]; /** * Swap X and Y coordinates of page boxes (change page boxes orientation). * * @param array $dims Array of page dimensions. * * @return array Page dimensions. */ public function swapCoordinates(array $dims): array { foreach (self::BOX as $type) { // swap X and Y coordinates if (!array_key_exists($type, $dims)) { continue; } $llx = $dims[$type]['llx']; $lly = $dims[$type]['lly']; $urx = $dims[$type]['urx']; $ury = $dims[$type]['ury']; $dims[$type]['llx'] = $lly; $dims[$type]['lly'] = $llx; $dims[$type]['urx'] = $ury; $dims[$type]['ury'] = $urx; } return $dims; } /** * Set page boundaries. * * @param array $dims Array of page dimensions to modify. * @param string|PageBoxType $type Box type: MediaBox, CropBox, BleedBox, TrimBox, ArtBox, or enum case. * @param float $llx Lower-left x coordinate in user units. * @param float $lly Lower-left y coordinate in user units. * @param float $urx Upper-right x coordinate in user units. * @param float $ury Upper-right y coordinate in user units. * @param PageBci $bci BoxColorInfo: guideline style (color, width, style, dash). * * @return array Page dimensions. * * @throws PageException */ public function setBox( array $dims, string|PageBoxType $type, float $llx, float $lly, float $urx, float $ury, ?array $bci = null, ): array { if ($type instanceof PageBoxType) { $type = $type->value; } if (!\in_array($type, self::BOX, true)) { throw new PageException('unknown page box type: ' . $type); } $dims[$type]['llx'] = $llx; $dims[$type]['lly'] = $lly; $dims[$type]['urx'] = $urx; $dims[$type]['ury'] = $ury; if ($bci === null) { // set default values $bci = [ 'color' => '#000000', 'width' => 1.0 / $this->kunit, 'style' => 'S', // S = solid; D = dash 'dash' => [3], ]; } $dims[$type]['bci'] = $bci; return $dims; } /** * Initialize page boxes. * * @param float $width Page width in points. * @param float $height Page height in points. * * @return array Page boxes. * * @throws PageException */ public function setPageBoxes(float $width, float $height): array { $dims = []; foreach (self::BOX as $type) { $dims = $this->setBox($dims, $type, 0, 0, $width, $height); } return $dims; } /** * Returns the PDF command to output the specified page boxes. * * @param array $dims Array of page dimensions. */ protected function getBox(array $dims): string { $out = ''; foreach (self::BOX as $box) { if (empty($dims[$box])) { // @codeCoverageIgnoreStart continue; // @codeCoverageIgnoreEnd } $out .= \sprintf( '/%s [%F %F %F %F]' . "\n", $box, $dims[$box]['llx'], $dims[$box]['lly'], $dims[$box]['urx'], $dims[$box]['ury'], ); } return $out; } /** * Returns the PDF command to output the specified page BoxColorInfo. * * @param array $dims Array of page dimensions. * * @SuppressWarnings("PHPMD.CyclomaticComplexity") */ protected function getBoxColorInfo(array $dims): string { $out = '/BoxColorInfo <<' . "\n"; foreach (self::BOX as $box) { $out .= '/' . $box . ' <<' . "\n"; if (!empty($dims[$box]['bci']['color'])) { $out .= '/C [' . $this->col->getPdfRgbComponents($dims[$box]['bci']['color']) . ']' . "\n"; } if (!empty($dims[$box]['bci']['width'])) { $out .= \sprintf('/W %F' . "\n", $dims[$box]['bci']['width'] * $this->kunit); } if (!empty($dims[$box]['bci']['style'])) { $mode = \strtoupper($dims[$box]['bci']['style'][0]); if ($mode !== 'D') { $mode = 'S'; } $out .= '/S /' . $mode . "\n"; } if (!empty($dims[$box]['bci']['dash'])) { $out .= '/D ['; foreach ($dims[$box]['bci']['dash'] as $dash) { $out .= \sprintf(' %F', (float) $dash * $this->kunit); } $out .= ' ]' . "\n"; } $out .= '>>' . "\n"; } return $out . ('>>' . "\n"); } }