:octocat: QRStringJSON overhaul

This commit is contained in:
smiley
2024-05-04 19:33:12 +02:00
parent f28605c809
commit 81eb21a7f8
4 changed files with 230 additions and 54 deletions
+69 -23
View File
@@ -18,16 +18,52 @@ use function json_encode;
*
*/
class QRStringJSON extends QROutputAbstract{
use CssColorModuleValueTrait;
final public const MIME_TYPE = 'application/json';
final public const SCHEMA = 'https://raw.githubusercontent.com/chillerlan/php-qrcode/main/src/Output/qrcode.schema.json';
/**
* @inheritDoc
*/
protected function getOutputDimensions():array{
return [$this->moduleCount, $this->moduleCount];
}
/**
* @inheritDoc
* @throws \JsonException
*/
public function dump(string $file = null):string{
$matrix = $this->matrix->getMatrix($this->options->jsonAsBooleans);
$data = json_encode($matrix, $this->options->jsonFlags);;
[$width, $height] = $this->getOutputDimensions();
$version = $this->matrix->getVersion();
$dimension = $version->getDimension();
$json = [
'$schema' => $this::SCHEMA,
'qrcode' => [
'version' => $version->getVersionNumber(),
'eccLevel' => (string)$this->matrix->getEccLevel(),
'matrix' => [
'size' => $dimension,
'quietzoneSize' => (int)(($this->moduleCount - $dimension) / 2),
'maskPattern' => $this->matrix->getMaskPattern()->getPattern(),
'width' => $width,
'height' => $height,
'rows' => [],
],
],
];
foreach($this->matrix->getMatrix() as $y => $row){
$matrixRow = $this->row($y, $row);
if($matrixRow !== null){
$json['qrcode']['matrix']['rows'][] = $matrixRow;
}
}
$data = json_encode($json, $this->options->jsonFlags);;
$this->saveToFile($data, $file);
@@ -35,33 +71,43 @@ class QRStringJSON extends QROutputAbstract{
}
/**
* unused - required by interface
*
* @inheritDoc
* @codeCoverageIgnore
* Creates an array element for a matrix row
*/
protected function prepareModuleValue(mixed $value):string{
return '';
protected function row(int $y, array $row):array|null{
$matrixRow = ['y' => $y, 'modules' => []];
foreach($row as $x => $M_TYPE){
$module = $this->module($x, $y, $M_TYPE);
if($module !== null){
$matrixRow['modules'][] = $module;
}
}
if(!empty($matrixRow['modules'])){
return $matrixRow;
}
// skip empty rows
return null;
}
/**
* unused - required by interface
*
* @inheritDoc
* @codeCoverageIgnore
* Creates an array element for a single module
*/
protected function getDefaultModuleValue(bool $isDark):string{
return '';
}
protected function module(int $x, int $y, int $M_TYPE):array|null{
$isDark = $this->matrix->isDark($M_TYPE);
/**
* unused - required by interface
*
* @inheritDoc
* @codeCoverageIgnore
*/
public static function moduleValueIsValid(mixed $value):bool{
return true;
if(!$this->drawLightModules && !$isDark){
return null;
}
return [
'x' => $x,
'dark' => $isDark,
'layer' => ($this::LAYERNAMES[$M_TYPE] ?? ''),
'value' => $this->getModuleValue($M_TYPE),
];
}
}
+142
View File
@@ -0,0 +1,142 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "chillerlan php-qrcode schema",
"type": "object",
"required": [
"qrcode"
],
"properties": {
"qrcode": {
"$ref": "#/$defs/qrcode"
}
},
"$defs": {
"qrcode": {
"description": "QR Code root element",
"type": "object",
"required": [
"eccLevel",
"matrix",
"version"
],
"properties": {
"version": {
"description": "The QR Code version: [1...40]",
"type": "number",
"minimum": 1,
"maximum": 40
},
"eccLevel": {
"description": "The ECC level: [L, M, Q, H]",
"enum": [
"L",
"M",
"Q",
"H"
]
},
"matrix": {
"$ref": "#/$defs/matrix"
}
}
},
"matrix": {
"description": "The matrix holds the encoded data in a 2-dimensional array of modules.",
"type": "object",
"required": [
"size",
"quietzoneSize",
"maskPattern",
"width",
"height"
],
"properties": {
"size": {
"description": "The side length of the QR symbol, excluding the quiet zone (version * 4 + 17). [21...177]",
"type": "number",
"minimum": 21,
"maximum": 177
},
"quietzoneSize": {
"description": "The size of the quiet zone (margin around the QR symbol).",
"type": "number",
"minimum": 0
},
"maskPattern": {
"description": "The detected mask pattern that was used to mask this matrix. [0...7].",
"type": "number",
"minimum": 0,
"maximum": 7
},
"width": {
"description": "The total width of the matrix, including the quiet zone.",
"type": "number",
"minimum": 21
},
"height": {
"description": "The total height of the matrix, including the quiet zone.",
"type": "number",
"minimum": 21
},
"rows": {
"type": "array",
"items": {
"$ref": "#/$defs/row"
},
"minItems": 0
}
}
},
"row": {
"description": "A row holds an array of modules",
"type": "object",
"required": [
"y",
"modules"
],
"properties": {
"y": {
"description": "The 'y' (vertical) coordinate of this row.",
"type": "number",
"minimum": 0
},
"modules": {
"type": "array",
"items": {
"$ref": "#/$defs/module"
},
"minItems": 0
}
}
},
"module": {
"description": "Represents a single module (pixel) of a QR symbol.",
"type": "object",
"required": [
"dark",
"layer",
"value",
"x"
],
"properties": {
"dark": {
"description": "Indicates whether this module is dark.",
"type": "boolean"
},
"layer": {
"description": "The layer (functional pattern) this module belongs to.",
"type": "string"
},
"value": {
"description": "The value for this module.",
"type": "string"
},
"x": {
"description": "The 'x' (horizontal) coordinate of this module.",
"type": "integer",
"minimum": 0
}
}
}
}
}
+3 -7
View File
@@ -17,7 +17,8 @@ namespace chillerlan\QRCode;
use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Version};
use chillerlan\QRCode\Output\QRMarkupSVG;
use function constant, in_array, is_string, max, min, sprintf, strtolower, strtoupper, trim;
use const JSON_THROW_ON_ERROR, PHP_EOL;
use const JSON_THROW_ON_ERROR, JSON_UNESCAPED_SLASHES, PHP_EOL;
/**
* The QRCode plug-in settings & setter functionality
@@ -408,12 +409,7 @@ trait QROptionsTrait{
*
* @see https://www.php.net/manual/json.constants.php
*/
protected int $jsonFlags = JSON_THROW_ON_ERROR;
/**
* Whether to return matrix values in JSON as booleans or `$M_TYPE` integers
*/
protected bool $jsonAsBooleans = false;
protected int $jsonFlags = JSON_THROW_ON_ERROR|JSON_UNESCAPED_SLASHES;
/*
* QRFpdf settings
+16 -24
View File
@@ -14,25 +14,12 @@ use chillerlan\QRCode\QROptions;
use chillerlan\QRCode\Data\QRMatrix;
use chillerlan\QRCode\Output\{QROutputInterface, QRStringJSON};
use chillerlan\Settings\SettingsContainerInterface;
use PHPUnit\Framework\Attributes\DataProvider;
use function extension_loaded;
/**
*
*/
final class QRStringJSONTest extends QROutputTestAbstract{
/**
* @inheritDoc
*/
protected function setUp():void{
// just in case someone's running this on some weird distro that's been compiled without ext-json
if(!extension_loaded('json')){
$this::markTestSkipped('ext-json not loaded');
}
parent::setUp();
}
use CssColorModuleValueProviderTrait;
protected function getOutputInterface(
SettingsContainerInterface|QROptions $options,
@@ -41,17 +28,22 @@ final class QRStringJSONTest extends QROutputTestAbstract{
return new QRStringJSON($options, $matrix);
}
public static function moduleValueProvider():array{
return [[null, false]];
}
#[DataProvider('moduleValueProvider')]
public function testValidateModuleValues(mixed $value, bool $expected):void{
$this::markTestSkipped('N/A (JSON test)');
}
/**
* @inheritDoc
*/
public function testSetModuleValues():void{
$this::markTestSkipped('N/A (JSON test)');
$this->options->moduleValues = [
// data
QRMatrix::M_DATA_DARK => '#AAA',
QRMatrix::M_DATA => '#BBB',
];
$this->outputInterface = $this->getOutputInterface($this->options, $this->matrix);
$data = $this->outputInterface->dump();
$this::assertStringContainsString('"layer":"data-dark","value":"#AAA"', $data);
$this::assertStringContainsString('"layer":"data","value":"#BBB"', $data);
}
}