594 lines
22 KiB
PHP
594 lines
22 KiB
PHP
<?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 (< > &) for XML output.
|
|
*
|
|
* @param string $str Input string to escape.
|
|
*/
|
|
protected function getEscapedXML(string $str): string
|
|
{
|
|
return strtr($str, [
|
|
"\0" => '',
|
|
'&' => '&',
|
|
'<' => '<',
|
|
'>' => '>',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 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 . ' >>';
|
|
}
|
|
}
|