Feature: Labelprint für Kistenetiketten hinzugefügt

This commit is contained in:
2025-10-27 12:14:44 +01:00
parent 43bc416554
commit 14bae6c9ef
1068 changed files with 229014 additions and 1807 deletions
+789
View File
@@ -0,0 +1,789 @@
<?php
/**
* Base.php
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
namespace Com\Tecnick\Pdf;
use Com\Tecnick\Pdf\Exception as PdfException;
use Com\Tecnick\Barcode\Barcode as ObjBarcode;
use Com\Tecnick\Color\Pdf as ObjColor;
use Com\Tecnick\File\Cache as ObjCache;
use Com\Tecnick\File\File as ObjFile;
use Com\Tecnick\Pdf\Encrypt\Encrypt as ObjEncrypt;
use Com\Tecnick\Pdf\Font\Stack as ObjFont;
use Com\Tecnick\Pdf\Graph\Draw as ObjGraph;
use Com\Tecnick\Pdf\Image\Import as ObjImage;
use Com\Tecnick\Pdf\Page\Page as ObjPage;
use Com\Tecnick\Unicode\Convert as ObjUniConvert;
/**
* Com\Tecnick\Pdf\Base
*
* Output PDF data
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* @phpstan-import-type PageInputData from \Com\Tecnick\Pdf\Page\Box
* @phpstan-import-type PageData from \Com\Tecnick\Pdf\Page\Box
* @phpstan-import-type TFontMetric from \Com\Tecnick\Pdf\Font\Stack
*
* @phpstan-type TViewerPref array{
* 'HideToolbar'?: bool,
* 'HideMenubar'?: bool,
* 'HideWindowUI'?: bool,
* 'FitWindow'?: bool,
* 'CenterWindow'?: bool,
* 'DisplayDocTitle'?: bool,
* 'NonFullScreenPageMode'?: string,
* 'Direction'?: string,
* 'ViewArea'?: string,
* 'ViewClip'?: string,
* 'PrintArea'?: string,
* 'PrintClip'?: string,
* 'PrintScaling'?: string,
* 'Duplex'?: string,
* 'PickTrayByPDFSize'?: bool,
* 'PrintPageRange'?: array<int>,
* 'NumCopies'?: int,
* }
*
* @phpstan-type TBBox array{
* 'x': float,
* 'y': float,
* 'w': float,
* 'h': float,
* }
*
* @phpstan-type TCellDef array{
* 'margin': array{
* 'T': float,
* 'R': float,
* 'B': float,
* 'L': float,
* },
* 'padding': array{
* 'T': float,
* 'R': float,
* 'B': float,
* 'L': float,
* },
* 'borderpos': float,
* }
*
* @phpstan-type TRefUnitValues array{
* 'parent': float,
* 'font': array{
* 'rootsize': float,
* 'size': float,
* 'xheight': float,
* 'zerowidth': float,
* },
* 'viewport': array{
* 'width': float,
* 'height': float,
* },
* 'page': array{
* 'width': float,
* 'height': float,
* },
* }
*
* @phpstan-type TCustomXMP array{
* 'x:xmpmeta': string,
* 'x:xmpmeta.rdf:RDF': string,
* 'x:xmpmeta.rdf:RDF.rdf:Description': string,
* 'x:xmpmeta.rdf:RDF.rdf:Description.pdfaExtension:schemas': string,
* 'x:xmpmeta.rdf:RDF.rdf:Description.pdfaExtension:schemas.rdf:Bag': string,
* }
*
* @phpstan-type TStackBBox array<int, TBBox>
*
* @phpstan-import-type TAnnot from Output
* @phpstan-import-type TEmbeddedFile from Output
* @phpstan-import-type TObjID from Output
* @phpstan-import-type TOutline from Output
* @phpstan-import-type TSignature from Output
* @phpstan-import-type TSignTimeStamp from Output
* @phpstan-import-type TGTransparency from Output
* @phpstan-import-type TUserRights from Output
* @phpstan-import-type TXOBject from Output
*
* @SuppressWarnings("PHPMD")
*/
abstract class Base
{
/**
* Encrypt object.
*/
public ObjEncrypt $encrypt;
/**
* Color object.
*/
public ObjColor $color;
/**
* Barcode object.
*/
public ObjBarcode $barcode;
/**
* File object.
*/
public ObjFile $file;
/**
* Cache object.
*/
public ObjCache $cache;
/**
* Unicode Convert object.
*/
public ObjUniConvert $uniconv;
/**
* Page object.
*/
public ObjPage $page;
/**
* Graph object.
*/
public ObjGraph $graph;
/**
* Font object.
*/
public ObjFont $font;
/**
* Image Import object.
*/
public ObjImage $image;
/**
* TCPDF version.
*/
protected string $version = '8.1.5';
/**
* Time is seconds since EPOCH when the document was created.
*/
protected int $doctime = 0;
/**
* Time is seconds since EPOCH when the document was modified.
*/
protected int $docmodtime = 0;
/**
* The name of the application that generates the PDF.
*
* If the document was converted to PDF from another format,
* the name of the conforming product that created the original document from which it was converted.
*/
protected string $creator = 'TCPDF';
/**
* The name of the person who created the document.
*/
protected string $author = 'TCPDF';
/**
* Subject of the document.
*/
protected string $subject = '-';
/**
* Title of the document.
*/
protected string $title = 'PDF Document';
/**
* Space-separated list of keywords associated with the document.
*/
protected string $keywords = 'TCPDF';
/**
* Additional custom XMP data.
*
* @var TCustomXMP
*/
protected array $custom_xmp = [
'x:xmpmeta' => '',
'x:xmpmeta.rdf:RDF' => '',
'x:xmpmeta.rdf:RDF.rdf:Description' => '',
'x:xmpmeta.rdf:RDF.rdf:Description.pdfaExtension:schemas' => '',
'x:xmpmeta.rdf:RDF.rdf:Description.pdfaExtension:schemas.rdf:Bag' => '',
];
/**
* Set this to TRUE to add the default sRGB ICC color profile
*/
protected bool $sRGB = false;
/**
* Viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
* (PDF reference, "Viewer Preferences").
*
* @var TViewerPref
*/
protected array $viewerpref = [];
/**
* Boolean flag to set the default document language direction.
* False = LTR = Left-To-Right.
* True = RTL = Right-To-Left.
*
* @val bool
*/
protected bool $rtl = false;
/**
* Document ID.
*/
protected string $fileid;
/**
* Unit of measure.
*/
protected string $unit = 'mm';
/**
* Valid HTML/CSS/SVG units.
*
* @var array<string>
*/
protected const VALIDUNITS = [
'%', 'ch', 'cm', 'em', 'ex',
'in', 'mm', 'pc', 'pt', 'px',
'rem', 'vh', 'vmax', 'vmin', 'vw',
];
/**
* Map of relative font sizes.
* The key is the relative size and the value is the font size increment in points.
*
* @var array<string, float>
*/
protected const FONTRELSIZE = [
'xx-small' => -4.0,
'x-small' => -3.0,
'smaller' => -3.0,
'small' => -2.0,
'medium' => 0.0,
'large' => 2.0,
'x-large' => 4.0,
'larger' => 3.0,
'xx-large' => 6.0,
];
/**
* Default eference values for unit conversion.
*
* @var TRefUnitValues
*/
protected const REFUNITVAL = [
'parent' => 1.0,
'font' => [
'rootsize' => 10.0,
'size' => 10.0,
'xheight' => 5.0,
'zerowidth' => 3.0,
],
'viewport' => [
'width' => 1000.0,
'height' => 1000.0,
],
'page' => [
'width' => 595.276,
'height' => 841.890,
],
];
/**
* DPI (Dot Per Inch) Document Resolution (do not change).
* 1pt = 1/72 of 1in.
*/
protected float $dpi = 72.0;
/**
* Unit of measure conversion ratio.
*/
protected float $kunit = 1.0;
/**
* Ratio between an internal point and pixel size.
*/
protected float $pointtopixelratio = 1.0;
/**
* Version of the PDF/A mode or 0 otherwise.
*/
protected int $pdfa = 0;
/**
* Enable stream compression.
*/
protected bool $compress = true;
/**
* True if we are in PDF/X mode.
*/
protected bool $pdfx = false;
/**
* True if the document is signed.
*/
protected bool $sign = false;
/**
* True if the signature approval is enabled (for incremental updates).
*/
protected bool $sigapp = false;
/**
* True to subset the fonts.
*/
protected bool $subsetfont = false;
/**
* True for Unicode font mode.
*/
protected bool $isunicode = true;
/**
* Document encoding.
*/
protected string $encoding = 'UTF-8';
/**
* Current PDF object number.
*/
public int $pon = 0;
/**
* PDF version.
*/
protected string $pdfver = '1.7';
/**
* Defines the way the document is to be displayed by the viewer.
*
* @var array{
* zoom: int|string,
* layout: string,
* mode: string,
* }
*/
protected array $display = [
'zoom' => 'default',
'layout' => 'SinglePage',
'mode' => 'UseNone',
];
/**
* Embedded files data.
*
* @var array<string, TEmbeddedFile>
*/
protected array $embeddedfiles = [];
/**
* Annotations indexed bu object IDs.
*
* @var array<int, TAnnot>
*/
protected array $annotation = [];
/**
* Array containing the regular expression used to identify withespaces or word separators.
*
* @var array{
* r: string,
* p: string,
* m: string,
* }
*/
protected array $spaceregexp = [
'r' => '/[^\S\xa0]/',
'p' => '[^\S\xa0]',
'm' => '',
];
/**
* File name of the PDF document.
*/
protected string $pdffilename;
/**
* Raw encoded fFile name of the PDF document.
*/
protected string $encpdffilename;
/**
* Array containing the ID of some named PDF objects.
*
* @var TObjID
*/
protected array $objid = [
'catalog' => 0,
'dests' => 0,
'form' => [],
'info' => 0,
'pages' => 0,
'resdic' => 0,
'signature' => 0,
'srgbicc' => 0,
'xmp' => 0,
];
/**
* Current XOBject template ID.
*
* @var string
*/
protected string $xobjtid = '';
/**
* Outlines Data.
*
* @var array<int, TOutline>
*/
protected array $outlines = [];
/**
* Outlines Root object ID.
*/
protected int $outlinerootoid = 0;
/**
* Javascript catalog entry.
*/
protected string $jstree = '';
/**
* Signature Data.
*
* @var TSignature
*/
protected array $signature = [
'appearance' => [
'empty' => [],
'name' => '',
'page' => 0,
'rect' => '',
],
'approval' => '',
'cert_type' => -1,
'extracerts' => null,
'info' => [
'ContactInfo' => '',
'Location' => '',
'Name' => '',
'Reason' => '',
],
'password' => '',
'privkey' => '',
'signcert' => '',
];
/**
* Signature Timestamp Data.
*
* @var TSignTimeStamp
*/
protected array $sigtimestamp = [
'enabled' => false,
'host' => '',
'username' => '',
'password' => '',
'cert' => '',
];
/**
* ByteRange placemark used during digital signature process.
*
* @var string
*/
protected const BYTERANGE = '/ByteRange[0 ********** ********** **********]';
/**
* Digital signature max length.
*
* @var int
*/
protected const SIGMAXLEN = 11742;
/**
* User rights Data.
*
* @var TUserRights
*/
protected array $userrights = [
'annots' => '/Create/Delete/Modify/Copy/Import/Export',
'document' => '/FullSave',
'ef' => '/Create/Delete/Modify/Import',
'enabled' => false,
'form' => '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
'formex' => '', // 'BarcodePlaintext',
'signature' => '/Modify',
];
/**
* XObjects data.
*
* @var array<string, TXOBject>
*/
protected array $xobjects = [];
/**
* Stack of bounding boxes [x, y, width, height] in user units.
*
* @var TStackBBox
*/
protected array $bbox = [[
'x' => 0,
'y' => 0,
'w' => 0,
'h' => 0,
]];
/**
* Set to true to enable the default page footer.
*
* @var bool
*/
protected bool $defPageContentEnabled = false;
/**
* Default font for defautl page content.
*
* @var ?TFontMetric
*/
protected ?array $defaultfont = null;
/**
* The default relative position of the cell origin when
* the border is centered on the cell edge.
*/
public const BORDERPOS_DEFAULT = 0;
/**
* The relative position of the cell origin when
* the border is external to the cell edge.
*/
public const BORDERPOS_EXTERNAL = -0.5; //-1/2
/**
* The relative position of the cell origin when
* the border is internal to the cell edge.
*/
public const BORDERPOS_INTERNAL = 0.5; // 1/2
/**
* Default values for cell.
*
* @const TCellDef
*/
public const ZEROCELL = [
'margin' => [
'T' => 0,
'R' => 0,
'B' => 0,
'L' => 0,
],
'padding' => [
'T' => 0,
'R' => 0,
'B' => 0,
'L' => 0,
],
'borderpos' => self::BORDERPOS_DEFAULT,
];
/**
* Default values for cell.
*
* @var TCellDef
*/
protected $defcell = self::ZEROCELL;
/**
* Convert user units to internal points unit.
*
* @param float $usr Value to convert.
*/
public function toPoints(float $usr): float
{
return ($usr * $this->kunit);
}
/**
* Convert internal points to user unit.
*
* @param float $pnt Value to convert in user units.
*/
public function toUnit(float $pnt): float
{
return ($pnt / $this->kunit);
}
/**
* Convert vertical user value to internal points unit.
* Note: the internal Y points coordinate starts at the bottom left of the page.
*
* @param float $usr Value to convert.
* @param float $pageh Optional page height in internal points ($pageh:$this->page->getPage()['pheight']).
*/
public function toYPoints(float $usr, float $pageh = -1): float
{
$pageh = $pageh >= 0 ? $pageh : $this->page->getPage()['pheight'];
return ($pageh - $this->toPoints($usr));
}
/**
* Convert vertical internal points value to user unit.
* Note: the internal Y points coordinate starts at the bottom left of the page.
*
* @param float $pnt Value to convert.
* @param float $pageh Optional page height in internal points ($pageh:$this->page->getPage()['pheight']).
*/
public function toYUnit(float $pnt, float $pageh = -1): float
{
$pageh = $pageh >= 0 ? $pageh : $this->page->getPage()['pheight'];
return $this->toUnit($pageh - $pnt);
}
/**
* Enable or disable the default page content.
*
* @param bool $enable Enable or disable the default page content.
*
* @return void
*/
public function enableDefaultPageContent(bool $enable = true): void
{
$this->defPageContentEnabled = $enable;
}
/**
* Set the pixel/point ratio used to convert pixel values to points.
*
* @param float $val
*
* @return void
*/
public function setPointToPixelRatio(float $val): void
{
$this->pointtopixelratio = $val;
}
/**
* Converts a string containing value and unit of measure to internal points.
* This is used to convert values for SVG, CSS, HTML.
*
* @param string|float|int $val String containing values and unit.
* @param TRefUnitValues $ref Reference values in internal points.
* @param string $defunit Default unit (can be one of the VALIDUNITS).
*
* @return float Internal points value.
*/
protected function getUnitValuePoints(
string|float|int $val,
array $ref = self::REFUNITVAL,
string $defunit = 'px',
): float {
$unit = 'px';
if (in_array($defunit, self::VALIDUNITS)) {
$unit = $defunit;
}
$value = 0.0;
if (is_numeric($val)) {
$value = floatval($val);
} elseif (preg_match('/([0-9\.\-\+]+)([a-z%]{0,4})/', $val, $match)) {
$value = floatval($match[1]);
if (in_array($match[2], self::VALIDUNITS)) {
$unit = $match[2];
}
} else {
throw new PdfException('Invalid value: ' . $val);
}
return match ($unit) {
// Percentage relative to the parent element.
'%' => (($value * $ref['parent']) / 100),
// Relative to the width of the "0" (zero)
'ch' => ($value * $ref['font']['zerowidth']),
// Centimeters.
'cm' => (($value * $this->dpi) / 2.54),
// Relative to the font-size of the element.
'em' => ($value * $ref['font']['size']),
// Relative to the x-height of the current font.
'ex' => ($value * $ref['font']['xheight']),
// Inches.
'in' => ($value * $this->dpi),
// Millimeters.
'mm' => (($value * $this->dpi) / 25.4),
// One pica is 12 points.
'pc' => ($value * 12),
// Points.
'pt' => $value,
// Pixels.
'px' => ($value * $this->pointtopixelratio),
// Relative to font-size of the root element.
'rem' => ($value * $ref['font']['rootsize']),
// Relative to 1% of the height of the viewport.
'vh' => (($value * $ref['viewport']['height']) / 100),
// Relative to 1% of viewport's* larger dimension.
'vmax' => (($value * max($ref['viewport']['height'], $ref['viewport']['width'])) / 100),
// Relative to 1% of viewport's smaller dimension.
'vmin' => (($value * min($ref['viewport']['height'], $ref['viewport']['width'])) / 100),
// Relative to 1% of the width of the viewport.
'vw' => (($value * $ref['viewport']['width']) / 100),
// Default to pixels.
default => ($value * $this->pointtopixelratio),
};
}
/**
* Converts a string containing font size value to internal points.
* This is used to convert values for SVG, CSS, HTML.
*
* @param string|float|int $val String containing values and unit.
* @param TRefUnitValues $ref Reference values in internal points.
* @param string $defunit Default unit (can be one of the VALIDUNITS).
*
* @return float Internal points value.
*/
protected function getFontValuePoints(
string|float|int $val,
array $ref = self::REFUNITVAL,
string $defunit = 'pt',
): float {
if (is_string($val) && isset(self::FONTRELSIZE[$val])) {
return ($ref['parent'] + self::FONTRELSIZE[$val]);
}
return $this->getUnitValuePoints($val, $ref, $defunit);
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
/**
* CSS.php
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
namespace Com\Tecnick\Pdf;
use Com\Tecnick\Pdf\Exception as PdfException;
/**
* Com\Tecnick\Pdf\CSS
*
* CSS PDF class
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*/
abstract class CSS extends \Com\Tecnick\Pdf\SVG
{
}
+625
View File
@@ -0,0 +1,625 @@
<?php
/**
* Text.php
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
namespace Com\Tecnick\Pdf;
/**
* Com\Tecnick\Pdf\Cell
*
* Cell PDF data
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* @phpstan-import-type StyleDataOpt from \Com\Tecnick\Pdf\Graph\Style
* @phpstan-import-type TCellDef from \Com\Tecnick\Pdf\Base
*
*/
abstract class Cell extends \Com\Tecnick\Pdf\Base
{
/**
* Set the default cell margin in user units.
*
* @param float $top Top.
* @param float $right Right.
* @param float $bottom Bottom.
* @param float $left Left.
*/
public function setDefaultCellMargin(
float $top,
float $right,
float $bottom,
float $left
): void {
$this->defcell['margin']['T'] = $this->toPoints($top);
$this->defcell['margin']['R'] = $this->toPoints($right);
$this->defcell['margin']['B'] = $this->toPoints($bottom);
$this->defcell['margin']['L'] = $this->toPoints($left);
}
/**
* Set the default cell padding in user units.
*
* @param float $top Top.
* @param float $right Right.
* @param float $bottom Bottom.
* @param float $left Left.
*/
public function setDefaultCellPadding(
float $top,
float $right,
float $bottom,
float $left
): void {
$this->defcell['padding']['T'] = $this->toPoints($top);
$this->defcell['padding']['R'] = $this->toPoints($right);
$this->defcell['padding']['B'] = $this->toPoints($bottom);
$this->defcell['padding']['L'] = $this->toPoints($left);
}
/**
* Sets the default cell border position.
*
* @param float $borderpos The border position to set:
* BORDERPOS_DEFAULT
* BORDERPOS_EXTERNAL
* BORDERPOS_INTERNAL
*/
public function setDefaultCellBorderPos(float $borderpos): void
{
if (
($borderpos == self::BORDERPOS_DEFAULT)
|| ($borderpos == self::BORDERPOS_EXTERNAL)
|| ($borderpos == self::BORDERPOS_INTERNAL)
) {
$this->defcell['borderpos'] = $borderpos;
return;
}
$this->defcell['borderpos'] = self::BORDERPOS_DEFAULT;
}
/**
* Increase the cell padding to account for the border tickness.
*
* @param array<int|string, StyleDataOpt> $styles Optional to overwrite the styles (see: getCurrentStyleArray).
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*
* @return TCellDef
*/
protected function adjustMinCellPadding(
array $styles = [],
?array $cell = null
): array {
if ($cell === null) {
$cell = $this->defcell;
}
if ($styles === []) {
$styles = $this->graph->getCurrentStyleArray();
}
$border_ratio = round(self::BORDERPOS_INTERNAL + $cell['borderpos'], 1);
$minT = 0;
$minR = 0;
$minB = 0;
$minL = 0;
if (! empty($styles['all']['lineWidth'])) {
$minT = $this->toPoints((float) $styles['all']['lineWidth'] * $border_ratio);
$minR = $minT;
$minB = $minT;
$minL = $minT;
} elseif (
(count($styles) == 4)
&& isset($styles[0]['lineWidth'])
&& isset($styles[1]['lineWidth'])
&& isset($styles[2]['lineWidth'])
&& isset($styles[3]['lineWidth'])
) {
$minT = $this->toPoints((float) $styles[0]['lineWidth'] * $border_ratio);
$minR = $this->toPoints((float) $styles[1]['lineWidth'] * $border_ratio);
$minB = $this->toPoints((float) $styles[2]['lineWidth'] * $border_ratio);
$minL = $this->toPoints((float) $styles[3]['lineWidth'] * $border_ratio);
} else {
return $cell;
}
$cell['padding']['T'] = max($cell['padding']['T'], $minT);
$cell['padding']['R'] = max($cell['padding']['R'], $minR);
$cell['padding']['B'] = max($cell['padding']['B'], $minB);
$cell['padding']['L'] = max($cell['padding']['L'], $minL);
return $cell;
}
/**
* Returns the minimum cell height in points for the current text height.
*
* @param float $pheight Text height in internal points.
* @param string $align Text vertical alignment inside the cell:
* - T=top;
* - C=center;
* - B=bottom;
* - A=center-on-font-ascent;
* - L=center-on-font-baseline;
* - D=center-on-font-descent.
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*/
protected function cellMinHeight(
float $pheight = 0,
string $align = 'C',
?array $cell = null
): float {
if ($cell === null) {
$cell = $this->defcell;
}
$curfont = $this->font->getCurrentFont();
if ($pheight == 0) {
$pheight = $curfont['height'];
}
return match ($align) {
'T', 'B' => ($pheight + $cell['padding']['T'] + $cell['padding']['B']),
'L' => ($pheight - $curfont['height'] + (2 * max(
($cell['padding']['T'] + $curfont['ascent']),
($cell['padding']['B'] - $curfont['descent'])
))),
'A', 'D' => ($pheight
- $curfont['height']
+ (2 * ($curfont['height'] + max($cell['padding']['T'], $cell['padding']['B'])))),
// default on 'C' case
default => ($pheight + (2 * max($cell['padding']['T'], $cell['padding']['B']))),
};
}
/**
* Returns the minimum cell width in points for the current text
*
* @param float $txtwidth Text width in internal points.
* @param string $align Cell horizontal alignment: L=left; C=center; R=right; J=Justify.
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*/
protected function cellMinWidth(
float $txtwidth,
string $align = 'L',
?array $cell = null
): float {
if ($cell === null) {
$cell = $this->defcell;
}
if ($align === '' || $align === 'J') { // Justify
$align = $this->rtl ? 'R' : 'L';
}
return match ($align) {
'C' => ceil($txtwidth + (2 * max($cell['padding']['L'], $cell['padding']['R']))),
default => ceil($txtwidth + $cell['padding']['L'] + $cell['padding']['R']),
};
}
/**
* Returns the adjusted cell top Y coordinate to account for margins.
*
* @param float $pnty Starting top Y coordinate in internal points.
* @param float $pheight Cell height in internal points.
* @param string $align Cell vertical alignment: T=top; C=center; B=bottom.
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*/
protected function cellVPos(
float $pnty,
float $pheight,
string $align = 'T',
?array $cell = null
): float {
if ($cell === null) {
$cell = $this->defcell;
}
return match ($align) {
'T' => $pnty - $cell['margin']['T'],
'C' => $pnty + ($pheight / 2),
'B' => $pnty + $cell['margin']['B'] + $pheight,
default => $pnty,
};
}
/**
* Returns the adjusted cell left X coordinate to account for margins.
*
* @param float $pntx Starting top Y coordinate in internal points.
* @param float $pwidth Cell width in internal points.
* @param string $align Cell horizontal alignment: L=left; C=center; R=right; J=Justify.
* @param TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*/
protected function cellHPos(
float $pntx,
float $pwidth,
string $align = 'L',
?array $cell = null
): float {
if ($cell === null) {
$cell = $this->defcell;
}
if ($align === '' || $align === 'J') { // Justify
$align = $this->rtl ? 'R' : 'L';
}
return match ($align) {
'L' => $pntx + $cell['margin']['L'],
'R' => $pntx - $cell['margin']['R'] - $pwidth,
'C' => $pntx - ($pwidth / 2),
default => $pntx,
};
}
/**
* Returns the vertical distance between the cell top side and the text.
*
* @param float $cellpheight Cell height in internal points.
* @param float $txtpheight Text height in internal points.
* @param string $align Text vertical alignment inside the cell:
* - T=top;
* - C=center;
* - B=bottom;
* - A=center-on-font-ascent;
* - L=center-on-font-baseline;
* - D=center-on-font-descent.
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*/
protected function cellTextVAlign(
float $cellpheight,
float $txtpheight = 0,
string $align = 'C',
?array $cell = null
): float {
if ($cell === null) {
$cell = $this->defcell;
}
$curfont = $this->font->getCurrentFont();
if ($txtpheight == 0) {
$txtpheight = $curfont['height'];
}
return match ($align) {
'T' => ($cell['padding']['T']),
'B' => (($cellpheight - $txtpheight) - $cell['padding']['B']),
'L' => ((($cellpheight - $txtpheight + $curfont['height']) / 2) - $curfont['ascent']),
'A' => (($cellpheight - $txtpheight + $curfont['height']) / 2),
'D' => ((($cellpheight - $txtpheight + $curfont['height']) / 2) - $curfont['height']),
// default on 'C' case
default => (($cellpheight - $txtpheight) / 2)
};
}
/**
* Returns the horizontal distance between the cell left side and the text left side.
*
* @param float $pwidth Cell width in internal points.
* @param float $txtpwidth Text width in internal points.
* @param string $align Text horizontal alignment inside the cell: L=left; C=center; R=right; J=Justify.
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*/
protected function cellTextHAlign(
float $pwidth,
float $txtpwidth,
string $align = 'L',
?array $cell = null
): float {
if ($cell === null) {
$cell = $this->defcell;
}
if ($align === '' || $align === 'J') { // Justify
$align = $this->rtl ? 'R' : 'L';
}
return match ($align) {
'C' => (($pwidth - $txtpwidth) / 2),
'R' => ($pwidth - $cell['padding']['R'] - $txtpwidth),
// default on 'L' case
default => ($cell['padding']['L']),
};
}
/**
* Returns the top Y coordinate of the cell wrapping the text.
*
* @param float $txty Text baseline top Y coordinate in internal points.
* @param float $cellpheight Cell height in internal points.
* @param float $txtpheight Text height in internal points.
* @param string $align Text vertical alignment inside the cell:
* - T=top;
* - C=center;
* - B=bottom;
* - A=center-on-font-ascent;
* - L=center-on-font-baseline;
* - D=center-on-font-descent.
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*/
protected function cellVPosFromText(
float $txty,
float $cellpheight,
float $txtpheight = 0,
string $align = 'C',
?array $cell = null
): float {
return ($txty + $this->cellTextVAlign($cellpheight, $txtpheight, $align, $cell));
}
/**
* Returns the left X coordinate of the cell wrapping the text.
*
* @param float $txtx Text left X coordinate in internal points.
* @param float $pwidth Cell width in internal points.
* @param float $txtpwidth Text width in internal points.
* @param string $align Text horizontal alignment inside the cell: L=left; C=center; R=right.
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*/
protected function cellHPosFromText(
float $txtx,
float $pwidth,
float $txtpwidth,
string $align = 'L',
?array $cell = null
): float {
return ($txtx - $this->cellTextHAlign($pwidth, $txtpwidth, $align, $cell));
}
/**
* Returns the top Y coordinate of the text inside the cell.
*
* @param float $pnty Cell top Y coordinate in internal points.
* @param float $cellpheight Cell height in internal points.
* @param float $txtpheight Text height in internal points.
* @param string $align Text vertical alignment inside the cell:
* - T=top;
* - C=center;
* - B=bottom;
* - A=center-on-font-ascent;
* - L=center-on-font-baseline;
* - D=center-on-font-descent.
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*/
protected function textVPosFromCell(
float $pnty,
float $cellpheight,
float $txtpheight = 0,
string $align = 'C',
?array $cell = null
): float {
return ($pnty - $this->cellTextVAlign($cellpheight, $txtpheight, $align, $cell));
}
/**
* Returns the left X coordinate of the text inside the cell.
*
* @param float $pntx Cell left X coordinate in internal points.
* @param float $pwidth Cell width in internal points.
* @param float $txtpwidth Text width in internal points.
* @param string $align Text horizontal alignment inside the cell: L=left; C=center; R=right.
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*/
protected function textHPosFromCell(
float $pntx,
float $pwidth,
float $txtpwidth,
string $align = 'L',
?array $cell = null
): float {
return ($pntx + $this->cellTextHAlign($pwidth, $txtpwidth, $align, $cell));
}
/**
* Calculates the maximum width available for a cell that fits the current region width.
*
* @param float $pntx Cell left X coordinate in internal points.
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*
* @return float
*/
protected function cellMaxWidth(
float $pntx = 0,
?array $cell = null
): float {
if ($cell === null) {
$cell = $this->defcell;
}
$region = $this->page->getRegion();
return ($this->toPoints($region['RW']) - $pntx - $cell['margin']['L'] - $cell['margin']['R']);
}
/**
* Calculates the maximum width available for text within a cell.
*
* @param float $pwidth Cell width in internal points.
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*
* @return float The maximum width available for text within the cell.
*/
protected function textMaxWidth(
float $pwidth,
?array $cell = null
): float {
if ($cell === null) {
$cell = $this->defcell;
}
return ($pwidth - $cell['padding']['L'] - $cell['padding']['R']);
}
/**
* Calculates the maximum height available for text within a cell.
*
* @param float $pheight Available vertical space in internal points.
* @param string $align Text vertical alignment inside the cell:
* - T=top;
* - C=center;
* - B=bottom;
* - A=center-on-font-ascent;
* - L=center-on-font-baseline;
* - D=center-on-font-descent.
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*
* @return float The maximum width available for text within the cell.
*/
protected function textMaxHeight(
float $pheight,
string $align = 'T',
?array $cell = null
): float {
if ($cell === null) {
$cell = $this->defcell;
}
$curfont = $this->font->getCurrentFont();
$cph = ($pheight - $cell['margin']['T'] - $cell['margin']['B']);
// Use a match expression to determine the maximum text height based on alignment.
return match ($align) {
// Top or Bottom
'T', 'B' => ($cph - $cell['padding']['T'] - $cell['padding']['B']),
// Center on font Baseline
'L' => ($cph + $curfont['height'] - (2 * max(
($cell['padding']['T'] + $curfont['ascent']),
($cell['padding']['B'] - $curfont['descent'])
))),
// Center on font Ascent or Descent
'A', 'D' => ($cph
+ $curfont['height']
- (2 * ($curfont['height'] + max($cell['padding']['T'], $cell['padding']['B'])))),
// Default to Center 'C' case
default => ($cph - (2 * max($cell['padding']['T'], $cell['padding']['B']))),
};
}
/**
* Returns the PDF code to draw the text cell border and background.
*
* @param float $pntx Cell left X coordinate in internal points.
* @param float $pnty Cell top Y coordinate in internal points.
* @param float $pwidth Cell width in internal points.
* @param float $pheight Cell height in internal points.
* @param array<int|string, StyleDataOpt> $styles Optional to overwrite the styles (see: getCurrentStyleArray).
* @param ?TCellDef $cell Optional to overwrite cell parameters for padding, margin etc.
*
* @return string
*/
protected function drawCell(
float $pntx,
float $pnty,
float $pwidth,
float $pheight,
array $styles = [],
?array $cell = null
) {
$drawfill = (!empty($styles['all']['fillColor']));
$drawborder = (
!empty($styles['all']['lineWidth'])
|| !empty($styles[0]['lineWidth'])
|| !empty($styles[1]['lineWidth'])
|| !empty($styles[2]['lineWidth'])
|| !empty($styles[3]['lineWidth'])
);
if (!$drawfill && !$drawborder) {
return '';
}
if ($cell === null) {
$cell = $this->defcell;
}
$styleall = (empty($styles['all']) ? [] : $styles['all']);
$out = $this->graph->getStartTransform();
$stoptr = $this->graph->getStopTransform();
if (
$drawfill
&& $drawborder
&& ($cell['borderpos'] == self::BORDERPOS_DEFAULT)
&& (count($styles) <= 1)
) {
// single default border style for all sides
$out .= $this->graph->getBasicRect(
$this->toUnit($pntx),
$this->toYUnit($pnty),
$this->toUnit($pwidth),
$this->toUnit($pheight),
'b', // close, fill, and then stroke the path
$styleall,
);
return $out . $stoptr;
}
if ($drawfill) {
$out .= $this->graph->getBasicRect(
$this->toUnit($pntx),
$this->toYUnit($pnty),
$this->toUnit($pwidth),
$this->toUnit($pheight),
'f', // fill the path
$styleall,
);
}
if (!$drawborder) {
return $out . $stoptr;
}
$adj = (isset($styles['all']['lineWidth'])
? $this->toPoints((float) $styles['all']['lineWidth'] * $cell['borderpos'])
: 0);
$adjx = (isset($styles['3']['lineWidth'])
? $this->toPoints((float) $styles['3']['lineWidth'] * $cell['borderpos'])
: $adj);
$adjy = isset($styles['0']['lineWidth'])
? $this->toPoints((float) $styles['0']['lineWidth'] * $cell['borderpos'])
: $adj;
$adjw = $adjx + (isset($styles['1']['lineWidth'])
? $this->toPoints((float) $styles['1']['lineWidth'] * $cell['borderpos'])
: $adj);
$adjh = $adjy + (isset($styles['2']['lineWidth'])
? $this->toPoints((float) $styles['2']['lineWidth'] * $cell['borderpos'])
: $adj);
// different border styles for each side
$out .= $this->graph->getRect(
$this->toUnit($pntx + $adjx),
$this->toYUnit($pnty - $adjy),
$this->toUnit($pwidth - $adjw),
$this->toUnit($pheight - $adjh),
's', // close and stroke the path
$styles,
);
return $out . $stoptr;
}
}
+105
View File
@@ -0,0 +1,105 @@
<?php
/**
* ClassObjects.php
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
namespace Com\Tecnick\Pdf;
use Com\Tecnick\Barcode\Barcode as ObjBarcode;
use Com\Tecnick\Color\Pdf as ObjColor;
use Com\Tecnick\File\Cache as ObjCache;
use Com\Tecnick\File\File as ObjFile;
use Com\Tecnick\Pdf\Encrypt\Encrypt as ObjEncrypt;
use Com\Tecnick\Pdf\Font\Stack as ObjFont;
use Com\Tecnick\Pdf\Graph\Draw as ObjGraph;
use Com\Tecnick\Pdf\Image\Import as ObjImage;
use Com\Tecnick\Pdf\Page\Page as ObjPage;
use Com\Tecnick\Unicode\Convert as ObjUniConvert;
/**
* Com\Tecnick\Pdf\ClassObjects
*
* External class objects PDF class
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* @SuppressWarnings("PHPMD.DepthOfInheritance")
*/
abstract class ClassObjects extends \Com\Tecnick\Pdf\Output
{
/**
* Initialize dependencies class objects.
*
* @param ?ObjEncrypt $objEncrypt Encryption object.
*/
public function initClassObjects(
?ObjEncrypt $objEncrypt = null
): void {
if ($objEncrypt instanceof ObjEncrypt) {
$this->encrypt = $objEncrypt;
} else {
$this->encrypt = new ObjEncrypt();
}
$this->color = new ObjColor();
$this->barcode = new ObjBarcode();
$this->file = new ObjFile();
$this->cache = new ObjCache();
$this->uniconv = new ObjUniConvert();
$pdfamode = (bool) ($this->pdfa > 0);
$this->page = new ObjPage(
$this->unit,
$this->color,
$this->encrypt,
$pdfamode,
$this->compress,
$this->sigapp,
);
$this->kunit = $this->page->getKUnit();
$this->svgminunitlen = $this->toUnit($this::SVGMINPNTLEN);
$this->graph = new ObjGraph(
$this->kunit,
0, // $this->graph->setPageWidth($pagew)
0, // $this->graph->setPageHeight($pageh)
$this->color,
$this->encrypt,
$pdfamode,
$this->compress,
);
$this->font = new ObjFont(
$this->kunit,
$this->subsetfont,
$this->isunicode,
$pdfamode,
);
$this->image = new ObjImage(
$this->kunit,
$this->encrypt,
$pdfamode,
$this->compress,
);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/**
* Exception.php
*
* @since 2015-02-21
* @category Library
* @package PDF
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2015-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
namespace Com\Tecnick\Pdf;
/**
* Com\Tecnick\Pdf\Exception
*
* Custom Exception class
*
* @since 2015-02-21
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2015-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*/
class Exception extends \Exception
{
}
+36
View File
@@ -0,0 +1,36 @@
<?php
/**
* HTML.php
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
namespace Com\Tecnick\Pdf;
use Com\Tecnick\Pdf\Exception as PdfException;
/**
* Com\Tecnick\Pdf\HTML
*
* HTML PDF class
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*/
abstract class HTML extends \Com\Tecnick\Pdf\CSS
{
}
+38
View File
@@ -0,0 +1,38 @@
<?php
/**
* JavaScript.php
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
namespace Com\Tecnick\Pdf;
use Com\Tecnick\Pdf\Exception as PdfException;
/**
* Com\Tecnick\Pdf\JavaScript
*
* JavaScript PDF class
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* @SuppressWarnings("PHPMD.DepthOfInheritance")
*/
abstract class JavaScript extends \Com\Tecnick\Pdf\HTML
{
}
+593
View File
@@ -0,0 +1,593 @@
<?php
/**
* MetaInfo.php
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* This file is part of tc-lib-pdf software library.
*/
namespace Com\Tecnick\Pdf;
use Com\Tecnick\Pdf\Exception as PdfException;
/**
* Com\Tecnick\Pdf\MetaInfo
*
* Meta Informaton PDF class
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-2025 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf
*
* @phpstan-import-type TViewerPref from Base
*
* @SuppressWarnings("PHPMD.DepthOfInheritance")
*/
abstract class MetaInfo extends \Com\Tecnick\Pdf\JavaScript
{
/**
* Valid document zoom modes
*
* @var array<string>
*/
protected const VALIDZOOM = ['fullpage', 'fullwidth', 'real', 'default'];
/**
* Return the program version.
*/
public function getVersion(): string
{
return $this->version;
}
/**
* Set a field value only if it is not empty.
*
* @param string $field Field name
* @param string $value Value to set
*/
private function setNonEmptyFieldValue(string $field, string $value): static
{
if ($value !== '') {
$this->$field = $value;
}
return $this;
}
/**
* Set the value of an existing array key if it is not empty.
*
* @param string $field Field array name
* @param string $key Key name
* @param string $value Value to set
*/
private function setNonEmptyArrayFieldValue(string $field, string $key, string $value): static
{
if (
isset($this->$field)
&& is_array($this->$field)
&& ($key !== '')
&& isset($this->$field[$key])
&& ($value !== '')
) {
$this->$field[$key] = $value;
}
return $this;
}
/**
* Defines the creator of the document.
* This is typically the name of the application that generates the PDF.
*
* @param string $creator The name of the creator.
*/
public function setCreator(string $creator): static
{
return $this->setNonEmptyFieldValue('creator', $creator);
}
/**
* Defines the author of the document.
*
* @param string $author The name of the author.
*/
public function setAuthor(string $author): static
{
return $this->setNonEmptyFieldValue('author', $author);
}
/**
* Defines the subject of the document.
*
* @param string $subject The subject.
*/
public function setSubject(string $subject): static
{
return $this->setNonEmptyFieldValue('subject', $subject);
}
/**
* Defines the title of the document.
*
* @param string $title The title.
*/
public function setTitle(string $title): static
{
return $this->setNonEmptyFieldValue('title', $title);
}
/**
* Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
*
* @param string $keywords Space-separated list of keywords.
*/
public function setKeywords(string $keywords): static
{
return $this->setNonEmptyFieldValue('keywords', $keywords);
}
/**
* Set the PDF version (check PDF reference for valid values).
*
* @param string $version PDF document version.
*
* @throw PdfException in case of error.
*/
public function setPDFVersion(string $version = '1.7'): static
{
if ($this->pdfa == 1) { // PDF/A 1 mode
$this->pdfver = '1.4';
return $this;
}
$isvalid = preg_match('/^[1-9]+[.]\d+$/', $version);
if ($isvalid === false) {
throw new PdfException('Invalid PDF version format');
}
$this->pdfver = $version;
return $this;
}
/**
* Set the sRGB mode
*
* @param bool $enabled Set to true to add the default sRGB ICC color profile
*/
public function setSRGB(bool $enabled): static
{
$this->sRGB = $enabled;
return $this;
}
/**
* Format a text string for output.
*
* @param string $str String to escape.
* @param int $oid Current PDF object number.
* @param bool $bom If true set the Byte Order Mark (BOM).
*
* @return string escaped string.
*/
protected function getOutTextString(
string $str,
int $oid,
bool $bom = false
): string {
if ($this->isunicode) {
$str = $this->uniconv->toUTF16BE($str);
if ($bom) {
$str = "\xFE\xFF" . $str; // Byte Order Mark (BOM)
}
}
return $this->encrypt->escapeDataString($str, $oid);
}
/**
* Returns a formatted date for meta information
*
* @param int $time Time in seconds.
*
* @return string date-time string.
*/
protected function getFormattedDate(int $time): string
{
return substr_replace(date('YmdHisO', $time), "'", (-2), 0) . "'";
}
/**
* Returns a formatted date for XMP meta information
*
* @param int $time Time in seconds.
*
* @return string date-time string.
*/
protected function getXMPFormattedDate(int $time): string
{
return date('Y-m-dTH:i:sP', $time);
}
/**
* Returns the producer string
*/
protected function getProducer(): string
{
return "\x54\x43\x50\x44\x46\x20"
. $this->version
. "\x20\x28\x68\x74\x74\x70\x73\x3a\x2f\x2f"
. "\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29";
}
/**
* Returns a formatted date for meta information
*
* @param int $time Time in seconds.
* @param int $oid Current PDF object number.
*
* @return string escaped date-time string.
*/
protected function getOutDateTimeString(int $time, int $oid): string
{
if ($time === 0) {
$time = $this->doctime;
}
return $this->encrypt->escapeDataString('D:' . $this->getFormattedDate($time), $oid);
}
/**
* Get the PDF output string for the Document Information Dictionary.
* (ref. Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf)
*/
protected function getOutMetaInfo(): string
{
$oid = ++$this->pon;
$this->objid['info'] = $oid;
return $oid . ' 0 obj' . "\n"
. '<<'
. ' /Creator ' . $this->getOutTextString($this->creator, $oid, true)
. ' /Author ' . $this->getOutTextString($this->author, $oid, true)
. ' /Subject ' . $this->getOutTextString($this->subject, $oid, true)
. ' /Title ' . $this->getOutTextString($this->title, $oid, true)
. ' /Keywords ' . $this->getOutTextString($this->keywords, $oid, true)
. ' /Producer ' . $this->getOutTextString($this->getProducer(), $oid, true)
. ' /CreationDate ' . $this->getOutDateTimeString($this->doctime, $oid)
. ' /ModDate ' . $this->getOutDateTimeString($this->docmodtime, $oid)
. ' /Trapped /False'
. ' >>' . "\n"
. 'endobj' . "\n";
}
/**
* Escape some special characters (&lt; &gt; &amp;) for XML output.
*
* @param string $str Input string to escape.
*/
protected function getEscapedXML(string $str): string
{
return strtr($str, [
"\0" => '',
'&' => '&amp;',
'<' => '&lt;',
'>' => '&gt;',
]);
}
/**
* Set additional custom XMP data to be appended just before the end of the tag indicated by the key.
*
* IMPORTANT:
* This data is added as-is without controls, so you have to validate your data before using this method.
*
* @param string $key Key for the custom XMP data. Valid keys are:
* - 'x:xmpmeta'
* - 'x:xmpmeta.rdf:RDF'
* - 'x:xmpmeta.rdf:RDF.rdf:Description'
* - 'x:xmpmeta.rdf:RDF.rdf:Description.pdfaExtension:schemas'
* - 'x:xmpmeta.rdf:RDF.rdf:Description.pdfaExtension:schemas.rdf:Bag'
* @param string $xmp Custom XMP data.
*/
public function setCustomXMP(string $key, string $xmp): static
{
return $this->setNonEmptyArrayFieldValue('custom_xmp', $key, $xmp);
}
/**
* Get the PDF output string for the XMP data object
*
* @SuppressWarnings("PHPMD.ExcessiveMethodLength")
*/
protected function getOutXMP(): string
{
$uuid = 'uuid:' . substr($this->fileid, 0, 8)
. '-' . substr($this->fileid, 8, 4)
. '-' . substr($this->fileid, 12, 4)
. '-' . substr($this->fileid, 16, 4)
. '-' . substr($this->fileid, 20, 12);
// @codingStandardsIgnoreStart
$xmp = '<?xpacket begin="' . $this->uniconv->chr(0xfeff) . '" id="W5M0MpCehiHzreSzNTczkc9d"?>' . "\n"
. '<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.1-c043 52.372728, 2009/01/18-15:08:04">' . "\n"
. "\t" . '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">' . "\n"
. "\t\t" . '<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">' . "\n"
. "\t\t\t" . '<dc:format>application/pdf</dc:format>' . "\n"
. "\t\t\t" . '<dc:title>' . "\n"
. "\t\t\t\t" . '<rdf:Alt>' . "\n"
. "\t\t\t\t\t" . '<rdf:li xml:lang="x-default">' . $this->getEscapedXML($this->title) . '</rdf:li>' . "\n"
. "\t\t\t\t" . '</rdf:Alt>' . "\n"
. "\t\t\t" . '</dc:title>' . "\n"
. "\t\t\t" . '<dc:creator>' . "\n"
. "\t\t\t\t" . '<rdf:Seq>' . "\n"
. "\t\t\t\t\t" . '<rdf:li>' . $this->getEscapedXML($this->author) . '</rdf:li>' . "\n"
. "\t\t\t\t" . '</rdf:Seq>' . "\n"
. "\t\t\t" . '</dc:creator>' . "\n"
. "\t\t\t" . '<dc:description>' . "\n"
. "\t\t\t\t" . '<rdf:Alt>' . "\n"
. "\t\t\t\t\t" . '<rdf:li xml:lang="x-default">' . $this->getEscapedXML($this->subject) . '</rdf:li>' . "\n"
. "\t\t\t\t" . '</rdf:Alt>' . "\n"
. "\t\t\t" . '</dc:description>' . "\n"
. "\t\t\t" . '<dc:subject>' . "\n"
. "\t\t\t\t" . '<rdf:Bag>' . "\n"
. "\t\t\t\t\t" . '<rdf:li>' . $this->getEscapedXML($this->keywords) . '</rdf:li>' . "\n"
. "\t\t\t\t" . '</rdf:Bag>' . "\n"
. "\t\t\t" . '</dc:subject>' . "\n"
. "\t\t" . '</rdf:Description>' . "\n"
. "\t\t" . '<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">' . "\n"
. "\t\t\t" . '<xmp:CreateDate>' . $this->getXMPFormattedDate($this->doctime) . '</xmp:CreateDate>' . "\n"
. "\t\t\t" . '<xmp:CreatorTool>' . $this->getEscapedXML($this->creator) . '</xmp:CreatorTool>' . "\n"
. "\t\t\t" . '<xmp:ModifyDate>' . $this->getXMPFormattedDate($this->docmodtime) . '</xmp:ModifyDate>' . "\n"
. "\t\t\t" . '<xmp:MetadataDate>' . $this->getXMPFormattedDate($this->doctime) . '</xmp:MetadataDate>' . "\n"
. "\t\t" . '</rdf:Description>' . "\n"
. "\t\t" . '<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">' . "\n"
. "\t\t\t" . '<pdf:Keywords>' . $this->getEscapedXML($this->keywords) . '</pdf:Keywords>' . "\n"
. "\t\t\t" . '<pdf:Producer>' . $this->getEscapedXML($this->getProducer()) . '</pdf:Producer>' . "\n"
. "\t\t" . '</rdf:Description>' . "\n"
. "\t\t" . '<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">' . "\n"
. "\t\t\t" . '<xmpMM:DocumentID>' . $uuid . '</xmpMM:DocumentID>' . "\n"
. "\t\t\t" . '<xmpMM:InstanceID>' . $uuid . '</xmpMM:InstanceID>' . "\n"
. "\t\t" . '</rdf:Description>' . "\n";
if ($this->pdfa !== 0) {
$xmp .= ' <rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">' . "\n"
. "\t\t\t" . '<pdfaid:part>' . $this->pdfa . '</pdfaid:part>' . "\n"
. "\t\t\t" . '<pdfaid:conformance>B</pdfaid:conformance>' . "\n"
. "\t\t" . '</rdf:Description>' . "\n";
}
// XMP extension schemas
$xmp .= "\t\t" . '<rdf:Description rdf:about="" xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/" xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#" xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">' . "\n"
. "\t\t\t" . '<pdfaExtension:schemas>' . "\n"
. "\t\t\t\t" . '<rdf:Bag>' . "\n"
. "\t\t\t\t\t" . '<rdf:li rdf:parseType="Resource">' . "\n"
. "\t\t\t\t\t\t" . '<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>' . "\n"
. "\t\t\t\t\t\t" . '<pdfaSchema:prefix>pdf</pdfaSchema:prefix>' . "\n"
. "\t\t\t\t\t\t" . '<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>' . "\n"
. "\t\t\t\t\t" . '</rdf:li>' . "\n"
. "\t\t\t\t\t" . '<rdf:li rdf:parseType="Resource">' . "\n"
. "\t\t\t\t\t\t" . '<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>' . "\n"
. "\t\t\t\t\t\t" . '<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>' . "\n"
. "\t\t\t\t\t\t" . '<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>' . "\n"
. "\t\t\t\t\t\t" . '<pdfaSchema:property>' . "\n"
. "\t\t\t\t\t\t\t" . '<rdf:Seq>' . "\n"
. "\t\t\t\t\t\t\t\t" . '<rdf:li rdf:parseType="Resource">' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:category>internal</pdfaProperty:category>' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:name>InstanceID</pdfaProperty:name>' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:valueType>URI</pdfaProperty:valueType>' . "\n"
. "\t\t\t\t\t\t\t\t" . '</rdf:li>' . "\n"
. "\t\t\t\t\t\t\t" . '</rdf:Seq>' . "\n"
. "\t\t\t\t\t\t" . '</pdfaSchema:property>' . "\n"
. "\t\t\t\t\t" . '</rdf:li>' . "\n"
. "\t\t\t\t\t" . '<rdf:li rdf:parseType="Resource">' . "\n"
. "\t\t\t\t\t\t" . '<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>' . "\n"
. "\t\t\t\t\t\t" . '<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>' . "\n"
. "\t\t\t\t\t\t" . '<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>' . "\n"
. "\t\t\t\t\t\t" . '<pdfaSchema:property>' . "\n"
. "\t\t\t\t\t\t\t" . '<rdf:Seq>' . "\n"
. "\t\t\t\t\t\t\t\t" . '<rdf:li rdf:parseType="Resource">' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:category>internal</pdfaProperty:category>' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:name>part</pdfaProperty:name>' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:valueType>Integer</pdfaProperty:valueType>' . "\n"
. "\t\t\t\t\t\t\t\t" . '</rdf:li>' . "\n"
. "\t\t\t\t\t\t\t\t" . '<rdf:li rdf:parseType="Resource">' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:category>internal</pdfaProperty:category>' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:name>amd</pdfaProperty:name>' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:valueType>Text</pdfaProperty:valueType>' . "\n"
. "\t\t\t\t\t\t\t\t" . '</rdf:li>' . "\n"
. "\t\t\t\t\t\t\t\t" . '<rdf:li rdf:parseType="Resource">' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:category>internal</pdfaProperty:category>' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:name>conformance</pdfaProperty:name>' . "\n"
. "\t\t\t\t\t\t\t\t\t" . '<pdfaProperty:valueType>Text</pdfaProperty:valueType>' . "\n"
. "\t\t\t\t\t\t\t\t" . '</rdf:li>' . "\n"
. "\t\t\t\t\t\t\t" . '</rdf:Seq>' . "\n"
. "\t\t\t\t\t\t" . '</pdfaSchema:property>' . "\n"
. "\t\t\t\t\t" . '</rdf:li>' . "\n"
. $this->custom_xmp['x:xmpmeta.rdf:RDF.rdf:Description.pdfaExtension:schemas.rdf:Bag'] . "\n"
. "\t\t\t\t" . '</rdf:Bag>' . "\n"
. $this->custom_xmp['x:xmpmeta.rdf:RDF.rdf:Description.pdfaExtension:schemas'] . "\n"
. "\t\t\t" . '</pdfaExtension:schemas>' . "\n"
. $this->custom_xmp['x:xmpmeta.rdf:RDF.rdf:Description'] . "\n"
. "\t\t" . '</rdf:Description>' . "\n"
. $this->custom_xmp['x:xmpmeta.rdf:RDF'] . "\n"
. "\t" . '</rdf:RDF>' . "\n"
. $this->custom_xmp['x:xmpmeta'] . "\n"
. '</x:xmpmeta>' . "\n"
. '<?xpacket end="w"?>';
// @codingStandardsIgnoreEnd
$oid = ++$this->pon;
$this->objid['xmp'] = $oid;
return $oid . ' 0 obj' . "\n"
. '<<'
. ' /Type /Metadata'
. ' /Subtype /XML'
. ' /Length ' . strlen($xmp)
. ' >> stream' . "\n"
. $xmp . "\n"
. 'endstream' . "\n"
. 'endobj' . "\n";
}
/**
* Set the default document language direction.
*
* @param bool $enabled False = LTR = Left-To-Right; True = RTL = Right-To-Left.
*/
public function setRTL(bool $enabled): static
{
$this->rtl = $enabled;
return $this;
}
/**
* Set the viewer preferences dictionary
* controlling the way the document is to be presented on the screen or in print.
*
* @param TViewerPref $pref Array of options (see PDF reference "Viewer Preferences").
*/
public function setViewerPreferences(array $pref): static
{
$this->viewerpref = $pref;
return $this;
}
/**
* Sanitize the page box name and return the default 'CropBox' in case of error.
*
* @param string $name Entry name.
*/
protected function getPageBoxName(string $name): string
{
$box = 'CropBox';
if (isset($this->viewerpref[$name])) {
$val = $this->viewerpref[$name];
if (
isset($this->page->$box[$val]) // @phpstan-ignore offsetAccess.nonOffsetAccessible
&& is_string($this->page->$box[$val])
) {
$box = $this->page->$box[$val];
}
}
return ' /' . $name . ' /' . $box;
}
/**
* Sanitize the page box name and return the default 'CropBox' in case of error.
*/
protected function getPagePrintScaling(): string
{
$mode = 'AppDefault';
if (isset($this->viewerpref['PrintScaling'])) {
$name = strtolower($this->viewerpref['PrintScaling']);
$valid = [
'none' => 'None',
'appdefault' => 'AppDefault',
];
if (isset($valid[$name])) {
$mode = $valid[$name];
}
}
return ' /PrintScaling /' . $mode;
}
/**
* Returns the Duplex mode for the Viewer Preferences
*/
protected function getDuplexMode(): string
{
if (isset($this->viewerpref['Duplex'])) {
$name = strtolower($this->viewerpref['Duplex']);
$valid = [
'simplex' => 'Simplex',
'duplexflipshortedge' => 'DuplexFlipShortEdge',
'duplexfliplongedge' => 'DuplexFlipLongEdge',
];
if (isset($valid[$name])) {
return ' /Duplex /' . $valid[$name];
}
}
return '';
}
/**
* Returns the Viewer Preference boolean entry.
*
* @param string $name Entry name.
*/
protected function getBooleanMode(string $name): string
{
if (isset($this->viewerpref[$name])) {
return ' /' . $name . ' ' . ($this->viewerpref[$name] === true ? 'true' : 'false');
}
return '';
}
/**
* Returns the PDF viewer preferences for the catalog section
*/
protected function getOutViewerPref(): string
{
$vpr = $this->viewerpref;
$out = ' /ViewerPreferences <<';
if ($this->rtl) {
$out .= ' /Direction /R2L';
} else {
$out .= ' /Direction /L2R';
}
$out .= $this->getBooleanMode('HideToolbar');
$out .= $this->getBooleanMode('HideMenubar');
$out .= $this->getBooleanMode('HideWindowUI');
$out .= $this->getBooleanMode('FitWindow');
$out .= $this->getBooleanMode('CenterWindow');
$out .= $this->getBooleanMode('DisplayDocTitle');
if (isset($vpr['NonFullScreenPageMode'])) {
$out .= ' /NonFullScreenPageMode /' . $this->page->getDisplay($vpr['NonFullScreenPageMode']);
}
$out .= $this->getPageBoxName('ViewArea');
$out .= $this->getPageBoxName('ViewClip');
$out .= $this->getPageBoxName('PrintArea');
$out .= $this->getPageBoxName('PrintClip');
$out .= $this->getPagePrintScaling();
$out .= $this->getDuplexMode();
$out .= $this->getBooleanMode('PickTrayByPDFSize');
if (isset($vpr['PrintPageRange'])) {
$PrintPageRangeNum = '';
foreach ($vpr['PrintPageRange'] as $pnum) {
$PrintPageRangeNum .= ' ' . ($pnum - 1) . '';
}
$out .= ' /PrintPageRange [' . $PrintPageRangeNum . ' ]';
}
if (isset($vpr['NumCopies'])) {
$out .= ' /NumCopies ' . (int) $vpr['NumCopies'];
}
return $out . ' >>';
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.