Files
putnamrec/vendor/tecnickcom/tc-lib-pdf-font/src/Buffer.php
T
2026-08-20 11:41:17 +00:00

343 lines
11 KiB
PHP

<?php
declare(strict_types=1);
/**
* Buffer.php
*
* @since 2011-05-23
* @category Library
* @package PdfFont
* @author Nicola Asuni <info@tecnick.com>
* @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-font
*
* This file is part of tc-lib-pdf-font software library.
*/
namespace Com\Tecnick\Pdf\Font;
use Com\Tecnick\File\File as ObjFile;
use Com\Tecnick\Pdf\Font\Exception as FontException;
/**
* Com\Tecnick\Pdf\Font\Buffer
*
* @since 2011-05-23
* @category Library
* @package PdfFont
* @author Nicola Asuni <info@tecnick.com>
* @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-font
*
* @phpstan-type TFileOptions array{
* allowedHosts?: array<string>,
* maxRemoteSize?: int,
* curlopts?: array<int, bool|int|string>,
* defaultCurlOpts?: array<int, bool|int|string>,
* fixedCurlOpts?: array<int, bool|int|string>
* }
*
* @phpstan-import-type TFontData from Load
*/
abstract class Buffer
{
/**
* Array containing all fonts data
*
* @var array<string, TFontData>
*/
protected array $font = [];
/**
* Font counter
*/
protected int $numfonts = 0;
/**
* Cache mapping a (font family, style) pair to its resolved font key, so repeated
* lookups of an already-loaded font skip constructing a throwaway Font object.
*
* @var array<string, array<string, string>>
*/
protected array $fontKeyCache = [];
/**
* Array containing encoding differences
*
* @var array<int, string>
*/
protected array $encdiff = [];
/**
* Index for Encoding differences
*/
protected int $numdiffs = 0;
/**
* Array containing font definitions grouped by file
*
* @var array<string, array{
* 'dir': string,
* 'keys': array<string>,
* 'length1': int,
* 'length2': int,
* 'subset': bool,
* }>
*/
protected array $file = [];
/**
* Optional file helper forwarded to font loaders.
*
* @var ObjFile|null
*/
protected ?ObjFile $fileHelper;
/**
* Initialize fonts buffer
*
* @param float $kunit Unit of measure conversion ratio.
* @param bool $subset If true embed only a subset of the fonts
* (stores only the information related to
* the used characters); If false embed
* full font; This option is valid only for
* TrueTypeUnicode fonts and is disabled
* for PDF/A. If you want to enable users to
* modify the document, set this parameter
* to false. If you subset the font, the
* person who receives your PDF would need
* to have your same font in order to make
* changes to your PDF. The file size of the
* PDF would also be smaller because you are
* embedding only a subset. NOTE: This
* option is computational and memory
* intensive.
* @param bool $unicode True if we are in Unicode mode, False otherwise.
* @param bool $pdfa True if we are in PDF/A mode, False otherwise.
* @param ObjFile|null $fileHelper Optional file helper for font loading.
*/
public function __construct(
protected float $kunit,
protected bool $subset = false,
protected bool $unicode = true,
protected bool $pdfa = false,
?ObjFile $fileHelper = null,
) {
$this->fileHelper = $fileHelper;
}
/**
* Get the default subset mode
*/
public function isSubsetMode(): bool
{
return $this->subset;
}
/**
* Returns the fonts buffer
*
* @return array<string, TFontData>
*/
public function getFonts(): array
{
return $this->font;
}
/**
* Returns the fonts buffer
*
* @return array<int, string>
*/
public function getEncDiffs(): array
{
return $this->encdiff;
}
/**
* Returns true if the specified font key exist on buffer
*
* @param string $key Font key
*/
public function isValidKey(string $key): bool
{
return isset($this->font[$key]);
}
/**
* Get font by key
*
* @param string $key Font key
*
* @return TFontData Returns the fonts array.
*
* @throws FontException in case of error
*/
public function getFont(string $key): array
{
if (!isset($this->font[$key])) {
throw new FontException('The font ' . $key . ' has not been loaded');
}
return $this->font[$key];
}
/**
* Add a character to the subset list
*
* @param string $key The font key
* @param int $char The Unicode character value to add
*
* @throws FontException
*/
public function addSubsetChar(string $key, int $char): void
{
if (!isset($this->font[$key])) {
throw new FontException('The font ' . $key . ' has not been loaded');
}
$this->font[$key]['subsetchars'][$char] = true;
}
/**
* Add a new font to the fonts buffer
*
* The definition file (and the font file itself when embedding) must be present either in the current directory
* or in the one indicated by K_PATH_FONTS if the constant is defined.
*
* @param int $objnum Current PDF object number
* @param string $font Font family.
* If it is a standard family name, it will override the corresponding font.
* @param string $style Font style.
* Possible values are (case-insensitive):
* regular (default)
* B: bold
* I: italic
* U: underline
* D: strikeout (linethrough)
* O: overline
* @param string $ifile The font definition file (or empty for autodetect).
* By default, the name is built from the family and style, in lower case with no spaces.
* @param ?bool $subset If true embed only a subset of the font
* (stores only the information related to
* the used characters); If false embed
* full font; This option is valid only
* for TrueTypeUnicode fonts and is
* disabled for PDF/A. If you want to
* enable users to modify the document,
* set this parameter to false. If you
* subset the font, the person who
* receives your PDF would need to have
* your same font in order to make changes
* to your PDF. The file size of the PDF
* would also be smaller because you are
* embedding only a subset. Set this to
* null to use the default value. NOTE:
* This option is computational and memory
* intensive.
*
* @return string Font key
*
* @throws FontException in case of error
*/
public function add(
int &$objnum,
string $font,
string $style = '',
string $ifile = '',
?bool $subset = null,
): string {
if ($subset === null) {
$subset = $this->subset;
}
// The font key depends only on (family, style, unicode, pdfa) - all known without
// constructing a Font. When autodetecting the definition file (ifile === '') and the
// resolved font is already loaded, skip the expensive Font allocation.
if ($ifile === '' && isset($this->fontKeyCache[$font][$style])) {
$cachedKey = $this->fontKeyCache[$font][$style];
if (isset($this->font[$cachedKey])) {
return $cachedKey;
}
}
$fobj = new Font($font, $style, $ifile, $subset, $this->unicode, $this->pdfa, true, $this->fileHelper);
$key = $fobj->getFontkey();
if ($ifile === '') {
$this->fontKeyCache[$font][$style] = $key;
}
if (isset($this->font[$key])) {
return $key;
}
$fobj->load();
$this->font[$key] = $fobj->getFontData();
$this->setFontFile($key);
$this->setFontDiff($key);
$this->font[$key]['i'] = ++$this->numfonts;
$this->font[$key]['n'] = ++$objnum; // @phpstan-ignore assign.propertyType
return $key;
}
/**
* Set font file and subset
*
* @param string $key Font key
*/
protected function setFontFile(string $key): void
{
if ($this->font[$key]['file'] === '') {
return;
}
$file = $this->font[$key]['file'];
if (!isset($this->file[$file])) {
$this->file[$file] = [
'dir' => '',
'keys' => [],
'length1' => 0,
'length2' => 0,
// a shared font file may only be subset if every font referencing it is subset
'subset' => $this->font[$key]['subset'],
];
} else {
$this->file[$file]['subset'] = $this->file[$file]['subset'] && $this->font[$key]['subset'];
}
if (!\in_array($key, $this->file[$file]['keys'], true)) {
$this->file[$file]['keys'][] = $key;
}
$this->file[$file]['dir'] = $this->font[$key]['dir'];
$this->file[$file]['length1'] = $this->font[$key]['length1'];
$this->file[$file]['length2'] = $this->font[$key]['length2'];
}
/**
* Set font diff
*
* @param string $key Font key
*/
protected function setFontDiff(string $key): void
{
if ($this->font[$key]['diff'] === '') {
return;
}
$diffid = \array_search($this->font[$key]['diff'], $this->encdiff, true);
if ($diffid === false) {
$diffid = ++$this->numdiffs;
$this->encdiff[$diffid] = $this->font[$key]['diff'];
}
$this->font[$key]['diffid'] = $diffid;
}
}