Files
2026-08-30 22:02:02 +00:00

408 lines
14 KiB
PHP

<?php
/**
* BaseTest.php
*
* @since 2002-08-03
* @category Library
* @package Pdf
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2002-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
*
* This file is part of tc-lib-pdf software library.
*/
namespace Test;
use PHPUnit\Framework\Attributes\DataProvider;
class BaseTest extends TestUtil
{
private function invokeBaseMethod(object $obj, string $method, mixed ...$args): mixed
{
$ref = new \ReflectionClass($obj);
return $ref->getMethod($method)->invokeArgs($obj, $args);
}
/** @throws \Throwable */
protected function getTestObject(): \Com\Tecnick\Pdf\Tcpdf
{
return new \Com\Tecnick\Pdf\Tcpdf();
}
/** @throws \Throwable */
protected function getInternalTestObject(): TestableBase
{
return new TestableBase();
}
/** @throws \Throwable */
public function testToPointsAndToUnitRoundTrip(): void
{
$obj = $this->getTestObject();
$usr = 12.34;
$pnt = $obj->toPoints($usr);
$this->assertGreaterThan(0, $pnt);
$this->bcAssertEqualsWithDelta($usr, $obj->toUnit($pnt), 0.0001);
}
/** @throws \Throwable */
public function testToYPointsAndToYUnitWithExplicitPageHeight(): void
{
$obj = $this->getTestObject();
$usr = 10.0;
$pageh = 200.0;
$yPoints = $obj->toYPoints($usr, $pageh);
$this->bcAssertEqualsWithDelta($pageh - $obj->toPoints($usr), $yPoints, 0.0001);
$yUnit = $obj->toYUnit($yPoints, $pageh);
$this->bcAssertEqualsWithDelta($usr, $yUnit, 0.0001);
}
/** @throws \Throwable */
public function testEnableDefaultPageContentTogglesFlag(): void
{
$obj = $this->getTestObject();
$obj->enableDefaultPageContent(false);
$this->assertFalse($this->getObjectProperty($obj, 'defPageContentEnabled'));
$obj->enableDefaultPageContent(true);
$this->assertTrue($this->getObjectProperty($obj, 'defPageContentEnabled'));
}
/** @throws \Throwable */
public function testSetRTLReturnsSameInstanceAndSetsProperty(): void
{
$obj = $this->getTestObject();
$ret = $obj->setRTL(true);
$this->assertSame($obj, $ret);
$this->assertTrue($this->getObjectProperty($obj, 'rtl'));
$obj->setRTL(false);
$this->assertFalse($this->getObjectProperty($obj, 'rtl'));
}
/** @throws \Throwable */
#[DataProvider('unitValueConversionProvider')]
public function testGetUnitValuePointsConvertsCommonUnits(string $input, float $expected, float $delta): void
{
$obj = $this->getInternalTestObject();
$result = $obj->exposeGetUnitValuePoints($input);
$this->bcAssertEqualsWithDelta($expected, $result, $delta);
}
/** @throws \Throwable */
public function testGetUnitValuePointsConvertsRelativeAndViewportUnits(): void
{
$obj = $this->getInternalTestObject();
$ref = [
'parent' => 120.0,
'font' => ['size' => 12.0, 'xheight' => 5.0, 'zerowidth' => 6.0, 'rootsize' => 16.0],
'viewport' => ['width' => 300.0, 'height' => 200.0],
'page' => ['width' => 300.0, 'height' => 200.0],
];
$this->bcAssertEqualsWithDelta(12.0, $obj->exposeGetUnitValuePoints('2ch', $ref), 0.0001);
$this->bcAssertEqualsWithDelta(12.0, $obj->exposeGetUnitValuePoints('10%', $ref), 0.0001);
$this->bcAssertEqualsWithDelta(24.0, $obj->exposeGetUnitValuePoints('2em', $ref), 0.0001);
$this->bcAssertEqualsWithDelta(10.0, $obj->exposeGetUnitValuePoints('2ex', $ref), 0.0001);
$this->bcAssertEqualsWithDelta(24.0, $obj->exposeGetUnitValuePoints('2pc', $ref), 0.0001);
$this->bcAssertEqualsWithDelta(32.0, $obj->exposeGetUnitValuePoints('2rem', $ref), 0.0001);
$this->bcAssertEqualsWithDelta(20.0, $obj->exposeGetUnitValuePoints('10vh', $ref), 0.0001);
$this->bcAssertEqualsWithDelta(30.0, $obj->exposeGetUnitValuePoints('10vw', $ref), 0.0001);
$this->bcAssertEqualsWithDelta(30.0, $obj->exposeGetUnitValuePoints('10vmax', $ref), 0.0001);
$this->bcAssertEqualsWithDelta(20.0, $obj->exposeGetUnitValuePoints('10vmin', $ref), 0.0001);
}
/** @throws \Throwable */
public function testGetUnitValuePointsConvertsNumericDefault(): void
{
$obj = $this->getInternalTestObject();
$result = $obj->exposeGetUnitValuePoints(5.5);
$this->assertGreaterThan(0, $result);
}
/** @throws \Throwable */
public function testGetUnitValuePointsThrowsForInvalidValue(): void
{
$obj = $this->getInternalTestObject();
$this->bcExpectException(\Com\Tecnick\Pdf\Exception::class);
$obj->exposeGetUnitValuePoints('invalid!!!');
}
/** @throws \Throwable */
public function testGetFontValuePointsConvertsNamedFontSize(): void
{
$obj = $this->getInternalTestObject();
$ref = [
'parent' => 12.0,
'font' => ['size' => 12.0, 'xheight' => 1.0, 'zerowidth' => 1.0, 'rootsize' => 16.0],
'viewport' => ['width' => 800.0, 'height' => 600.0],
'page' => ['width' => 800.0, 'height' => 600.0],
];
$result = $obj->exposeGetFontValuePoints('larger', $ref);
// 'larger' should result in parent + larger factor
$this->assertGreaterThan($ref['parent'], $result);
}
/** @throws \Throwable */
public function testGetFontValuePointsDelegatesUnknownUnitToGetUnitValuePoints(): void
{
$obj = $this->getInternalTestObject();
$result = $obj->exposeGetFontValuePoints('10mm');
// 10 mm = 10 * 72 / 25.4 ≈ 28.35 points
$this->bcAssertEqualsWithDelta(28.35, $result, 0.1);
}
/** @throws \Throwable */
#[DataProvider('tmpRtlModeProvider')]
public function testSetTmpRTLWithMode(string $mode, bool $expectedRtl): void
{
$obj = $this->getInternalTestObject();
$obj->exposeSetTmpRTL($mode);
$this->assertSame($expectedRtl, $obj->exposeIsRTL());
}
/** @throws \Throwable */
#[DataProvider('isRtlStateProvider')]
public function testIsRTLReturnsExpectedState(bool $globalRtl, bool $expected): void
{
$obj = $this->getInternalTestObject();
$obj->setRTL($globalRtl);
$result = $obj->exposeIsRTL();
$this->assertSame($expected, $result);
}
/** @throws \Throwable */
public function testIsTransparencyAllowedReturnsTrueWhenPdfxDisabled(): void
{
$obj = $this->getInternalTestObject();
$this->setObjectProperty($obj, 'pdfx', false);
$this->setObjectProperty($obj, 'pdfxMode', 'pdfx1a');
$this->assertTrue($this->invokeBaseMethod($obj, 'isTransparencyAllowed') === true);
}
/** @throws \Throwable */
public function testIsTransparencyAllowedDependsOnPdfxMode(): void
{
$obj = $this->getInternalTestObject();
$this->setObjectProperty($obj, 'pdfx', true);
$this->setObjectProperty($obj, 'pdfxMode', 'pdfx4');
$this->assertTrue($this->invokeBaseMethod($obj, 'isTransparencyAllowed'));
$this->setObjectProperty($obj, 'pdfxMode', 'pdfx3');
$this->assertFalse($this->invokeBaseMethod($obj, 'isTransparencyAllowed'));
}
/** @throws \Throwable */
public function testRequiresPdfxDeviceCmykReturnsFalseWhenPdfxDisabled(): void
{
$obj = $this->getInternalTestObject();
$this->setObjectProperty($obj, 'pdfx', false);
$this->setObjectProperty($obj, 'pdfxMode', 'pdfx1a');
$this->assertFalse($this->invokeBaseMethod($obj, 'requiresPdfxDeviceCmyk'));
}
/** @throws \Throwable */
public function testRequiresPdfxDeviceCmykDependsOnPdfxMode(): void
{
$obj = $this->getInternalTestObject();
$this->setObjectProperty($obj, 'pdfx', true);
$this->setObjectProperty($obj, 'pdfxMode', 'pdfx3');
$this->assertTrue($this->invokeBaseMethod($obj, 'requiresPdfxDeviceCmyk'));
$this->setObjectProperty($obj, 'pdfxMode', 'pdfx5');
$this->assertFalse($this->invokeBaseMethod($obj, 'requiresPdfxDeviceCmyk'));
}
/** @throws \Throwable */
public function testDefaultFileAllowedPathsIncludesKnownRoots(): void
{
$obj = $this->getTestObject();
$paths = $obj->defaultFileAllowedPaths();
$tmpRoot = \realpath(\sys_get_temp_dir());
$pkgRoot = \realpath(__DIR__ . '/..');
$fontsRoot = \defined('K_PATH_FONTS') ? \realpath((string) \constant('K_PATH_FONTS')) : false;
if (\is_string($tmpRoot) && $tmpRoot !== '') {
$this->assertContains($tmpRoot, $paths);
}
if (\is_string($pkgRoot) && $pkgRoot !== '') {
$this->assertContains($pkgRoot, $paths);
}
if (\is_string($fontsRoot) && $fontsRoot !== '') {
$this->assertContains($fontsRoot, $paths);
}
}
/** @throws \Throwable */
public function testDefaultFileAllowedPathsReturnsUniqueNonEmptyResolvedPaths(): void
{
$obj = $this->getTestObject();
$paths = $obj->defaultFileAllowedPaths();
foreach ($paths as $path) {
$this->assertNotSame('', $path);
$this->assertSame($path, \realpath($path));
}
$this->assertCount(\count(\array_unique($paths)), $paths);
}
/** @throws \Throwable */
public function testDefaultMarkupAllowedPathsExcludeSystemTemp(): void
{
$obj = $this->getTestObject();
$paths = $obj->defaultMarkupAllowedPaths();
$tmpRoot = \realpath(\sys_get_temp_dir());
if (\is_string($tmpRoot) && $tmpRoot !== '') {
$this->assertNotContains($tmpRoot, $paths);
}
$pkgRoot = \realpath(__DIR__ . '/..');
if (\is_string($pkgRoot) && $pkgRoot !== '') {
$this->assertContains($pkgRoot, $paths);
}
}
/** @throws \Throwable */
public function testVendorSiblingPackagesPathDetectsComposerDependencyLayout(): void
{
$obj = $this->getTestObject();
$this->assertSame('/proj/vendor/tecnickcom', $this->invokeBaseMethod(
$obj,
'vendorSiblingPackagesPath',
'/proj/vendor/tecnickcom/tc-lib-pdf/src',
));
$this->assertNull($this->invokeBaseMethod(
$obj,
'vendorSiblingPackagesPath',
'/home/dev/github.com/tecnickcom/tc-lib-pdf/src',
));
$this->assertNull($this->invokeBaseMethod(
$obj,
'vendorSiblingPackagesPath',
'/proj/custom-deps/tecnickcom/tc-lib-pdf/src',
));
}
/** @throws \Throwable */
public function testDefaultFileAllowedPathsMatchVendorSiblingDetection(): void
{
$obj = $this->getTestObject();
$paths = $obj->defaultFileAllowedPaths();
$baseFileClass = new \ReflectionClass(\Com\Tecnick\Pdf\Base::class);
$baseFile = $baseFileClass->getFileName();
$this->assertIsString($baseFile);
$srcDir = \dirname($baseFile);
/** @var ?string $vendorSiblings */
$vendorSiblings = $this->invokeBaseMethod($obj, 'vendorSiblingPackagesPath', $srcDir);
if ($vendorSiblings === null) {
$siblingsDir = \realpath(\dirname($srcDir, 2));
if (\is_string($siblingsDir) && $siblingsDir !== '') {
$this->assertNotContains($siblingsDir, $paths);
}
return;
}
$this->assertIsString($vendorSiblings);
$resolvedVendorSiblings = \realpath($vendorSiblings);
if (\is_string($resolvedVendorSiblings) && $resolvedVendorSiblings !== '') {
$this->assertContains($resolvedVendorSiblings, $paths);
}
}
/** @throws \Throwable */
public function testAllowedRootsMatchWindowsStylePathsWithDriveLetters(): void
{
$probe = new class extends \Com\Tecnick\File\File {
/** @param array<string> $roots */
public function probeIsPathWithinAllowedRoots(string $path, array $roots): bool
{
return $this->isPathWithinAllowedRoots($path, $roots);
}
/**
* @param array<string> $roots
* @return array<string>
*/
public function probeNormalizeAllowedPaths(array $roots): array
{
return $this->normalizeAllowedPaths($roots);
}
};
$allowedRoot = 'C:\\webdev\\project\\storage\\app\\private\\pdf\\fonts';
$allowedPath = 'c:\\webdev\\project\\storage\\app\\private\\pdf\\fonts\\core\\helvetica.json';
$blockedPath = 'C:\\webdev\\project\\storage\\app\\private\\pdf\\fontsevil\\core\\helvetica.json';
$normalizedRoots = $probe->probeNormalizeAllowedPaths([$allowedRoot]);
$this->assertSame(['c:/webdev/project/storage/app/private/pdf/fonts'], $normalizedRoots);
$this->assertTrue($probe->probeIsPathWithinAllowedRoots($allowedPath, $normalizedRoots));
$this->assertFalse($probe->probeIsPathWithinAllowedRoots($blockedPath, $normalizedRoots));
}
/** @return array<string, array{0: string, 1: float, 2: float}> */
public static function unitValueConversionProvider(): array
{
return [
'px' => ['96px', 72.0, 0.1],
'pt' => ['12pt', 12.0, 0.0001],
'cm' => ['1cm', 28.35, 0.1],
'in' => ['1in', 72.0, 0.1],
];
}
/** @return array<string, array{0: string, 1: bool}> */
public static function tmpRtlModeProvider(): array
{
return [
'R_mode' => ['R', true],
'L_mode' => ['L', false],
'empty_mode' => ['', false],
];
}
/** @return array<string, array{0: bool, 1: bool}> */
public static function isRtlStateProvider(): array
{
return [
'default_false' => [false, false],
'global_true' => [true, true],
];
}
}