diff --git a/bin/pdf2images.cmd b/bin/pdf2images.cmd index e2fa733..c7ef5ac 100644 --- a/bin/pdf2images.cmd +++ b/bin/pdf2images.cmd @@ -1 +1,8 @@ -find.exe ./tests/_files -type f -name "*.pdf" | xargs -n1 sh -c 'magick -verbose -density 300 "$0[0]" -background white -flatten "${0%%.pdf}.png"' +@echo off +setlocal + +pushd "%~dp0.." +for /r "tests\_files" %%F in (*.pdf) do ( + magick -verbose -density 300 "%%~fF[0]" -background white -flatten "%%~dpnF.png" +) +popd diff --git a/bin/pdf2images.ps1 b/bin/pdf2images.ps1 new file mode 100644 index 0000000..0fa2869 --- /dev/null +++ b/bin/pdf2images.ps1 @@ -0,0 +1,67 @@ +param( + [string]$Root = "tests/_files", + [int]$Density = 300, + [switch]$VerboseMagick, + [switch]$FirstPageOnly = $true +) + +$ErrorActionPreference = 'Stop' + +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$repoRoot = Resolve-Path (Join-Path $scriptDir "..") +Set-Location $repoRoot + +Write-Host "Working directory: $repoRoot" -ForegroundColor Cyan + +if (-not (Get-Command magick -ErrorAction SilentlyContinue)) { + throw "ImageMagick 'magick' was not found in PATH." +} + +if (-not (Test-Path $Root)) { + throw "Root path '$Root' does not exist." +} + +$files = @(Get-ChildItem -Path $Root -Filter *.pdf -Recurse -File) +$total = $files.Count + +if ($total -eq 0) { + Write-Host "No PDF files found in '$Root'." -ForegroundColor Yellow + exit 0 +} + +Write-Host "Found $total PDF file(s) in '$Root'. Converting..." -ForegroundColor Cyan + +$index = 0 +foreach ($file in $files) { + $index++ + $pdf = $file.FullName + $png = [System.IO.Path]::ChangeExtension($pdf, 'png') + + Write-Host "[$index/$total] $($file.Name)" -NoNewline + + $source = if ($FirstPageOnly) { "$pdf`[0`]" } else { $pdf } + $magickArgs = @() + + if ($VerboseMagick) { + $magickArgs += '-verbose' + } + + $magickArgs += @( + '-density', $Density, + $source, + '-background', 'white', + '-flatten', + $png + ) + + & magick @magickArgs + if ($LASTEXITCODE -ne 0) { + Write-Host " [FAILED]" -ForegroundColor Red + throw "ImageMagick failed for '$pdf'" + } + + Write-Host " -> $([System.IO.Path]::GetFileName($png))" -ForegroundColor Green +} + +Write-Host "" +Write-Host "Done. $total file(s) converted." -ForegroundColor Cyan diff --git a/composer.lock b/composer.lock index 2d4a869..343fec6 100644 --- a/composer.lock +++ b/composer.lock @@ -154,30 +154,30 @@ }, { "name": "doctrine/instantiator", - "version": "2.0.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11", + "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { @@ -204,7 +204,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { @@ -220,20 +220,20 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:23:10+00:00" + "time": "2022-12-30T00:15:36+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.12.1", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -272,7 +272,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -280,29 +280,31 @@ "type": "tidelift" } ], - "time": "2024-11-08T17:47:46+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v4.19.4", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/715f4d25e225bc47b293a8b997fe6ce99bf987d2", - "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.1" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -310,7 +312,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -334,9 +336,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2024-09-29T15:01:53+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "pdepend/pdepend", @@ -604,33 +606,29 @@ }, { "name": "phpmetrics/phpmetrics", - "version": "v2.8.2", + "version": "v2.9.1", "source": { "type": "git", "url": "https://github.com/phpmetrics/PhpMetrics.git", - "reference": "4b77140a11452e63c7a9b98e0648320bf6710090" + "reference": "e2e68ddd1543bc3f44402c383f7bccb62de1ece3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmetrics/PhpMetrics/zipball/4b77140a11452e63c7a9b98e0648320bf6710090", - "reference": "4b77140a11452e63c7a9b98e0648320bf6710090", + "url": "https://api.github.com/repos/phpmetrics/PhpMetrics/zipball/e2e68ddd1543bc3f44402c383f7bccb62de1ece3", + "reference": "e2e68ddd1543bc3f44402c383f7bccb62de1ece3", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", - "nikic/php-parser": "^3|^4", - "php": ">=5.5" + "nikic/php-parser": "^3|^4|^5" }, "replace": { "halleck45/php-metrics": "*", "halleck45/phpmetrics": "*" }, "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14", - "sebastian/comparator": ">=1.2.3", - "squizlabs/php_codesniffer": "^3.5", - "symfony/dom-crawler": "^3.0 || ^4.0 || ^5.0" + "phpunit/phpunit": "*" }, "bin": [ "bin/phpmetrics" @@ -666,22 +664,28 @@ ], "support": { "issues": "https://github.com/PhpMetrics/PhpMetrics/issues", - "source": "https://github.com/phpmetrics/PhpMetrics/tree/v2.8.2" + "source": "https://github.com/phpmetrics/PhpMetrics/tree/v2.9.1" }, - "time": "2023-03-08T15:03:36+00:00" + "funding": [ + { + "url": "https://github.com/Halleck45", + "type": "github" + } + ], + "time": "2025-09-25T05:21:02+00:00" }, { "name": "phpoption/phpoption", - "version": "1.9.3", + "version": "1.9.5", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", "shasum": "" }, "require": { @@ -689,7 +693,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" }, "type": "library", "extra": { @@ -731,7 +735,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.5" }, "funding": [ { @@ -743,20 +747,15 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:41:07+00:00" + "time": "2025-12-27T19:41:33+00:00" }, { "name": "phpstan/phpstan", - "version": "1.12.16", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "e0bb5cb78545aae631220735aa706eac633a6be9" - }, + "version": "1.12.33", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e0bb5cb78545aae631220735aa706eac633a6be9", - "reference": "e0bb5cb78545aae631220735aa706eac633a6be9", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/37982d6fc7cbb746dda7773530cda557cdf119e1", + "reference": "37982d6fc7cbb746dda7773530cda557cdf119e1", "shasum": "" }, "require": { @@ -801,7 +800,7 @@ "type": "github" } ], - "time": "2025-01-21T14:50:05+00:00" + "time": "2026-02-28T20:30:03+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1124,16 +1123,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.22", + "version": "9.6.34", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c" + "reference": "b36f02317466907a230d3aa1d34467041271ef4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c", - "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b36f02317466907a230d3aa1d34467041271ef4a", + "reference": "b36f02317466907a230d3aa1d34467041271ef4a", "shasum": "" }, "require": { @@ -1144,7 +1143,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -1155,11 +1154,11 @@ "phpunit/php-timer": "^5.0.3", "sebastian/cli-parser": "^1.0.2", "sebastian/code-unit": "^1.0.8", - "sebastian/comparator": "^4.0.8", + "sebastian/comparator": "^4.0.10", "sebastian/diff": "^4.0.6", "sebastian/environment": "^5.1.5", - "sebastian/exporter": "^4.0.6", - "sebastian/global-state": "^5.0.7", + "sebastian/exporter": "^4.0.8", + "sebastian/global-state": "^5.0.8", "sebastian/object-enumerator": "^4.0.4", "sebastian/resource-operations": "^3.0.4", "sebastian/type": "^3.2.1", @@ -1207,7 +1206,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.22" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.34" }, "funding": [ { @@ -1218,12 +1217,20 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2024-12-05T13:48:26+00:00" + "time": "2026-01-27T05:45:00+00:00" }, { "name": "psr/container", @@ -1497,16 +1504,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d", + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d", "shasum": "" }, "require": { @@ -1559,15 +1566,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.10" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2026-01-24T09:22:56+00:00" }, { "name": "sebastian/complexity", @@ -1757,16 +1776,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", "shasum": "" }, "require": { @@ -1822,28 +1841,40 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-03-02T06:33:00+00:00" + "time": "2025-09-24T06:03:27+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "5.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", "shasum": "" }, "require": { @@ -1886,15 +1917,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2024-03-02T06:35:11+00:00" + "time": "2025-08-10T07:10:35+00:00" }, { "name": "sebastian/lines-of-code", @@ -2067,16 +2110,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", "shasum": "" }, "require": { @@ -2118,15 +2161,27 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T06:07:39+00:00" + "time": "2025-08-10T06:57:39+00:00" }, { "name": "sebastian/resource-operations", @@ -2293,16 +2348,16 @@ }, { "name": "setasign/fpdf", - "version": "1.8.6", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/Setasign/FPDF.git", - "reference": "0838e0ee4925716fcbbc50ad9e1799b5edfae0a0" + "reference": "051b70e4c57dedc88df41b1eff1c62894e5f9ed0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Setasign/FPDF/zipball/0838e0ee4925716fcbbc50ad9e1799b5edfae0a0", - "reference": "0838e0ee4925716fcbbc50ad9e1799b5edfae0a0", + "url": "https://api.github.com/repos/Setasign/FPDF/zipball/051b70e4c57dedc88df41b1eff1c62894e5f9ed0", + "reference": "051b70e4c57dedc88df41b1eff1c62894e5f9ed0", "shasum": "" }, "require": { @@ -2333,9 +2388,9 @@ "pdf" ], "support": { - "source": "https://github.com/Setasign/FPDF/tree/1.8.6" + "source": "https://github.com/Setasign/FPDF/tree/1.9.0" }, - "time": "2023-06-26T14:44:25+00:00" + "time": "2026-05-31T14:25:29+00:00" }, { "name": "setasign/tfpdf", @@ -2393,37 +2448,32 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.11.2", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "1368f4a58c3c52114b86b1abe8f4098869cb0079" + "reference": "0525c73950de35ded110cffafb9892946d7771b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/1368f4a58c3c52114b86b1abe8f4098869cb0079", - "reference": "1368f4a58c3c52114b86b1abe8f4098869cb0079", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0525c73950de35ded110cffafb9892946d7771b5", + "reference": "0525c73950de35ded110cffafb9892946d7771b5", "shasum": "" }, "require": { "ext-simplexml": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": ">=5.4.0" + "php": ">=7.2.0" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + "phpunit/phpunit": "^8.4.0 || ^9.3.4 || ^10.5.32 || 11.3.3 - 11.5.28 || ^11.5.31" }, "bin": [ "bin/phpcbf", "bin/phpcs" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" @@ -2442,7 +2492,7 @@ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" } ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "description": "PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.", "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", "keywords": [ "phpcs", @@ -2467,40 +2517,47 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" } ], - "time": "2024-12-11T16:04:26+00:00" + "time": "2025-11-10T16:43:36+00:00" }, { "name": "symfony/config", - "version": "v7.2.0", + "version": "v6.0.19", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "bcd3c4adf0144dee5011bb35454728c38adec055" + "reference": "db4fc45c24e0c3e2198e68ada9d7f90daa1f97e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/bcd3c4adf0144dee5011bb35454728c38adec055", - "reference": "bcd3c4adf0144dee5011bb35454728c38adec055", + "url": "https://api.github.com/repos/symfony/config/zipball/db4fc45c24e0c3e2198e68ada9d7f90daa1f97e3", + "reference": "db4fc45c24e0c3e2198e68ada9d7f90daa1f97e3", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/filesystem": "^7.1", - "symfony/polyfill-ctype": "~1.8" + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/filesystem": "^5.4|^6.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php81": "^1.22" }, "conflict": { - "symfony/finder": "<6.4", - "symfony/service-contracts": "<2.5" + "symfony/finder": "<4.4" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0" + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" }, "type": "library", "autoload": { @@ -2528,7 +2585,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.2.0" + "source": "https://github.com/symfony/config/tree/v6.0.19" }, "funding": [ { @@ -2544,43 +2601,51 @@ "type": "tidelift" } ], - "time": "2024-11-04T11:36:24+00:00" + "time": "2023-01-09T04:36:00+00:00" }, { "name": "symfony/dependency-injection", - "version": "v7.2.0", + "version": "v6.0.20", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "a475747af1a1c98272a5471abc35f3da81197c5d" + "reference": "359806e1adebd1c43e18e5ea22acd14bef7fcf8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/a475747af1a1c98272a5471abc35f3da81197c5d", - "reference": "a475747af1a1c98272a5471abc35f3da81197c5d", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/359806e1adebd1c43e18e5ea22acd14bef7fcf8c", + "reference": "359806e1adebd1c43e18e5ea22acd14bef7fcf8c", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.0.2", "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/service-contracts": "^3.5", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php81": "^1.22", + "symfony/service-contracts": "^1.1.6|^2.0|^3.0" }, "conflict": { "ext-psr": "<1.1|>=2", - "symfony/config": "<6.4", - "symfony/finder": "<6.4", - "symfony/yaml": "<6.4" + "symfony/config": "<5.4", + "symfony/finder": "<5.4", + "symfony/proxy-manager-bridge": "<5.4", + "symfony/yaml": "<5.4" }, "provide": { "psr/container-implementation": "1.1|2.0", "symfony/service-implementation": "1.1|2.0|3.0" }, "require-dev": { - "symfony/config": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "symfony/config": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" }, "type": "library", "autoload": { @@ -2608,7 +2673,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.2.0" + "source": "https://github.com/symfony/dependency-injection/tree/v6.0.20" }, "funding": [ { @@ -2624,24 +2689,24 @@ "type": "tidelift" } ], - "time": "2024-11-25T15:45:00+00:00" + "time": "2023-01-30T15:41:07+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.1", + "version": "v3.0.2", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.0.2" }, "type": "library", "extra": { @@ -2650,7 +2715,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -2675,7 +2740,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2" }, "funding": [ { @@ -2691,30 +2756,27 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/filesystem", - "version": "v7.2.0", + "version": "v6.0.19", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/3d49eec03fda1f0fc19b7349fbbe55ebc1004214", + "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.0.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, - "require-dev": { - "symfony/process": "^6.4|^7.0" - }, "type": "library", "autoload": { "psr-4": { @@ -2741,7 +2803,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.2.0" + "source": "https://github.com/symfony/filesystem/tree/v6.0.19" }, "funding": [ { @@ -2757,20 +2819,20 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:15:23+00:00" + "time": "2023-01-20T17:44:14+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.31.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { @@ -2820,7 +2882,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { @@ -2831,28 +2893,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/14c5439eec4ccff081ac14eca2dc57feb2a66d92", + "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { @@ -2900,7 +2967,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.1" }, "funding": [ { @@ -2911,25 +2978,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.31.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { @@ -2980,7 +3051,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" }, "funding": [ { @@ -2991,38 +3062,50 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { - "name": "symfony/process", - "version": "v5.4.47", + "name": "symfony/polyfill-php81", + "version": "v1.38.1", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "6bfb9c766cacffbc8e118cb87217d08ed84e5cd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/5d1662fb32ebc94f17ddb8d635454a776066733d", - "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/6bfb9c766cacffbc8e118cb87217d08ed84e5cd7", + "reference": "6bfb9c766cacffbc8e118cb87217d08ed84e5cd7", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" + "php": ">=7.2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Process\\": "" + "Symfony\\Polyfill\\Php81\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3031,18 +3114,24 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Executes commands in sub-processes", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/process/tree/v5.4.47" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.38.1" }, "funding": [ { @@ -3053,51 +3142,42 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-06T11:36:42+00:00" + "time": "2026-05-26T12:45:58+00:00" }, { - "name": "symfony/service-contracts", - "version": "v3.5.1", + "name": "symfony/process", + "version": "v5.4.51", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + "url": "https://github.com/symfony/process.git", + "reference": "467bfc56f18f5ef6d5ccb09324d7e988c1c0a98f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "url": "https://api.github.com/repos/symfony/process/zipball/467bfc56f18f5ef6d5ccb09324d7e988c1c0a98f", + "reference": "467bfc56f18f5ef6d5ccb09324d7e988c1c0a98f", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" }, "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.5-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Contracts\\Service\\": "" + "Symfony\\Component\\Process\\": "" }, "exclude-from-classmap": [ - "/Test/" + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3106,26 +3186,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to writing services", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/process/tree/v5.4.51" }, "funding": [ { @@ -3136,43 +3208,55 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2026-01-26T15:53:37+00:00" }, { - "name": "symfony/var-exporter", - "version": "v7.2.0", + "name": "symfony/service-contracts", + "version": "v3.0.2", "source": { "type": "git", - "url": "https://github.com/symfony/var-exporter.git", - "reference": "1a6a89f95a46af0f142874c9d650a6358d13070d" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/1a6a89f95a46af0f142874c9d650a6358d13070d", - "reference": "1a6a89f95a46af0f142874c9d650a6358d13070d", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d78d39c1599bd1188b8e26bb341da52c3c6d8a66", + "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.0.2", + "psr/container": "^2.0" }, - "require-dev": { - "symfony/property-access": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.0-dev" + } + }, "autoload": { "psr-4": { - "Symfony\\Component\\VarExporter\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Contracts\\Service\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3188,20 +3272,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "description": "Generic abstractions related to writing services", "homepage": "https://symfony.com", "keywords": [ - "clone", - "construct", - "export", - "hydrate", - "instantiate", - "lazy-loading", - "proxy", - "serialize" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.2.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.0.2" }, "funding": [ { @@ -3217,34 +3299,38 @@ "type": "tidelift" } ], - "time": "2024-10-18T07:58:17+00:00" + "time": "2022-05-30T19:17:58+00:00" }, { "name": "tecnickcom/tcpdf", - "version": "6.8.0", + "version": "6.11.3", "source": { "type": "git", "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "14ffa0e308f5634aa2489568b4b90b24073b6731" + "reference": "b18f6119161019916c5bb07cb8da5205ae5c1b63" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/14ffa0e308f5634aa2489568b4b90b24073b6731", - "reference": "14ffa0e308f5634aa2489568b4b90b24073b6731", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/b18f6119161019916c5bb07cb8da5205ae5c1b63", + "reference": "b18f6119161019916c5bb07cb8da5205ae5c1b63", "shasum": "" }, "require": { "ext-curl": "*", "php": ">=7.1.0" }, + "suggest": { + "ext-gd": "Enables additional image handling in some workflows.", + "ext-imagick": "Enables additional image format support when available.", + "ext-zlib": "Recommended for compressed streams and related features.", + "tecnickcom/tc-lib-pdf": "Modern replacement for TCPDF for new projects." + }, "type": "library", "autoload": { "classmap": [ "config", "include", "tcpdf.php", - "tcpdf_parser.php", - "tcpdf_import.php", "tcpdf_barcodes_1d.php", "tcpdf_barcodes_2d.php", "include/tcpdf_colors.php", @@ -3269,8 +3355,8 @@ "role": "lead" } ], - "description": "TCPDF is a PHP class for generating PDF documents and barcodes.", - "homepage": "http://www.tcpdf.org/", + "description": "Deprecated legacy PDF engine for PHP. For new projects use tecnickcom/tc-lib-pdf.", + "homepage": "https://tcpdf.org", "keywords": [ "PDFD32000-2008", "TCPDF", @@ -3282,28 +3368,28 @@ ], "support": { "issues": "https://github.com/tecnickcom/TCPDF/issues", - "source": "https://github.com/tecnickcom/TCPDF/tree/6.8.0" + "source": "https://github.com/tecnickcom/TCPDF" }, "funding": [ { - "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_donations¤cy_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20tcpdf%20project", - "type": "custom" + "url": "https://www.paypal.com/donate/?hosted_button_id=NZUEC5XS8MFBJ", + "type": "paypal" } ], - "time": "2024-12-23T13:34:57+00:00" + "time": "2026-04-21T17:00:18+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -3332,7 +3418,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -3340,7 +3426,7 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" }, { "name": "vlucas/phpdotenv", @@ -3438,5 +3524,5 @@ "ext-json": "*", "ext-fileinfo": "*" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/dev/example-table-bottom-border-multiline.pdf b/dev/example-table-bottom-border-multiline.pdf new file mode 100644 index 0000000..2060c37 Binary files /dev/null and b/dev/example-table-bottom-border-multiline.pdf differ diff --git a/dev/example-table-bottom-border-multiline.php b/dev/example-table-bottom-border-multiline.php new file mode 100644 index 0000000..c321912 --- /dev/null +++ b/dev/example-table-bottom-border-multiline.php @@ -0,0 +1,108 @@ +SetAuthor('EvoSys21'); +$pdf->SetMargins(20, 20, 20); +$pdf->SetAutoPageBreak(true, 20); +$pdf->SetFont('helvetica', '', 10); +$pdf->SetTextColor(0, 0, 0); +$pdf->AddPage(); + +$table = new Table($pdf); +$table->setStyle('default', 10, '', [0, 0, 0], 'helvetica'); +$table->setStyle('p'); +$table->setStyle('b', null, 'B'); + +$table->initialize([ + 18, + 75, + 77, +], [ + 'TABLE' => [ + 'TABLE_ALIGN' => 'L', + 'TABLE_LEFT_MARGIN' => 10, + 'BORDER_TYPE' => 0, + 'BORDER_SIZE' => 0.3, + 'BORDER_COLOR' => [0, 0, 0], + ], + 'HEADER' => [ + 'TEXT_COLOR' => [0, 0, 0], + 'TEXT_SIZE' => 10, + 'TEXT_FONT' => 'helvetica', + 'TEXT_ALIGN' => 'L', + 'VERTICAL_ALIGN' => 'M', + 'TEXT_TYPE' => 'B', + 'LINE_SIZE' => 5, + 'BACKGROUND_COLOR' => [255, 255, 255], + 'BORDER_COLOR' => [0, 0, 0], + 'BORDER_SIZE' => 0.3, + 'BORDER_TYPE' => 'B', + 'TEXT' => ' ', + 'PADDING_TOP' => 2, + 'PADDING_RIGHT' => 2, + 'PADDING_LEFT' => 2, + 'PADDING_BOTTOM' => 2, + ], + 'ROW' => [ + 'TEXT_COLOR' => [0, 0, 0], + 'TEXT_SIZE' => 10, + 'TEXT_FONT' => 'helvetica', + 'TEXT_ALIGN' => 'L', + 'VERTICAL_ALIGN' => 'T', + 'TEXT_TYPE' => '', + 'LINE_SIZE' => 5, + 'BACKGROUND_COLOR' => [255, 255, 255], + 'BORDER_COLOR' => [0, 0, 0], + 'BORDER_SIZE' => 0.3, + 'BORDER_TYPE' => 'B', + 'TEXT' => ' ', + 'PADDING_TOP' => 2, + 'PADDING_RIGHT' => 2, + 'PADDING_LEFT' => 2, + 'PADDING_BOTTOM' => 2, + ], +]); + +$table->addHeader([ + ['TEXT' => 'ID'], + ['TEXT' => 'Description'], + ['TEXT' => 'Notes'], +]); + +$table->addRow([ + ['TEXT' => '01', 'TEXT_ALIGN' => 'C'], + ['TEXT' => 'This row demonstrates a wrapped description that spans multiple lines while keeping only the bottom border visible.', 'TEXT_ALIGN' => 'L'], + ['TEXT' => 'Bottom border only', 'TEXT_ALIGN' => 'L'], +]); + +$table->addRow([ + ['TEXT' => '02', 'TEXT_ALIGN' => 'C'], + ['TEXT' => 'Short text on the left, but the middle cell is long enough to wrap naturally and prove the border stays at the bottom.', 'TEXT_ALIGN' => 'L'], + ['TEXT' => "First line\nSecond line\nThird line", 'TEXT_ALIGN' => 'L'], +]); + +$table->addRow([ + ['TEXT' => '03', 'TEXT_ALIGN' => 'C'], + ['TEXT' => 'Another multiline example with enough words to create a tall row in the description column.', 'TEXT_ALIGN' => 'L'], + ['TEXT' => 'The whole table uses only bottom borders for each cell.', 'TEXT_ALIGN' => 'L'], +]); + +$table->close(); + +if (PHP_SAPI === 'cli') { + $outputFile = __DIR__ . '/example-table-bottom-border-multiline.pdf'; + $pdf->Output('F', $outputFile); + echo "PDF written to: {$outputFile}\n"; + return; +} + +$pdf->Output(); + diff --git a/dev/example-table-bottom-border-multiline1.php b/dev/example-table-bottom-border-multiline1.php new file mode 100644 index 0000000..f28b641 --- /dev/null +++ b/dev/example-table-bottom-border-multiline1.php @@ -0,0 +1,108 @@ +SetAuthor('EvoSys21'); +$pdf->SetMargins(20, 20, 20); +$pdf->SetAutoPageBreak(true, 20); +$pdf->SetFont('helvetica', '', 10); +$pdf->SetTextColor(0, 0, 0); +$pdf->AddPage(); + +$table = new Table($pdf); +$table->setStyle('default', 10, '', [0, 0, 0], 'helvetica'); +$table->setStyle('p'); +$table->setStyle('b', null, 'B'); + +$table->initialize([ + 18, + 75, + 77, +], [ + 'TABLE' => [ + 'TABLE_ALIGN' => 'L', + 'TABLE_LEFT_MARGIN' => 10, + 'BORDER_TYPE' => 0, + 'BORDER_SIZE' => 0.3, + 'BORDER_COLOR' => [0, 0, 0], + ], + 'HEADER' => [ + 'TEXT_COLOR' => [0, 0, 0], + 'TEXT_SIZE' => 10, + 'TEXT_FONT' => 'helvetica', + 'TEXT_ALIGN' => 'L', + 'VERTICAL_ALIGN' => 'M', + 'TEXT_TYPE' => 'B', + 'LINE_SIZE' => 5, + 'BACKGROUND_COLOR' => [255, 255, 255], + 'BORDER_COLOR' => [0, 0, 0], + 'BORDER_SIZE' => 0.3, + 'BORDER_TYPE' => 'B', + 'TEXT' => ' ', + 'PADDING_TOP' => 2, + 'PADDING_RIGHT' => 2, + 'PADDING_LEFT' => 2, + 'PADDING_BOTTOM' => 2, + ], + 'ROW' => [ + 'TEXT_COLOR' => [0, 0, 0], + 'TEXT_SIZE' => 10, + 'TEXT_FONT' => 'helvetica', + 'TEXT_ALIGN' => 'L', + 'VERTICAL_ALIGN' => 'T', + 'TEXT_TYPE' => '', + 'LINE_SIZE' => 5, + 'BACKGROUND_COLOR' => [255, 255, 255], + 'BORDER_COLOR' => [10, 220, 0], + 'BORDER_SIZE' => 3, + 'BORDER_TYPE' => 'BL', + 'TEXT' => ' ', + 'PADDING_TOP' => 2, + 'PADDING_RIGHT' => 2, + 'PADDING_LEFT' => 2, + 'PADDING_BOTTOM' => 2, + ], +]); + +$table->addHeader([ + ['TEXT' => 'ID'], + ['TEXT' => 'Description'], + ['TEXT' => 'Notes'], +]); + +$table->addRow([ + ['TEXT' => '01', 'TEXT_ALIGN' => 'C'], + ['BACKGROUND_COLOR' => [20, 20, 5],'TEXT' => 'This row demonstrates a wrapped description that spans multiple lines while keeping only the bottom border visible.', 'TEXT_ALIGN' => 'L'], + ['TEXT' => 'Bottom border only', 'TEXT_ALIGN' => 'L'], +]); + +$table->addRow([ + ['TEXT' => '02', 'TEXT_ALIGN' => 'C'], + ['TEXT' => 'Short text on the left, but the middle cell is long enough to wrap naturally and prove the border stays at the bottom.', 'TEXT_ALIGN' => 'L'], + ['TEXT' => "First line\nSecond line\nThird line", 'TEXT_ALIGN' => 'L'], +]); + +$table->addRow([ + ['TEXT' => '03', 'TEXT_ALIGN' => 'C'], + ['TEXT' => 'Another multiline example with enough words to create a tall row in the description column.', 'TEXT_ALIGN' => 'L'], + ['TEXT' => 'The whole table uses only bottom borders for each cell.', 'TEXT_ALIGN' => 'L'], +]); + +$table->close(); + +if (PHP_SAPI === 'cli') { + $outputFile = __DIR__ . '/example-table-bottom-border-multiline.pdf'; + $pdf->Output('F', $outputFile); + echo "PDF written to: {$outputFile}\n"; + return; +} + +$pdf->Output(); + diff --git a/dev/test-bl-border-specific.php b/dev/test-bl-border-specific.php new file mode 100644 index 0000000..b2a8786 --- /dev/null +++ b/dev/test-bl-border-specific.php @@ -0,0 +1,102 @@ +SetAuthor('EvoSys21'); +$pdf->SetMargins(20, 20, 20); +$pdf->SetAutoPageBreak(true, 20); +$pdf->SetFont('helvetica', '', 10); +$pdf->SetTextColor(0, 0, 0); +$pdf->AddPage(); + +$pdf->SetFont('helvetica', 'B', 14); +$pdf->Cell(0, 10, 'BL Border Test - Multiline Cells', 0, 1, 'C'); +$pdf->SetFont('helvetica', '', 10); +$pdf->Ln(5); + +$pdf->Cell(0, 5, 'Each cell has ONLY Bottom and Left borders. Background should NOT cut off borders.', 0, 1, 'L'); +$pdf->Ln(5); + +$table = new Table($pdf); +$table->initialize([30, 80, 60], [ + 'TABLE' => [ + 'TABLE_ALIGN' => 'L', + 'BORDER_TYPE' => 0, + 'BORDER_SIZE' => 1, + ], + 'HEADER' => [ + 'BORDER_TYPE' => 'BL', + 'BORDER_SIZE' => 1, + 'BORDER_COLOR' => [0, 0, 200], + 'BACKGROUND_COLOR' => [220, 220, 255], + 'TEXT_TYPE' => 'B', + 'PADDING_TOP' => 3, + 'PADDING_BOTTOM' => 3, + 'PADDING_LEFT' => 3, + 'PADDING_RIGHT' => 3, + ], + 'ROW' => [ + 'BORDER_TYPE' => 'BL', + 'BORDER_SIZE' => 1, + 'BORDER_COLOR' => [0, 0, 200], + 'BACKGROUND_COLOR' => [255, 255, 255], + 'PADDING_TOP' => 3, + 'PADDING_BOTTOM' => 3, + 'PADDING_LEFT' => 3, + 'PADDING_RIGHT' => 3, + ], +]); + +$table->addHeader([ + ['TEXT' => 'No.'], + ['TEXT' => 'Description'], + ['TEXT' => 'Notes'], +]); + +$table->addRow([ + ['TEXT' => '1', 'TEXT_ALIGN' => 'C'], + ['TEXT' => 'This is a long description that wraps to multiple lines to test if the bottom border from the previous row gets cut off by the background color.'], + ['TEXT' => 'Watch the borders closely!'], +]); + +$table->addRow([ + ['TEXT' => '2', 'TEXT_ALIGN' => 'C'], + ['TEXT' => 'Another multiline cell. The background fill should be INSET so it does not overwrite the bottom border of the row above.'], + ['TEXT' => "Multiple\nlines\nhere\ntoo"], +]); + +$table->addRow([ + ['TEXT' => '3', 'TEXT_ALIGN' => 'C'], + ['TEXT' => 'Short text'], + ['TEXT' => 'All borders should be clean and continuous with no gaps or double-thickness.'], +]); + +$table->addRow([ + ['TEXT' => '4', 'TEXT_ALIGN' => 'C'], + ['TEXT' => 'Final row with enough text to wrap naturally and ensure all borders render correctly without artifacts or overlaps.'], + ['TEXT' => 'Perfect!'], +]); + +$table->close(); + +if (PHP_SAPI === 'cli') { + $outputFile = __DIR__ . '/test-bl-border-specific.pdf'; + $pdf->Output('F', $outputFile); + echo "PDF written to: {$outputFile}\n"; + echo "Open the PDF and verify:\n"; + echo " - Bottom borders are NOT cut off\n"; + echo " - Left borders are NOT cut off\n"; + echo " - No double-thickness borders\n"; + echo " - Background color does not overlap borders\n"; + return; +} + +$pdf->Output(); + diff --git a/dev/test-border-types.php b/dev/test-border-types.php new file mode 100644 index 0000000..f3dd5be --- /dev/null +++ b/dev/test-border-types.php @@ -0,0 +1,222 @@ +SetAuthor('EvoSys21'); +$pdf->SetMargins(20, 20, 20); +$pdf->SetAutoPageBreak(true, 20); +$pdf->SetFont('helvetica', '', 10); +$pdf->SetTextColor(0, 0, 0); +$pdf->AddPage(); + +// Test 1: Bottom border only +$pdf->Cell(0, 10, 'Test 1: Bottom Border Only', 0, 1); + +$table = new Table($pdf); +$table->initialize([60, 60, 60], [ + 'TABLE' => [ + 'BORDER_TYPE' => 0, + 'BORDER_SIZE' => 0.5, + ], + 'HEADER' => [ + 'BORDER_TYPE' => 'B', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [0, 0, 0], + 'PADDING' => 2, + ], + 'ROW' => [ + 'BORDER_TYPE' => 'B', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [0, 0, 0], + 'PADDING' => 2, + ], +]); + +$table->addHeader([ + ['TEXT' => 'Column 1'], + ['TEXT' => 'Column 2'], + ['TEXT' => 'Column 3'], +]); + +$table->addRow([ + ['TEXT' => 'Row 1'], + ['TEXT' => 'Data'], + ['TEXT' => 'More data'], +]); + +$table->addRow([ + ['TEXT' => 'Row 2'], + ['TEXT' => 'Multiline\ntext\nhere'], + ['TEXT' => 'Test'], +]); + +$table->close(); + +// Test 2: Top border only +$pdf->Ln(10); +$pdf->Cell(0, 10, 'Test 2: Top Border Only', 0, 1); + +$table = new Table($pdf); +$table->initialize([60, 60, 60], [ + 'TABLE' => [ + 'BORDER_TYPE' => 0, + 'BORDER_SIZE' => 0.5, + ], + 'HEADER' => [ + 'BORDER_TYPE' => 'T', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [255, 0, 0], + 'PADDING' => 2, + ], + 'ROW' => [ + 'BORDER_TYPE' => 'T', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [255, 0, 0], + 'PADDING' => 2, + ], +]); + +$table->addHeader([ + ['TEXT' => 'A'], + ['TEXT' => 'B'], + ['TEXT' => 'C'], +]); + +$table->addRow([ + ['TEXT' => 'X'], + ['TEXT' => 'Y'], + ['TEXT' => 'Z'], +]); + +$table->close(); + +// Test 3: Left border only +$pdf->Ln(10); +$pdf->Cell(0, 10, 'Test 3: Left Border Only', 0, 1); + +$table = new Table($pdf); +$table->initialize([60, 60, 60], [ + 'TABLE' => [ + 'BORDER_TYPE' => 0, + 'BORDER_SIZE' => 0.5, + ], + 'HEADER' => [ + 'BORDER_TYPE' => 'L', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [0, 0, 255], + 'PADDING' => 2, + ], + 'ROW' => [ + 'BORDER_TYPE' => 'L', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [0, 0, 255], + 'PADDING' => 2, + ], +]); + +$table->addHeader([ + ['TEXT' => 'Col 1'], + ['TEXT' => 'Col 2'], + ['TEXT' => 'Col 3'], +]); + +$table->addRow([ + ['TEXT' => 'Data 1'], + ['TEXT' => 'Data 2'], + ['TEXT' => 'Data 3'], +]); + +$table->close(); + +// Test 4: Right border only +$pdf->Ln(10); +$pdf->Cell(0, 10, 'Test 4: Right Border Only', 0, 1); + +$table = new Table($pdf); +$table->initialize([60, 60, 60], [ + 'TABLE' => [ + 'BORDER_TYPE' => 0, + 'BORDER_SIZE' => 0.5, + ], + 'HEADER' => [ + 'BORDER_TYPE' => 'R', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [0, 255, 0], + 'PADDING' => 2, + ], + 'ROW' => [ + 'BORDER_TYPE' => 'R', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [0, 255, 0], + 'PADDING' => 2, + ], +]); + +$table->addHeader([ + ['TEXT' => 'Header 1'], + ['TEXT' => 'Header 2'], + ['TEXT' => 'Header 3'], +]); + +$table->addRow([ + ['TEXT' => 'Value 1'], + ['TEXT' => 'Value 2'], + ['TEXT' => 'Value 3'], +]); + +$table->close(); + +// Test 5: All borders (ensure we didn't break standard borders) +$pdf->AddPage(); +$pdf->Cell(0, 10, 'Test 5: All Borders (Standard)', 0, 1); + +$table = new Table($pdf); +$table->initialize([60, 60, 60], [ + 'TABLE' => [ + 'BORDER_TYPE' => 0, + 'BORDER_SIZE' => 0.5, + ], + 'HEADER' => [ + 'BORDER_TYPE' => 1, + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [0, 0, 0], + 'PADDING' => 2, + ], + 'ROW' => [ + 'BORDER_TYPE' => 1, + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [0, 0, 0], + 'PADDING' => 2, + ], +]); + +$table->addHeader([ + ['TEXT' => 'All'], + ['TEXT' => 'Borders'], + ['TEXT' => 'Test'], +]); + +$table->addRow([ + ['TEXT' => 'Should'], + ['TEXT' => 'Have'], + ['TEXT' => 'Box'], +]); + +$table->close(); + +if (PHP_SAPI === 'cli') { + $outputFile = __DIR__ . '/test-border-types.pdf'; + $pdf->Output('F', $outputFile); + echo "PDF written to: {$outputFile}\n"; + return; +} + +$pdf->Output(); + diff --git a/dev/test-table-border-combinations.php b/dev/test-table-border-combinations.php new file mode 100644 index 0000000..35da8bf --- /dev/null +++ b/dev/test-table-border-combinations.php @@ -0,0 +1,183 @@ +SetAuthor('EvoSys21'); +$pdf->SetMargins(20, 20, 20); +$pdf->SetAutoPageBreak(true, 20); +$pdf->SetFont('helvetica', '', 10); +$pdf->SetTextColor(0, 0, 0); +$pdf->AddPage(); + +$pdf->SetFont('helvetica', 'B', 12); +$pdf->Cell(0, 10, 'Border Combination Tests', 0, 1, 'L'); +$pdf->SetFont('helvetica', '', 10); +$pdf->Ln(5); + +// Test 1: Bottom and Left borders (BL) +$pdf->SetFont('helvetica', 'B', 10); +$pdf->Cell(0, 6, 'Test 1: BL (Bottom-Left) borders', 0, 1, 'L'); +$pdf->SetFont('helvetica', '', 10); + +$table = new Table($pdf); +$table->initialize([60, 60, 60], [ + 'TABLE' => [ + 'TABLE_ALIGN' => 'L', + 'BORDER_TYPE' => 0, + ], + 'HEADER' => [ + 'BORDER_TYPE' => 'BL', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [0, 0, 0], + 'BACKGROUND_COLOR' => [230, 230, 230], + 'PADDING_TOP' => 2, + 'PADDING_BOTTOM' => 2, + ], + 'ROW' => [ + 'BORDER_TYPE' => 'BL', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [0, 0, 0], + 'BACKGROUND_COLOR' => [255, 255, 255], + 'PADDING_TOP' => 2, + 'PADDING_BOTTOM' => 2, + ], +]); + +$table->addHeader([ + ['TEXT' => 'Column 1'], + ['TEXT' => 'Column 2'], + ['TEXT' => 'Column 3'], +]); + +$table->addRow([ + ['TEXT' => 'Row 1, Cell 1'], + ['TEXT' => 'Row 1, Cell 2'], + ['TEXT' => 'Row 1, Cell 3'], +]); + +$table->addRow([ + ['TEXT' => 'Row 2, Cell 1'], + ['TEXT' => 'Row 2, Cell 2'], + ['TEXT' => 'Row 2, Cell 3'], +]); + +$table->close(); + +$pdf->Ln(10); + +// Test 2: Top and Right borders (TR) +$pdf->SetFont('helvetica', 'B', 10); +$pdf->Cell(0, 6, 'Test 2: TR (Top-Right) borders', 0, 1, 'L'); +$pdf->SetFont('helvetica', '', 10); + +$table = new Table($pdf); +$table->initialize([60, 60, 60], [ + 'TABLE' => [ + 'TABLE_ALIGN' => 'L', + 'BORDER_TYPE' => 0, + ], + 'HEADER' => [ + 'BORDER_TYPE' => 'TR', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [200, 0, 0], + 'BACKGROUND_COLOR' => [255, 230, 230], + 'PADDING_TOP' => 2, + 'PADDING_BOTTOM' => 2, + ], + 'ROW' => [ + 'BORDER_TYPE' => 'TR', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [200, 0, 0], + 'BACKGROUND_COLOR' => [255, 255, 255], + 'PADDING_TOP' => 2, + 'PADDING_BOTTOM' => 2, + ], +]); + +$table->addHeader([ + ['TEXT' => 'Column 1'], + ['TEXT' => 'Column 2'], + ['TEXT' => 'Column 3'], +]); + +$table->addRow([ + ['TEXT' => 'Row 1, Cell 1'], + ['TEXT' => 'Row 1, Cell 2'], + ['TEXT' => 'Row 1, Cell 3'], +]); + +$table->addRow([ + ['TEXT' => 'Row 2, Cell 1'], + ['TEXT' => 'Row 2, Cell 2'], + ['TEXT' => 'Row 2, Cell 3'], +]); + +$table->close(); + +$pdf->Ln(10); + +// Test 3: Bottom only (B) with multiline +$pdf->SetFont('helvetica', 'B', 10); +$pdf->Cell(0, 6, 'Test 3: B (Bottom only) with multiline text', 0, 1, 'L'); +$pdf->SetFont('helvetica', '', 10); + +$table = new Table($pdf); +$table->initialize([40, 80, 60], [ + 'TABLE' => [ + 'TABLE_ALIGN' => 'L', + 'BORDER_TYPE' => 0, + ], + 'HEADER' => [ + 'BORDER_TYPE' => 'B', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [0, 100, 0], + 'BACKGROUND_COLOR' => [230, 255, 230], + 'PADDING_TOP' => 2, + 'PADDING_BOTTOM' => 2, + ], + 'ROW' => [ + 'BORDER_TYPE' => 'B', + 'BORDER_SIZE' => 0.5, + 'BORDER_COLOR' => [0, 100, 0], + 'BACKGROUND_COLOR' => [255, 255, 255], + 'PADDING_TOP' => 2, + 'PADDING_BOTTOM' => 2, + ], +]); + +$table->addHeader([ + ['TEXT' => 'ID'], + ['TEXT' => 'Description'], + ['TEXT' => 'Status'], +]); + +$table->addRow([ + ['TEXT' => '1'], + ['TEXT' => 'This is a multiline description that should wrap and maintain proper bottom border'], + ['TEXT' => 'Active'], +]); + +$table->addRow([ + ['TEXT' => '2'], + ['TEXT' => 'Another row with text that wraps to multiple lines'], + ['TEXT' => 'Pending'], +]); + +$table->close(); + +if (PHP_SAPI === 'cli') { + $outputFile = __DIR__ . '/test-table-border-combinations.pdf'; + $pdf->Output('F', $outputFile); + echo "PDF written to: {$outputFile}\n"; + return; +} + +$pdf->Output(); + diff --git a/examples/Fpdf/example-table-4-override.pdf b/examples/Fpdf/example-table-4-override.pdf index a7f866e..d137d13 100644 Binary files a/examples/Fpdf/example-table-4-override.pdf and b/examples/Fpdf/example-table-4-override.pdf differ diff --git a/examples/Fpdf/example-table-5-row-height.pdf b/examples/Fpdf/example-table-5-row-height.pdf index e24fb2e..5b71e1d 100644 Binary files a/examples/Fpdf/example-table-5-row-height.pdf and b/examples/Fpdf/example-table-5-row-height.pdf differ diff --git a/examples/Tcpdf/example-multicell-1-overview.pdf b/examples/Tcpdf/example-multicell-1-overview.pdf index b7f54e6..12e2d64 100644 Binary files a/examples/Tcpdf/example-multicell-1-overview.pdf and b/examples/Tcpdf/example-multicell-1-overview.pdf differ diff --git a/examples/Tcpdf/example-multicell-2-overview-page-break.pdf b/examples/Tcpdf/example-multicell-2-overview-page-break.pdf index 5fcd67f..94a9f33 100644 Binary files a/examples/Tcpdf/example-multicell-2-overview-page-break.pdf and b/examples/Tcpdf/example-multicell-2-overview-page-break.pdf differ diff --git a/examples/Tcpdf/example-multicell-3-line-breaking.pdf b/examples/Tcpdf/example-multicell-3-line-breaking.pdf index 541e7f1..6c6164d 100644 Binary files a/examples/Tcpdf/example-multicell-3-line-breaking.pdf and b/examples/Tcpdf/example-multicell-3-line-breaking.pdf differ diff --git a/examples/Tcpdf/example-multicell-4-page-break.pdf b/examples/Tcpdf/example-multicell-4-page-break.pdf index bcd3d5e..23eaea1 100644 Binary files a/examples/Tcpdf/example-multicell-4-page-break.pdf and b/examples/Tcpdf/example-multicell-4-page-break.pdf differ diff --git a/examples/Tcpdf/example-multicell-5-max-lines.pdf b/examples/Tcpdf/example-multicell-5-max-lines.pdf index 4ae2899..05cf43e 100644 Binary files a/examples/Tcpdf/example-multicell-5-max-lines.pdf and b/examples/Tcpdf/example-multicell-5-max-lines.pdf differ diff --git a/examples/Tcpdf/example-multicell-6-shrinking.pdf b/examples/Tcpdf/example-multicell-6-shrinking.pdf index 4b76415..703a096 100644 Binary files a/examples/Tcpdf/example-multicell-6-shrinking.pdf and b/examples/Tcpdf/example-multicell-6-shrinking.pdf differ diff --git a/examples/Tcpdf/example-table-1-overview.pdf b/examples/Tcpdf/example-table-1-overview.pdf index 6f66c70..f7889ba 100644 Binary files a/examples/Tcpdf/example-table-1-overview.pdf and b/examples/Tcpdf/example-table-1-overview.pdf differ diff --git a/examples/Tcpdf/example-table-2-overview.pdf b/examples/Tcpdf/example-table-2-overview.pdf index 139fade..76d03c4 100644 Binary files a/examples/Tcpdf/example-table-2-overview.pdf and b/examples/Tcpdf/example-table-2-overview.pdf differ diff --git a/examples/Tcpdf/example-table-3-detailed.pdf b/examples/Tcpdf/example-table-3-detailed.pdf index 7fa9f78..59b0221 100644 Binary files a/examples/Tcpdf/example-table-3-detailed.pdf and b/examples/Tcpdf/example-table-3-detailed.pdf differ diff --git a/examples/Tcpdf/example-table-4-override.pdf b/examples/Tcpdf/example-table-4-override.pdf index 2f6776b..c2e59a2 100644 Binary files a/examples/Tcpdf/example-table-4-override.pdf and b/examples/Tcpdf/example-table-4-override.pdf differ diff --git a/examples/Tcpdf/example-table-5-row-height.pdf b/examples/Tcpdf/example-table-5-row-height.pdf index c551cc6..7d4027f 100644 Binary files a/examples/Tcpdf/example-table-5-row-height.pdf and b/examples/Tcpdf/example-table-5-row-height.pdf differ diff --git a/examples/Tfpdf/example-table-4-override.pdf b/examples/Tfpdf/example-table-4-override.pdf index 102c870..b3e6d3f 100644 Binary files a/examples/Tfpdf/example-table-4-override.pdf and b/examples/Tfpdf/example-table-4-override.pdf differ diff --git a/examples/Tfpdf/example-table-5-row-height.pdf b/examples/Tfpdf/example-table-5-row-height.pdf index 4f5ed85..7e6a394 100644 Binary files a/examples/Tfpdf/example-table-5-row-height.pdf and b/examples/Tfpdf/example-table-5-row-height.pdf differ diff --git a/src/Table.php b/src/Table.php index bef659f..3d3eb83 100644 --- a/src/Table.php +++ b/src/Table.php @@ -1414,6 +1414,9 @@ protected function cachePrepOutputData() $count = count($dataCache); + /** @var CellInterface[] $prevRowData */ + $prevRowData = null; + for ($k = 0; $k < $count; $k++) { $val = &$dataCache[$k]; @@ -1423,6 +1426,7 @@ protected function cachePrepOutputData() if ($val['DATATYPE'] == 'new_page') { //add a new page $this->addPage(); + $prevRowData = null; // page break resets adjacent-row tracking continue; } @@ -1446,6 +1450,25 @@ protected function cachePrepOutputData() $cell->setCellDrawHeight($val['HEIGHT']); } + // Inform the cell whether an already-rendered adjacent border + // sits on its top / left edge so the fill inset can be adjusted. + if (method_exists($cell, 'setAdjacentBorderTop')) { + $cell->setAdjacentBorderTop( + $prevRowData !== null + && isset($prevRowData[$i]) + && method_exists($prevRowData[$i], 'borderIncludesSide') + && $prevRowData[$i]->borderIncludesSide('B') + ); + } + if (method_exists($cell, 'setAdjacentBorderLeft')) { + $cell->setAdjacentBorderLeft( + $i > 0 + && isset($data[$i - 1]) + && method_exists($data[$i - 1], 'borderIncludesSide') + && $data[$i - 1]->borderIncludesSide('R') + ); + } + $cell->render(); } @@ -1454,6 +1477,8 @@ protected function cachePrepOutputData() //if we have colspan, just ignore the next cells } + $prevRowData = $data; + $this->dataOnCurrentPage = true; //Go to the next line diff --git a/src/Table/Cell/CellAbstract.php b/src/Table/Cell/CellAbstract.php index c8b53a2..9aa20dd 100644 --- a/src/Table/Cell/CellAbstract.php +++ b/src/Table/Cell/CellAbstract.php @@ -169,6 +169,18 @@ abstract class CellAbstract implements CellInterface */ protected $bSkip = false; + /** + * Whether the adjacent cell above has a bottom border (set by the table renderer). + * Used to protect that border from being covered by this cell's fill. + */ + protected bool $adjacentBorderTop = false; + + /** + * Whether the adjacent cell to the left has a right border (set by the table renderer). + * Used to protect that border from being covered by this cell's fill. + */ + protected bool $adjacentBorderLeft = false; + public function __construct($pdf) { if ($pdf instanceof PdfInterface) { @@ -394,6 +406,32 @@ public function getSkipped(): bool return $this->bSkip; } + public function setAdjacentBorderTop(bool $value): void + { + $this->adjacentBorderTop = $value; + } + + public function setAdjacentBorderLeft(bool $value): void + { + $this->adjacentBorderLeft = $value; + } + + /** + * Returns true if this cell's border type includes the given side ('T', 'B', 'L', 'R'). + */ + public function borderIncludesSide(string $side): bool + { + $bt = $this->getBorderType(); + if ($bt === 1 || $bt === '1') { + return true; + } + if ($bt === 0 || $bt === '0' || $bt === '') { + return false; + } + + return strpos((string) $bt, $side) !== false; + } + public function __get($property) { if (isset($this->properties[$property])) { @@ -447,15 +485,61 @@ public function renderCellLayout() [$r, $g, $b] = $this->getBorderColor(); $this->pdf->SetDrawColor($r, $g, $b); - $this->pdf->Cell( - $this->getCellDrawWidth(), - $this->getCellDrawHeight(), - '', - $this->getBorderType(), - 0, - '', - ! $this->isTransparent() - ); + $borderType = $this->getBorderType(); + $width = $this->getCellDrawWidth(); + $height = $this->getCellDrawHeight(); + $borderSize = $this->getBorderSize(); + + // For any border type other than 0 (no border) or 1 (all borders with Cell()), + // draw manually to avoid overwriting adjacent borders + if ($borderType !== 0 && $borderType !== '0' && $borderType !== 1 && $borderType !== '1') { + $borderStr = (string) $borderType; + $hasTop = strpos($borderStr, 'T') !== false; + $hasBottom = strpos($borderStr, 'B') !== false; + $hasLeft = strpos($borderStr, 'L') !== false; + $hasRight = strpos($borderStr, 'R') !== false; + + // Inset on sides that have a border on THIS cell, or where an adjacent + // cell already drew a border on the shared edge (to avoid covering it). + if (! $this->isTransparent()) { + $insetLeft = ($hasLeft || $this->adjacentBorderLeft) ? $borderSize / 2 : 0; + $insetRight = $hasRight ? $borderSize / 2 : 0; + $insetTop = ($hasTop || $this->adjacentBorderTop) ? $borderSize / 2 : 0; + $insetBottom = $hasBottom ? $borderSize / 2 : 0; + $this->pdf->Rect( + $x + $insetLeft, + $y + $insetTop, + max(0, $width - $insetLeft - $insetRight), + max(0, $height - $insetTop - $insetBottom), + 'F' + ); + } + + // Manually draw each requested border side + if ($hasTop) { + $this->pdf->Line($x, $y, $x + $width, $y); + } + if ($hasBottom) { + $this->pdf->Line($x, $y + $height, $x + $width, $y + $height); + } + if ($hasLeft) { + $this->pdf->Line($x, $y, $x, $y + $height); + } + if ($hasRight) { + $this->pdf->Line($x + $width, $y, $x + $width, $y + $height); + } + } else { + // For 0 (no border) or 1 (all sides), use standard Cell() + $this->pdf->Cell( + $this->getCellDrawWidth(), + $this->getCellDrawHeight(), + '', + $borderType, + 0, + '', + ! $this->isTransparent() + ); + } $this->pdf->SetXY($x, $y); } diff --git a/tests/_files/dev/Fpdf/multi-page-draw-table-model1.png b/tests/_files/dev/Fpdf/multi-page-draw-table-model1.png index a66870f..62e199c 100644 Binary files a/tests/_files/dev/Fpdf/multi-page-draw-table-model1.png and b/tests/_files/dev/Fpdf/multi-page-draw-table-model1.png differ diff --git a/tests/_files/dev/Fpdf/multi-page-draw-table-model2.png b/tests/_files/dev/Fpdf/multi-page-draw-table-model2.png index 067a6b5..c6c76c9 100644 Binary files a/tests/_files/dev/Fpdf/multi-page-draw-table-model2.png and b/tests/_files/dev/Fpdf/multi-page-draw-table-model2.png differ diff --git a/tests/_files/dev/Fpdf/multi-page-draw-table-model3.png b/tests/_files/dev/Fpdf/multi-page-draw-table-model3.png index 1ab9a90..d6fbe14 100644 Binary files a/tests/_files/dev/Fpdf/multi-page-draw-table-model3.png and b/tests/_files/dev/Fpdf/multi-page-draw-table-model3.png differ diff --git a/tests/_files/dev/Fpdf/test-multicell-align.png b/tests/_files/dev/Fpdf/test-multicell-align.png index b9a8ef0..54b50df 100644 Binary files a/tests/_files/dev/Fpdf/test-multicell-align.png and b/tests/_files/dev/Fpdf/test-multicell-align.png differ diff --git a/tests/_files/dev/Fpdf/test-multicell-shrinking.png b/tests/_files/dev/Fpdf/test-multicell-shrinking.png index b8cb11d..0635cb3 100644 Binary files a/tests/_files/dev/Fpdf/test-multicell-shrinking.png and b/tests/_files/dev/Fpdf/test-multicell-shrinking.png differ diff --git a/tests/_files/dev/Fpdf/test-multicell-shrinking2.png b/tests/_files/dev/Fpdf/test-multicell-shrinking2.png index 6006055..d24fc6e 100644 Binary files a/tests/_files/dev/Fpdf/test-multicell-shrinking2.png and b/tests/_files/dev/Fpdf/test-multicell-shrinking2.png differ diff --git a/tests/_files/dev/Fpdf/test-multicell-style.png b/tests/_files/dev/Fpdf/test-multicell-style.png index 2280e47..4b1b5ff 100644 Binary files a/tests/_files/dev/Fpdf/test-multicell-style.png and b/tests/_files/dev/Fpdf/test-multicell-style.png differ diff --git a/tests/_files/dev/Tcpdf/multi-page-draw-table-model1.pdf b/tests/_files/dev/Tcpdf/multi-page-draw-table-model1.pdf index 1e44c9c..c8aaba8 100644 Binary files a/tests/_files/dev/Tcpdf/multi-page-draw-table-model1.pdf and b/tests/_files/dev/Tcpdf/multi-page-draw-table-model1.pdf differ diff --git a/tests/_files/dev/Tcpdf/multi-page-draw-table-model1.png b/tests/_files/dev/Tcpdf/multi-page-draw-table-model1.png index bdc673a..305122d 100644 Binary files a/tests/_files/dev/Tcpdf/multi-page-draw-table-model1.png and b/tests/_files/dev/Tcpdf/multi-page-draw-table-model1.png differ diff --git a/tests/_files/dev/Tcpdf/multi-page-draw-table-model2.pdf b/tests/_files/dev/Tcpdf/multi-page-draw-table-model2.pdf index 9316344..839c7f9 100644 Binary files a/tests/_files/dev/Tcpdf/multi-page-draw-table-model2.pdf and b/tests/_files/dev/Tcpdf/multi-page-draw-table-model2.pdf differ diff --git a/tests/_files/dev/Tcpdf/multi-page-draw-table-model2.png b/tests/_files/dev/Tcpdf/multi-page-draw-table-model2.png index 17de028..2255882 100644 Binary files a/tests/_files/dev/Tcpdf/multi-page-draw-table-model2.png and b/tests/_files/dev/Tcpdf/multi-page-draw-table-model2.png differ diff --git a/tests/_files/dev/Tcpdf/multi-page-draw-table-model3.pdf b/tests/_files/dev/Tcpdf/multi-page-draw-table-model3.pdf index e8fb96e..bce3ec4 100644 Binary files a/tests/_files/dev/Tcpdf/multi-page-draw-table-model3.pdf and b/tests/_files/dev/Tcpdf/multi-page-draw-table-model3.pdf differ diff --git a/tests/_files/dev/Tcpdf/multi-page-draw-table-model3.png b/tests/_files/dev/Tcpdf/multi-page-draw-table-model3.png index de7f430..d55b4fc 100644 Binary files a/tests/_files/dev/Tcpdf/multi-page-draw-table-model3.png and b/tests/_files/dev/Tcpdf/multi-page-draw-table-model3.png differ diff --git a/tests/_files/dev/Tcpdf/test-multicell-align.pdf b/tests/_files/dev/Tcpdf/test-multicell-align.pdf index 8cd7fe5..918bc4d 100644 Binary files a/tests/_files/dev/Tcpdf/test-multicell-align.pdf and b/tests/_files/dev/Tcpdf/test-multicell-align.pdf differ diff --git a/tests/_files/dev/Tcpdf/test-multicell-align.png b/tests/_files/dev/Tcpdf/test-multicell-align.png index a495260..59ecf37 100644 Binary files a/tests/_files/dev/Tcpdf/test-multicell-align.png and b/tests/_files/dev/Tcpdf/test-multicell-align.png differ diff --git a/tests/_files/dev/Tcpdf/test-multicell-disable-pagebreak.pdf b/tests/_files/dev/Tcpdf/test-multicell-disable-pagebreak.pdf index 1804d87..ebf07a5 100644 Binary files a/tests/_files/dev/Tcpdf/test-multicell-disable-pagebreak.pdf and b/tests/_files/dev/Tcpdf/test-multicell-disable-pagebreak.pdf differ diff --git a/tests/_files/dev/Tcpdf/test-multicell-min-height.pdf b/tests/_files/dev/Tcpdf/test-multicell-min-height.pdf index 90b279c..83f9f00 100644 Binary files a/tests/_files/dev/Tcpdf/test-multicell-min-height.pdf and b/tests/_files/dev/Tcpdf/test-multicell-min-height.pdf differ diff --git a/tests/_files/dev/Tcpdf/test-multicell-shrinking.pdf b/tests/_files/dev/Tcpdf/test-multicell-shrinking.pdf index 23a356d..c47a6ab 100644 Binary files a/tests/_files/dev/Tcpdf/test-multicell-shrinking.pdf and b/tests/_files/dev/Tcpdf/test-multicell-shrinking.pdf differ diff --git a/tests/_files/dev/Tcpdf/test-multicell-shrinking.png b/tests/_files/dev/Tcpdf/test-multicell-shrinking.png index 40ca92f..04b7f70 100644 Binary files a/tests/_files/dev/Tcpdf/test-multicell-shrinking.png and b/tests/_files/dev/Tcpdf/test-multicell-shrinking.png differ diff --git a/tests/_files/dev/Tcpdf/test-multicell-shrinking2.pdf b/tests/_files/dev/Tcpdf/test-multicell-shrinking2.pdf index 129e66f..7a442d9 100644 Binary files a/tests/_files/dev/Tcpdf/test-multicell-shrinking2.pdf and b/tests/_files/dev/Tcpdf/test-multicell-shrinking2.pdf differ diff --git a/tests/_files/dev/Tcpdf/test-multicell-shrinking2.png b/tests/_files/dev/Tcpdf/test-multicell-shrinking2.png index ac7e06b..2454118 100644 Binary files a/tests/_files/dev/Tcpdf/test-multicell-shrinking2.png and b/tests/_files/dev/Tcpdf/test-multicell-shrinking2.png differ diff --git a/tests/_files/dev/Tcpdf/test-multicell-style.pdf b/tests/_files/dev/Tcpdf/test-multicell-style.pdf index 40758f3..90939e7 100644 Binary files a/tests/_files/dev/Tcpdf/test-multicell-style.pdf and b/tests/_files/dev/Tcpdf/test-multicell-style.pdf differ diff --git a/tests/_files/dev/Tcpdf/test-multicell-style.png b/tests/_files/dev/Tcpdf/test-multicell-style.png index 1ec7ab6..3fcb807 100644 Binary files a/tests/_files/dev/Tcpdf/test-multicell-style.png and b/tests/_files/dev/Tcpdf/test-multicell-style.png differ diff --git a/tests/_files/dev/Tfpdf/multi-page-draw-table-model1.png b/tests/_files/dev/Tfpdf/multi-page-draw-table-model1.png index 1ed9336..7178b28 100644 Binary files a/tests/_files/dev/Tfpdf/multi-page-draw-table-model1.png and b/tests/_files/dev/Tfpdf/multi-page-draw-table-model1.png differ diff --git a/tests/_files/dev/Tfpdf/multi-page-draw-table-model2.png b/tests/_files/dev/Tfpdf/multi-page-draw-table-model2.png index 8291954..15b6ffb 100644 Binary files a/tests/_files/dev/Tfpdf/multi-page-draw-table-model2.png and b/tests/_files/dev/Tfpdf/multi-page-draw-table-model2.png differ diff --git a/tests/_files/dev/Tfpdf/multi-page-draw-table-model3.png b/tests/_files/dev/Tfpdf/multi-page-draw-table-model3.png index 86229b4..e0ea73c 100644 Binary files a/tests/_files/dev/Tfpdf/multi-page-draw-table-model3.png and b/tests/_files/dev/Tfpdf/multi-page-draw-table-model3.png differ diff --git a/tests/_files/dev/Tfpdf/test-multicell-align.png b/tests/_files/dev/Tfpdf/test-multicell-align.png index a31947b..3fef0ef 100644 Binary files a/tests/_files/dev/Tfpdf/test-multicell-align.png and b/tests/_files/dev/Tfpdf/test-multicell-align.png differ diff --git a/tests/_files/dev/Tfpdf/test-multicell-shrinking.png b/tests/_files/dev/Tfpdf/test-multicell-shrinking.png index 0c22039..90cc0a3 100644 Binary files a/tests/_files/dev/Tfpdf/test-multicell-shrinking.png and b/tests/_files/dev/Tfpdf/test-multicell-shrinking.png differ diff --git a/tests/_files/dev/Tfpdf/test-multicell-shrinking2.png b/tests/_files/dev/Tfpdf/test-multicell-shrinking2.png index b73c556..be1adb5 100644 Binary files a/tests/_files/dev/Tfpdf/test-multicell-shrinking2.png and b/tests/_files/dev/Tfpdf/test-multicell-shrinking2.png differ diff --git a/tests/_files/dev/Tfpdf/test-multicell-style.png b/tests/_files/dev/Tfpdf/test-multicell-style.png index f048333..1d5b6db 100644 Binary files a/tests/_files/dev/Tfpdf/test-multicell-style.png and b/tests/_files/dev/Tfpdf/test-multicell-style.png differ