mirror of
https://github.com/chillerlan/php-qrcode.git
synced 2026-08-25 08:57:54 +00:00
✨ added PDF support (#49)
This commit is contained in:
@@ -34,8 +34,11 @@ namespaced, cleaned up, improved and other stuff.
|
||||
|
||||
### Requirements
|
||||
- PHP 7.2+
|
||||
- `ext-gd`, `ext-json`, `ext-mbstring`
|
||||
- optional `ext-imagick`
|
||||
- `ext-mbstring`
|
||||
- optional:
|
||||
- `ext-json`, `ext-gd`
|
||||
- `ext-imagick` with [ImageMagick](https://imagemagick.org) installed
|
||||
- [`setasign/fpdf`](https://github.com/setasign/fpdf) for the PDF output module
|
||||
|
||||
### Installation
|
||||
**requires [composer](https://getcomposer.org)**
|
||||
@@ -289,6 +292,7 @@ name | description
|
||||
`OUTPUT_IMAGE_PNG`, `OUTPUT_IMAGE_JPG`, `OUTPUT_IMAGE_GIF` | `QROptions::$outputType` image
|
||||
`OUTPUT_STRING_JSON`, `OUTPUT_STRING_TEXT` | `QROptions::$outputType` string
|
||||
`OUTPUT_IMAGICK` | `QROptions::$outputType` ImageMagick
|
||||
`OUTPUT_FPDF` | `QROptions::$outputType` PDF, using [FPDF](https://github.com/setasign/fpdf)
|
||||
`OUTPUT_CUSTOM` | `QROptions::$outputType`, requires `QROptions::$outputInterface`
|
||||
`ECC_L`, `ECC_M`, `ECC_Q`, `ECC_H`, | ECC-Level: 7%, 15%, 25%, 30% in `QROptions::$eccLevel`
|
||||
`DATA_NUMBER`, `DATA_ALPHANUM`, `DATA_BYTE`, `DATA_KANJI` | `QRDataInterface::$datamode`
|
||||
|
||||
+4
-4
@@ -25,16 +25,16 @@
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2",
|
||||
"ext-gd": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"chillerlan/php-settings-container": "^1.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.5"
|
||||
"phpunit/phpunit": "^8.5",
|
||||
"setasign/fpdf": "^1.8.2"
|
||||
},
|
||||
"suggest": {
|
||||
"chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps."
|
||||
"chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.",
|
||||
"setasign/fpdf": "Required to use the QR FPDF output."
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace chillerlan\QRCodeExamples;
|
||||
|
||||
use chillerlan\QRCode\{QRCode, QROptions};
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$data = 'https://www.youtube.com/watch?v=DLzxrzFCyOs&t=43s';
|
||||
|
||||
$options = new QROptions([
|
||||
'version' => 7,
|
||||
'outputType' => QRCode::OUTPUT_FPDF,
|
||||
'eccLevel' => QRCode::ECC_L,
|
||||
'scale' => 5,
|
||||
'moduleValues' => [
|
||||
// finder
|
||||
1536 => [0, 63, 255], // dark (true)
|
||||
6 => [255, 255, 255], // light (false), white is the transparency color and is enabled by default
|
||||
// alignment
|
||||
2560 => [255, 0, 255],
|
||||
10 => [255, 255, 255],
|
||||
// timing
|
||||
3072 => [255, 0, 0],
|
||||
12 => [255, 255, 255],
|
||||
// format
|
||||
3584 => [67, 191, 84],
|
||||
14 => [255, 255, 255],
|
||||
// version
|
||||
4096 => [62, 174, 190],
|
||||
16 => [255, 255, 255],
|
||||
// data
|
||||
1024 => [0, 0, 0],
|
||||
4 => [255, 255, 255],
|
||||
// darkmodule
|
||||
512 => [0, 0, 0],
|
||||
// separator
|
||||
8 => [255, 255, 255],
|
||||
// quietzone
|
||||
18 => [255, 255, 255],
|
||||
],
|
||||
]);
|
||||
|
||||
\header('Content-type: application/pdf');
|
||||
|
||||
echo (new QRCode($options))->render($data);
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* Class QRFpdf
|
||||
*
|
||||
* https://github.com/chillerlan/php-qrcode/pull/49
|
||||
*
|
||||
* @filesource QRFpdf.php
|
||||
* @created 03.06.2020
|
||||
* @package chillerlan\QRCode\Output
|
||||
* @author Maximilian Kresse
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace chillerlan\QRCode\Output;
|
||||
|
||||
use chillerlan\QRCode\Data\QRMatrix;
|
||||
use chillerlan\QRCode\QRCodeException;
|
||||
use chillerlan\Settings\SettingsContainerInterface;
|
||||
use FPDF;
|
||||
|
||||
use function array_values, class_exists, count, is_array;
|
||||
|
||||
/**
|
||||
* QRFpdf output module (requires fpdf)
|
||||
*
|
||||
* @see https://github.com/Setasign/FPDF
|
||||
* @see http://www.fpdf.org/
|
||||
*/
|
||||
class QRFpdf extends QROutputAbstract{
|
||||
|
||||
public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
|
||||
|
||||
if(!class_exists(FPDF::class)){
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new QRCodeException(
|
||||
'The QRFpdf output requires FPDF as dependency but the class "\FPDF" couldn\'t be found.'
|
||||
);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
parent::__construct($options, $matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function setModuleValues():void{
|
||||
|
||||
foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){
|
||||
$v = $this->options->moduleValues[$M_TYPE] ?? null;
|
||||
|
||||
if(!is_array($v) || count($v) < 3){
|
||||
$this->moduleValues[$M_TYPE] = $defaultValue
|
||||
? [0, 0, 0]
|
||||
: [255, 255, 255];
|
||||
}
|
||||
else{
|
||||
$this->moduleValues[$M_TYPE] = array_values($v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function dump(string $file = null):string{
|
||||
$file = $file ?? $this->options->cachefile;
|
||||
|
||||
$fpdf = new FPDF('P', $this->options->fpdfMeasureUnit, [$this->length, $this->length]);
|
||||
$fpdf->AddPage();
|
||||
|
||||
$prevColor = null;
|
||||
|
||||
foreach($this->matrix->matrix() as $y => $row){
|
||||
|
||||
foreach($row as $x => $M_TYPE){
|
||||
/** @var int $M_TYPE */
|
||||
$color = $this->moduleValues[$M_TYPE];
|
||||
|
||||
if($prevColor === null || $prevColor !== $color){
|
||||
$fpdf->SetFillColor(...$color);
|
||||
$prevColor = $color;
|
||||
}
|
||||
|
||||
$fpdf->Rect($x * $this->scale, $y * $this->scale, 1 * $this->scale, 1 * $this->scale, 'F');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$pdfData = $fpdf->Output('S');
|
||||
|
||||
if($file !== null){
|
||||
$this->saveToFile($pdfData, $file);
|
||||
}
|
||||
|
||||
return $pdfData;
|
||||
}
|
||||
|
||||
}
|
||||
+5
-1
@@ -16,7 +16,7 @@ use chillerlan\QRCode\Data\{
|
||||
MaskPatternTester, QRCodeDataException, QRDataInterface, QRMatrix
|
||||
};
|
||||
use chillerlan\QRCode\Output\{
|
||||
QRCodeOutputException, QRImage, QRImagick, QRMarkup, QROutputInterface, QRString
|
||||
QRCodeOutputException, QRFpdf, QRImage, QRImagick, QRMarkup, QROutputInterface, QRString
|
||||
};
|
||||
use chillerlan\Settings\SettingsContainerInterface;
|
||||
|
||||
@@ -42,6 +42,7 @@ class QRCode{
|
||||
public const OUTPUT_STRING_JSON = 'json';
|
||||
public const OUTPUT_STRING_TEXT = 'text';
|
||||
public const OUTPUT_IMAGICK = 'imagick';
|
||||
public const OUTPUT_FPDF = 'fpdf';
|
||||
public const OUTPUT_CUSTOM = 'custom';
|
||||
|
||||
public const VERSION_AUTO = -1;
|
||||
@@ -88,6 +89,9 @@ class QRCode{
|
||||
QRImagick::class => [
|
||||
self::OUTPUT_IMAGICK,
|
||||
],
|
||||
QRFpdf::class => [
|
||||
self::OUTPUT_FPDF
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,6 +51,8 @@ use chillerlan\Settings\SettingsContainerAbstract;
|
||||
* @property string $imagickFormat
|
||||
* @property string $imagickBG
|
||||
*
|
||||
* @property string $fpdfMeasureUnit
|
||||
*
|
||||
* @property array $moduleValues
|
||||
*/
|
||||
class QROptions extends SettingsContainerAbstract{
|
||||
|
||||
+23
-1
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace chillerlan\QRCode;
|
||||
|
||||
use function array_values, count, is_array, is_numeric, max, min, sprintf;
|
||||
use function array_values, count, in_array, is_array, is_numeric, max, min, sprintf, strtolower;
|
||||
|
||||
trait QROptionsTrait{
|
||||
|
||||
@@ -241,6 +241,13 @@ trait QROptionsTrait{
|
||||
*/
|
||||
protected $imagickBG = null;
|
||||
|
||||
/**
|
||||
* Measurement unit for FPDF output: pt, mm, cm, in (defaults to "pt")
|
||||
*
|
||||
* @see \FPDF::__construct()
|
||||
*/
|
||||
protected $fpdfMeasureUnit = 'pt';
|
||||
|
||||
/**
|
||||
* Module values map
|
||||
*
|
||||
@@ -367,4 +374,19 @@ trait QROptionsTrait{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the FPDF measurement unit
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
protected function set_fpdfMeasureUnit(string $unit):void{
|
||||
$unit = strtolower($unit);
|
||||
|
||||
if(in_array($unit, ['cm', 'in', 'mm', 'pt'], true)){
|
||||
$this->fpdfMeasureUnit = $unit;
|
||||
}
|
||||
|
||||
// @todo throw or ignore silently?
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* Class QRFpdfTest
|
||||
*
|
||||
* @filesource QRFpdfTest.php
|
||||
* @created 03.06.2020
|
||||
* @package chillerlan\QRCodeTest\Output
|
||||
* @author smiley <smiley@chillerlan.net>
|
||||
* @copyright 2020 smiley
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace chillerlan\QRCodeTest\Output;
|
||||
|
||||
use FPDF;
|
||||
use chillerlan\QRCode\Output\{QRFpdf, QROutputInterface};
|
||||
use chillerlan\QRCode\{QRCode, QROptions};
|
||||
|
||||
use function class_exists, substr;
|
||||
|
||||
/**
|
||||
* Tests the QRFpdf output module
|
||||
*/
|
||||
class QRFpdfTest extends QROutputTestAbstract{
|
||||
|
||||
protected $FQCN = QRFpdf::class;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @internal
|
||||
*/
|
||||
public function setUp():void{
|
||||
|
||||
if(!class_exists(FPDF::class)){
|
||||
$this->markTestSkipped('FPDF not available');
|
||||
return;
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function testSetModuleValues():void{
|
||||
|
||||
$this->options->moduleValues = [
|
||||
// data
|
||||
1024 => [0, 0, 0],
|
||||
4 => [255, 255, 255],
|
||||
];
|
||||
|
||||
$this->outputInterface->dump();
|
||||
|
||||
$this::assertTrue(true); // tricking the code coverage
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function testRenderImage():void{
|
||||
$type = QRCode::OUTPUT_FPDF;
|
||||
|
||||
$this->options->outputType = $type;
|
||||
$this->outputInterface->dump($this::cachefile.$type);
|
||||
|
||||
// substr() to avoid CreationDate
|
||||
$expected = substr(file_get_contents($this::cachefile.$type), 0, 2000);
|
||||
$actual = substr($this->outputInterface->dump(), 0, 2000);
|
||||
|
||||
$this::assertSame($expected, $actual);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user