Commit 22010e59 authored by luhongguang's avatar luhongguang

update:goods图片

parent c35d0fc1
......@@ -1014,4 +1014,27 @@ class GoodsService
return $data;
}
/**
* 获取图片地址,这里因为bucket可能不同,不保证图片顺序
* @param $picUrlStr
* @return array
*/
private static function getUrlList($picUrlStr)
{
$data = $ksyunParams = [];
$strList = explode(",", $picUrlStr);
foreach ($strList as $str) {
$arr = explode("/", $str);
$bucket = $arr[0];
$objectId = $arr[1];
$ksyunParams[$bucket][] = $objectId;
}
foreach ($ksyunParams as $bucket => $objectIds) {
$ksyun = Ksyun::getDownUrl($objectIds, 'merchant-b', $bucket, 60);
$data = array_merge($data, array_values($ksyun));
}
return $data;
}
}
\ No newline at end of file
生活圈通用业务
\ No newline at end of file
生活圈通用业务
负责人:崔伟峰
\ No newline at end of file
......@@ -3,9 +3,10 @@
"description": "bp api php_services",
"type": "library",
"require": {
"php": ">=7.2",
"php": "7.2.*",
"api/php_utils":"dev-master",
"perftools/php-profiler": "^0.18.0"
"perftools/php-profiler": "^0.18.0",
"endroid/qr-code": "^3.9"
},
"autoload": {
"psr-4": {
......
......@@ -170,4 +170,22 @@ class JwUser
}
return $list;
}
/**
* 给简网同步生活号
*/
public static function saveUpdateLife($params)
{
$url = config('interface', 'service.jw_user.save_update_life');
if (!$url) {
throw new CodeSpecialException("failed");
}
$ret = (new TimeOut())->runPost($url, $params);
if (!$ret) {
throw new CodeSpecialException("timeout");
}
return $ret;
}
}
......@@ -50,7 +50,7 @@ class Ksyun
{
if (!empty($headers))
{
$headers = $headers . "\n";
$headers .= "\n";
}
$StringToSign = $http_method . "\n"
......@@ -65,6 +65,22 @@ class Ksyun
return $signature;
}
/**
* @param string $appid 应用
* @param $scenario
* @param $http_method
* @param $date
* @param $resource
* @param string $headers
* @param string $content_md5
* @param string $content_type
* @return string
*/
public function getTokenV2($appid,$scenario,$http_method,$date,$resource,$headers='',$content_md5='',$content_type='') : string
{
return $this->getToken($http_method,$date,$resource,$headers,$content_md5,$content_type);
}
/**
* 返回bucket
* @param $appid
......
<?php
namespace Api\PhpServices\PG;
class GetImage
{
public static function getCoupon(){
//创建画布
$im = imagecreatetruecolor(500, 600);
$qr = new QR('abc');
$qr_img = $qr->get();
list($code_w,$code_h) = getimagesizefromstring($qr_img);
$codeImg = imagecreatefromstring($qr_img);
imagecopyresized($im, $codeImg, 0, 0, 0, 0, 170, 170, $code_w, $code_h);
Header("Content-Type: image/png");
imagepng ($im);
imagedestroy($im);
}
public static function getLeaderShare(){
}
public static function getGoods(){
}
}
\ No newline at end of file
<?php
namespace Api\PhpServices\PG;
use Endroid\QrCode\QrCode;
class QR
{
private $_content;
private $_size = 300;
private $_logo_url = '';
private $_logo_size = 80;
private $_writeFile = false;
public function setWriteFile(bool $writeFile): void
{
$this->_writeFile = $writeFile;
}
public function setContent($content): void
{
$this->_content = $content;
}
public function setSize(int $size): void
{
$this->_size = $size;
}
public function setLogoUrl(string $logo_url): void
{
$this->_logo_url = $logo_url;
}
public function setLogoSize(int $logo_size): void
{
$this->_logo_size = $logo_size;
}
public function __construct($content)
{
$this->_content = $content;
}
public function get(){
$qr = new QrCode($this->_content);
$qr->setSize($this->_size);
if(!empty($this->_logo_url)){
$qr->setLogoPath($this->_logo_url);
$qr->setLogoSize($this->_logo_size);
}
if (!$this->_writeFile) {
header('Content-Type: ' . $qr->getContentType());
}
return $qr->writeString();
}
}
\ No newline at end of file
......@@ -18,6 +18,13 @@ class XhMongo extends MongoBase
protected function getCollectionName()
{
$sever = str_replace('.','_',$_SERVER['SERVER_NAME']);
$collection = \Yaconf::get('xhprof.common.collection');
if (isset($collection[$sever])) {
return $sever;
}
// 默认使用results
return 'results';
}
}
\ No newline at end of file
<?php
namespace Api\PhpServices\XhProfiler;
class Xhprof
{
public static $xhprof_service;
protected function __construct()
{
}
/**
* 开启 xhprof 性能检测分析
*/
public static function getInstance()
{
if (empty(self::$xhprof_service))
{
return new XhProfilerService();
}
return false;
}
public static function startXhprof($config)
{
if (!self::judgeSwitch($config)) {
return false;
}
self::$xhprof_service = self::getInstance();
if ( !empty(self::$xhprof_service))
{
self::$xhprof_service->start();
}
return true;
}
/**
* 开关决策器
* @return bool
*/
private static function judgeSwitch($config=[])
{
//从 yaconf 获取配置 ,判断当前域名是否要开启
$domain = str_replace('.', '_',$_SERVER['SERVER_NAME']);
return isset($config[$domain]) && $config[$domain] == 1;
}
/**
* 关闭分析器,并且将数据写入mongo
*/
public static function endXhprof()
{
self::$xhprof_service->end($_SERVER['SERVER_NAME']);
}
}
\ No newline at end of file
生活圈商户系统工具类库,配合yaf框架使用的基础库
\ No newline at end of file
生活圈商户系统工具类库,配合yaf框架使用的基础库
负责人:崔伟峰
\ No newline at end of file
......@@ -3,7 +3,7 @@
"description": "bp api php_utils",
"type": "library",
"require": {
"php": ">=7.2",
"php": "7.2.*",
"guzzlehttp/guzzle": "6.3",
"mongodb/mongodb": "1.4.3",
"ext-mbstring": "*",
......
......@@ -52,4 +52,47 @@ class GoodsSkuId
return $tmp . $code;
}
/**
* 验证 str 是否是合法的 goods_sku_id
* @param $str
* @return bool
*/
public static function validGoodSkuId($str)
{
$strlen = strlen($str);
$chklen = 2;
$code = substr($str, -$chklen);
$str = substr($str, 0, $strlen-$chklen);
$strlen -= $chklen;
$base = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
if ($strlen % 2 == 1)
{
$str = '0' . $str;
$strlen += 1;
}
$sum = 0;
$len = $strlen/2;
for ($i=0; $i<$len; $i++)
{
$sum *= 137;
$sum += ord($str[$i]) ^ ord($str[$strlen-$i-1]);
}
$ret = '';
for ($i=$chklen; $i>0; $i--)
{
$idx = $sum % 62;
$sum = ($sum-$idx) / 62;
$ret = $base[$idx] . $ret;
}
return $ret === $code;
}
}
\ No newline at end of file
......@@ -99,7 +99,7 @@ class MonUtil{
if (empty($db_host)) {
$host = 'unknow';
} else {
$host = str_replace(array(".", ":", "/"),array("_", "_", "-"), $db_host);
$host = str_replace(array(".", ":", "/",','),array("_", "_", "-",'_'), $db_host);
}
//记录请求量
......@@ -107,7 +107,7 @@ class MonUtil{
//耗时打点
if(!empty($request_time) && is_numeric($request_time) && $request_time != -799){
$result = self::timing(($db_name.'.'.$host.$operator), "TotalTime", $request_time);
$result = self::timing(($db_name.'.'.$host.$operator), "TotalTime", $request_time,'s');
}
}
......@@ -219,12 +219,13 @@ class MonUtil{
* @param string $module 子模块名字
* @param string $index 指标
* @param int $value
* @param string $type 单位
* @return mixed
*/
public static function timing($module = 'api', $index = '', $value = 0){
public static function timing($module = 'api', $index = '', $value = 0,$type='ms'){
$result = "";
$send_msg = self::getMsg($module, $index, $value, "ms");
$send_msg = self::getMsg($module, $index, $value, $type);
if($send_msg){
$result = self::base($send_msg);
}
......
......@@ -99,6 +99,21 @@ abstract class MongoBase
);
}
$this->database = $this->client->selectDatabase($this->getDatabaseName());
// xhprof 需要 显示的创建固定大小的集合
$db_collection = [];
if ($this->getDatabaseName() === 'xhprof') {
foreach ($this->database->listCollections() as $item){
$db_collection[] = $item['name'];
}
if (!in_array($this->getCollectionName(), $db_collection, true)) {
//默认创建固定20GB大小的集合
$this->database->createCollection($this->getCollectionName(),['capped' => true,'size' => 21474836480]);
}
}
$this->collection = $this->database->selectCollection($this->getCollectionName(), $this->getCollectionOptions());
}
}
......
......@@ -1459,7 +1459,56 @@ class Medoo
return $result;
}
public function insert($table, $datas, $options = null)
public function selectForUpdateSkipLocked($table, $columns = null, $where = null, $options = null)
{
$map = [];
$result = [];
$column_map = [];
$index = 0;
$column = $columns;
$is_single = (is_string($column) && $column !== '*');
$query = $this->exec($this->selectContext($table, $map, $columns, $where, null, $options) . ' FOR UPDATE SKIP LOCKED ', $map);
$this->columnMap($columns, $column_map, true);
if (!$this->statement)
{
return false;
}
if ($columns === '*')
{
return $query->fetchAll(PDO::FETCH_ASSOC);
}
while ($data = $query->fetch(PDO::FETCH_ASSOC))
{
$current_stack = [];
$this->dataMap($data, $columns, $column_map, $current_stack, true, $result);
}
if ($is_single)
{
$single_result = [];
$result_key = $column_map[ $column ][ 0 ];
foreach ($result as $item)
{
$single_result[] = $item[ $result_key ];
}
return $single_result;
}
return $result;
}
public function insert($table, $datas)
{
$stack = [];
$columns = [];
......
......@@ -37,6 +37,7 @@ use Api\PhpUtils\Log\FileLog;
* @method static MysqlBase select($columns, $where, $options)
* @method static MysqlBase selectMaster($columns, $where, $options)
* @method static MysqlBase selectForUpdate($columns, $where, $options)
* * @method static MysqlBase selectForUpdateSkipLocked($columns, $where, $options)
* @method static MysqlBase getMaster($columns, $where, $options)
* @method static MysqlBase get($columns, $where, $options)
* @method static MysqlBase count($columns, $where)
......@@ -105,6 +106,7 @@ abstract class MysqlBase
"selectMaster", // 或使用/*master*/强制路由,对主从实时性要求较高的场景使用,分布式系统尽量用消息代替查主库等其他方案
"getMaster",
"selectForUpdate",
"selectForUpdateSkipLocked",
"insertDuplicate",
];
......
# Changelog
All notable changes to this project will be documented in this file, in reverse chronological order by release.
## 2.0.2 - 2020-07-30
### Changed
- [#71](https://github.com/Bacon/BaconQrCode/issues/71) Upgrade phpunit.
- [#71](https://github.com/Bacon/BaconQrCode/issues/71) Allow tests in vendor bundles for Debian packaging.
- [#71](https://github.com/Bacon/BaconQrCode/issues/71) Update TravisCI config file.
## 2.0.1 - 2020-07-14
### Fixed
- [#69](https://github.com/Bacon/BaconQrCode/pull/69) SimpleCircleEye Class not working properly.
## 2.0.0 - 2018-04-25
### Added
- [#25](https://github.com/Bacon/BaconQrCode/pull/25) allows for setting a more compact text output
- CHANGELOG.md added (how meta)
- Allows more complex shapes for modules
- Allows setting a gradient for the foreground
- Allows transparent backgrounds and alpha channel on all colors
### Changed
- Minimum PHP version changed to 7.1
- Imagick renderer now allows setting different output formats
- New optimized SVG renderer
### Deprecated
- Nothing.
### Removed
- Legacy ZF module support removed
### Fixed
- Non-release files are excluded from composer packages
Copyright (c) 2017, Ben Scholzen 'DASPRiD'
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# QR Code generator
[![Build Status](https://api.travis-ci.org/Bacon/BaconQrCode.png?branch=master)](http://travis-ci.org/Bacon/BaconQrCode)
[![Coverage Status](https://coveralls.io/repos/github/Bacon/BaconQrCode/badge.svg?branch=master)](https://coveralls.io/github/Bacon/BaconQrCode?branch=master)
[![Latest Stable Version](https://poser.pugx.org/bacon/bacon-qr-code/v/stable)](https://packagist.org/packages/bacon/bacon-qr-code)
[![Total Downloads](https://poser.pugx.org/bacon/bacon-qr-code/downloads)](https://packagist.org/packages/bacon/bacon-qr-code)
[![License](https://poser.pugx.org/bacon/bacon-qr-code/license)](https://packagist.org/packages/bacon/bacon-qr-code)
## Introduction
BaconQrCode is a port of QR code portion of the ZXing library. It currently
only features the encoder part, but could later receive the decoder part as
well.
As the Reed Solomon codec implementation of the ZXing library performs quite
slow in PHP, it was exchanged with the implementation by Phil Karn.
## Example usage
```php
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\Image\ImagickImageBackEnd;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
$renderer = new ImageRenderer(
new RendererStyle(400),
new ImagickImageBackEnd()
);
$writer = new Writer($renderer);
$writer->writeFile('Hello World!', 'qrcode.png');
```
## Available image renderer back ends
BaconQrCode comes with multiple back ends for rendering images. Currently included are the following:
- `ImagickImageBackEnd`: renders raster images using the Imagick library
- `SvgImageBackEnd`: renders SVG files using XMLWriter
- `EpsImageBackEnd`: renders EPS files
{
"name": "bacon/bacon-qr-code",
"description": "BaconQrCode is a QR code generator for PHP.",
"license" : "BSD-2-Clause",
"homepage": "https://github.com/Bacon/BaconQrCode",
"require": {
"php": "^7.1 || ^8.0",
"ext-iconv": "*",
"dasprid/enum": "^1.0.3"
},
"suggest": {
"ext-imagick": "to generate QR code images"
},
"authors": [
{
"name": "Ben Scholzen 'DASPRiD'",
"email": "mail@dasprids.de",
"homepage": "https://dasprids.de/",
"role": "Developer"
}
],
"autoload": {
"psr-4": {
"BaconQrCode\\": "src/"
}
},
"require-dev": {
"phpunit/phpunit": "^7 | ^8 | ^9",
"squizlabs/php_codesniffer": "^3.4",
"phly/keep-a-changelog": "^1.4"
}
}
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="BaconQrCode Tests">
<directory>./test</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
</whitelist>
</filter>
</phpunit>
<?php
declare(strict_types = 1);
namespace BaconQrCode\Common;
use BaconQrCode\Exception\InvalidArgumentException;
use SplFixedArray;
/**
* A simple, fast array of bits.
*/
final class BitArray
{
/**
* Bits represented as an array of integers.
*
* @var SplFixedArray<int>
*/
private $bits;
/**
* Size of the bit array in bits.
*
* @var int
*/
private $size;
/**
* Creates a new bit array with a given size.
*/
public function __construct(int $size = 0)
{
$this->size = $size;
$this->bits = SplFixedArray::fromArray(array_fill(0, ($this->size + 31) >> 3, 0));
}
/**
* Gets the size in bits.
*/
public function getSize() : int
{
return $this->size;
}
/**
* Gets the size in bytes.
*/
public function getSizeInBytes() : int
{
return ($this->size + 7) >> 3;
}
/**
* Ensures that the array has a minimum capacity.
*/
public function ensureCapacity(int $size) : void
{
if ($size > count($this->bits) << 5) {
$this->bits->setSize(($size + 31) >> 5);
}
}
/**
* Gets a specific bit.
*/
public function get(int $i) : bool
{
return 0 !== ($this->bits[$i >> 5] & (1 << ($i & 0x1f)));
}
/**
* Sets a specific bit.
*/
public function set(int $i) : void
{
$this->bits[$i >> 5] = $this->bits[$i >> 5] | 1 << ($i & 0x1f);
}
/**
* Flips a specific bit.
*/
public function flip(int $i) : void
{
$this->bits[$i >> 5] ^= 1 << ($i & 0x1f);
}
/**
* Gets the next set bit position from a given position.
*/
public function getNextSet(int $from) : int
{
if ($from >= $this->size) {
return $this->size;
}
$bitsOffset = $from >> 5;
$currentBits = $this->bits[$bitsOffset];
$bitsLength = count($this->bits);
$currentBits &= ~((1 << ($from & 0x1f)) - 1);
while (0 === $currentBits) {
if (++$bitsOffset === $bitsLength) {
return $this->size;
}
$currentBits = $this->bits[$bitsOffset];
}
$result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits);
return $result > $this->size ? $this->size : $result;
}
/**
* Gets the next unset bit position from a given position.
*/
public function getNextUnset(int $from) : int
{
if ($from >= $this->size) {
return $this->size;
}
$bitsOffset = $from >> 5;
$currentBits = ~$this->bits[$bitsOffset];
$bitsLength = count($this->bits);
$currentBits &= ~((1 << ($from & 0x1f)) - 1);
while (0 === $currentBits) {
if (++$bitsOffset === $bitsLength) {
return $this->size;
}
$currentBits = ~$this->bits[$bitsOffset];
}
$result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits);
return $result > $this->size ? $this->size : $result;
}
/**
* Sets a bulk of bits.
*/
public function setBulk(int $i, int $newBits) : void
{
$this->bits[$i >> 5] = $newBits;
}
/**
* Sets a range of bits.
*
* @throws InvalidArgumentException if end is smaller than start
*/
public function setRange(int $start, int $end) : void
{
if ($end < $start) {
throw new InvalidArgumentException('End must be greater or equal to start');
}
if ($end === $start) {
return;
}
--$end;
$firstInt = $start >> 5;
$lastInt = $end >> 5;
for ($i = $firstInt; $i <= $lastInt; ++$i) {
$firstBit = $i > $firstInt ? 0 : $start & 0x1f;
$lastBit = $i < $lastInt ? 31 : $end & 0x1f;
if (0 === $firstBit && 31 === $lastBit) {
$mask = 0x7fffffff;
} else {
$mask = 0;
for ($j = $firstBit; $j < $lastBit; ++$j) {
$mask |= 1 << $j;
}
}
$this->bits[$i] = $this->bits[$i] | $mask;
}
}
/**
* Clears the bit array, unsetting every bit.
*/
public function clear() : void
{
$bitsLength = count($this->bits);
for ($i = 0; $i < $bitsLength; ++$i) {
$this->bits[$i] = 0;
}
}
/**
* Checks if a range of bits is set or not set.
* @throws InvalidArgumentException if end is smaller than start
*/
public function isRange(int $start, int $end, bool $value) : bool
{
if ($end < $start) {
throw new InvalidArgumentException('End must be greater or equal to start');
}
if ($end === $start) {
return true;
}
--$end;
$firstInt = $start >> 5;
$lastInt = $end >> 5;
for ($i = $firstInt; $i <= $lastInt; ++$i) {
$firstBit = $i > $firstInt ? 0 : $start & 0x1f;
$lastBit = $i < $lastInt ? 31 : $end & 0x1f;
if (0 === $firstBit && 31 === $lastBit) {
$mask = 0x7fffffff;
} else {
$mask = 0;
for ($j = $firstBit; $j <= $lastBit; ++$j) {
$mask |= 1 << $j;
}
}
if (($this->bits[$i] & $mask) !== ($value ? $mask : 0)) {
return false;
}
}
return true;
}
/**
* Appends a bit to the array.
*/
public function appendBit(bool $bit) : void
{
$this->ensureCapacity($this->size + 1);
if ($bit) {
$this->bits[$this->size >> 5] = $this->bits[$this->size >> 5] | (1 << ($this->size & 0x1f));
}
++$this->size;
}
/**
* Appends a number of bits (up to 32) to the array.
* @throws InvalidArgumentException if num bits is not between 0 and 32
*/
public function appendBits(int $value, int $numBits) : void
{
if ($numBits < 0 || $numBits > 32) {
throw new InvalidArgumentException('Num bits must be between 0 and 32');
}
$this->ensureCapacity($this->size + $numBits);
for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) {
$this->appendBit((($value >> ($numBitsLeft - 1)) & 0x01) === 1);
}
}
/**
* Appends another bit array to this array.
*/
public function appendBitArray(self $other) : void
{
$otherSize = $other->getSize();
$this->ensureCapacity($this->size + $other->getSize());
for ($i = 0; $i < $otherSize; ++$i) {
$this->appendBit($other->get($i));
}
}
/**
* Makes an exclusive-or comparision on the current bit array.
*
* @throws InvalidArgumentException if sizes don't match
*/
public function xorBits(self $other) : void
{
$bitsLength = count($this->bits);
$otherBits = $other->getBitArray();
if ($bitsLength !== count($otherBits)) {
throw new InvalidArgumentException('Sizes don\'t match');
}
for ($i = 0; $i < $bitsLength; ++$i) {
$this->bits[$i] = $this->bits[$i] ^ $otherBits[$i];
}
}
/**
* Converts the bit array to a byte array.
*
* @return SplFixedArray<int>
*/
public function toBytes(int $bitOffset, int $numBytes) : SplFixedArray
{
$bytes = new SplFixedArray($numBytes);
for ($i = 0; $i < $numBytes; ++$i) {
$byte = 0;
for ($j = 0; $j < 8; ++$j) {
if ($this->get($bitOffset)) {
$byte |= 1 << (7 - $j);
}
++$bitOffset;
}
$bytes[$i] = $byte;
}
return $bytes;
}
/**
* Gets the internal bit array.
*
* @return SplFixedArray<int>
*/
public function getBitArray() : SplFixedArray
{
return $this->bits;
}
/**
* Reverses the array.
*/
public function reverse() : void
{
$newBits = new SplFixedArray(count($this->bits));
for ($i = 0; $i < $this->size; ++$i) {
if ($this->get($this->size - $i - 1)) {
$newBits[$i >> 5] = $newBits[$i >> 5] | (1 << ($i & 0x1f));
}
}
$this->bits = $newBits;
}
/**
* Returns a string representation of the bit array.
*/
public function __toString() : string
{
$result = '';
for ($i = 0; $i < $this->size; ++$i) {
if (0 === ($i & 0x07)) {
$result .= ' ';
}
$result .= $this->get($i) ? 'X' : '.';
}
return $result;
}
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Common;
use BaconQrCode\Exception\InvalidArgumentException;
use SplFixedArray;
/**
* Bit matrix.
*
* Represents a 2D matrix of bits. In function arguments below, and throughout
* the common module, x is the column position, and y is the row position. The
* ordering is always x, y. The origin is at the top-left.
*/
class BitMatrix
{
/**
* Width of the bit matrix.
*
* @var int
*/
private $width;
/**
* Height of the bit matrix.
*
* @var int
*/
private $height;
/**
* Size in bits of each individual row.
*
* @var int
*/
private $rowSize;
/**
* Bits representation.
*
* @var SplFixedArray<int>
*/
private $bits;
/**
* @throws InvalidArgumentException if a dimension is smaller than zero
*/
public function __construct(int $width, int $height = null)
{
if (null === $height) {
$height = $width;
}
if ($width < 1 || $height < 1) {
throw new InvalidArgumentException('Both dimensions must be greater than zero');
}
$this->width = $width;
$this->height = $height;
$this->rowSize = ($width + 31) >> 5;
$this->bits = SplFixedArray::fromArray(array_fill(0, $this->rowSize * $height, 0));
}
/**
* Gets the requested bit, where true means black.
*/
public function get(int $x, int $y) : bool
{
$offset = $y * $this->rowSize + ($x >> 5);
return 0 !== (BitUtils::unsignedRightShift($this->bits[$offset], ($x & 0x1f)) & 1);
}
/**
* Sets the given bit to true.
*/
public function set(int $x, int $y) : void
{
$offset = $y * $this->rowSize + ($x >> 5);
$this->bits[$offset] = $this->bits[$offset] | (1 << ($x & 0x1f));
}
/**
* Flips the given bit.
*/
public function flip(int $x, int $y) : void
{
$offset = $y * $this->rowSize + ($x >> 5);
$this->bits[$offset] = $this->bits[$offset] ^ (1 << ($x & 0x1f));
}
/**
* Clears all bits (set to false).
*/
public function clear() : void
{
$max = count($this->bits);
for ($i = 0; $i < $max; ++$i) {
$this->bits[$i] = 0;
}
}
/**
* Sets a square region of the bit matrix to true.
*
* @throws InvalidArgumentException if left or top are negative
* @throws InvalidArgumentException if width or height are smaller than 1
* @throws InvalidArgumentException if region does not fit into the matix
*/
public function setRegion(int $left, int $top, int $width, int $height) : void
{
if ($top < 0 || $left < 0) {
throw new InvalidArgumentException('Left and top must be non-negative');
}
if ($height < 1 || $width < 1) {
throw new InvalidArgumentException('Width and height must be at least 1');
}
$right = $left + $width;
$bottom = $top + $height;
if ($bottom > $this->height || $right > $this->width) {
throw new InvalidArgumentException('The region must fit inside the matrix');
}
for ($y = $top; $y < $bottom; ++$y) {
$offset = $y * $this->rowSize;
for ($x = $left; $x < $right; ++$x) {
$index = $offset + ($x >> 5);
$this->bits[$index] = $this->bits[$index] | (1 << ($x & 0x1f));
}
}
}
/**
* A fast method to retrieve one row of data from the matrix as a BitArray.
*/
public function getRow(int $y, BitArray $row = null) : BitArray
{
if (null === $row || $row->getSize() < $this->width) {
$row = new BitArray($this->width);
}
$offset = $y * $this->rowSize;
for ($x = 0; $x < $this->rowSize; ++$x) {
$row->setBulk($x << 5, $this->bits[$offset + $x]);
}
return $row;
}
/**
* Sets a row of data from a BitArray.
*/
public function setRow(int $y, BitArray $row) : void
{
$bits = $row->getBitArray();
for ($i = 0; $i < $this->rowSize; ++$i) {
$this->bits[$y * $this->rowSize + $i] = $bits[$i];
}
}
/**
* This is useful in detecting the enclosing rectangle of a 'pure' barcode.
*
* @return int[]|null
*/
public function getEnclosingRectangle() : ?array
{
$left = $this->width;
$top = $this->height;
$right = -1;
$bottom = -1;
for ($y = 0; $y < $this->height; ++$y) {
for ($x32 = 0; $x32 < $this->rowSize; ++$x32) {
$bits = $this->bits[$y * $this->rowSize + $x32];
if (0 !== $bits) {
if ($y < $top) {
$top = $y;
}
if ($y > $bottom) {
$bottom = $y;
}
if ($x32 * 32 < $left) {
$bit = 0;
while (($bits << (31 - $bit)) === 0) {
$bit++;
}
if (($x32 * 32 + $bit) < $left) {
$left = $x32 * 32 + $bit;
}
}
}
if ($x32 * 32 + 31 > $right) {
$bit = 31;
while (0 === BitUtils::unsignedRightShift($bits, $bit)) {
--$bit;
}
if (($x32 * 32 + $bit) > $right) {
$right = $x32 * 32 + $bit;
}
}
}
}
$width = $right - $left;
$height = $bottom - $top;
if ($width < 0 || $height < 0) {
return null;
}
return [$left, $top, $width, $height];
}
/**
* Gets the most top left set bit.
*
* This is useful in detecting a corner of a 'pure' barcode.
*
* @return int[]|null
*/
public function getTopLeftOnBit() : ?array
{
$bitsOffset = 0;
while ($bitsOffset < count($this->bits) && 0 === $this->bits[$bitsOffset]) {
++$bitsOffset;
}
if (count($this->bits) === $bitsOffset) {
return null;
}
$x = intdiv($bitsOffset, $this->rowSize);
$y = ($bitsOffset % $this->rowSize) << 5;
$bits = $this->bits[$bitsOffset];
$bit = 0;
while (0 === ($bits << (31 - $bit))) {
++$bit;
}
$x += $bit;
return [$x, $y];
}
/**
* Gets the most bottom right set bit.
*
* This is useful in detecting a corner of a 'pure' barcode.
*
* @return int[]|null
*/
public function getBottomRightOnBit() : ?array
{
$bitsOffset = count($this->bits) - 1;
while ($bitsOffset >= 0 && 0 === $this->bits[$bitsOffset]) {
--$bitsOffset;
}
if ($bitsOffset < 0) {
return null;
}
$x = intdiv($bitsOffset, $this->rowSize);
$y = ($bitsOffset % $this->rowSize) << 5;
$bits = $this->bits[$bitsOffset];
$bit = 0;
while (0 === BitUtils::unsignedRightShift($bits, $bit)) {
--$bit;
}
$x += $bit;
return [$x, $y];
}
/**
* Gets the width of the matrix,
*/
public function getWidth() : int
{
return $this->width;
}
/**
* Gets the height of the matrix.
*/
public function getHeight() : int
{
return $this->height;
}
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Common;
/**
* General bit utilities.
*
* All utility methods are based on 32-bit integers and also work on 64-bit
* systems.
*/
final class BitUtils
{
private function __construct()
{
}
/**
* Performs an unsigned right shift.
*
* This is the same as the unsigned right shift operator ">>>" in other
* languages.
*/
public static function unsignedRightShift(int $a, int $b) : int
{
return (
$a >= 0
? $a >> $b
: (($a & 0x7fffffff) >> $b) | (0x40000000 >> ($b - 1))
);
}
/**
* Gets the number of trailing zeros.
*/
public static function numberOfTrailingZeros(int $i) : int
{
$lastPos = strrpos(str_pad(decbin($i), 32, '0', STR_PAD_LEFT), '1');
return $lastPos === false ? 32 : 31 - $lastPos;
}
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Common;
use BaconQrCode\Exception\InvalidArgumentException;
use DASPRiD\Enum\AbstractEnum;
/**
* Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1 of ISO 18004.
*
* @method static self CP437()
* @method static self ISO8859_1()
* @method static self ISO8859_2()
* @method static self ISO8859_3()
* @method static self ISO8859_4()
* @method static self ISO8859_5()
* @method static self ISO8859_6()
* @method static self ISO8859_7()
* @method static self ISO8859_8()
* @method static self ISO8859_9()
* @method static self ISO8859_10()
* @method static self ISO8859_11()
* @method static self ISO8859_12()
* @method static self ISO8859_13()
* @method static self ISO8859_14()
* @method static self ISO8859_15()
* @method static self ISO8859_16()
* @method static self SJIS()
* @method static self CP1250()
* @method static self CP1251()
* @method static self CP1252()
* @method static self CP1256()
* @method static self UNICODE_BIG_UNMARKED()
* @method static self UTF8()
* @method static self ASCII()
* @method static self BIG5()
* @method static self GB18030()
* @method static self EUC_KR()
*/
final class CharacterSetEci extends AbstractEnum
{
protected const CP437 = [[0, 2]];
protected const ISO8859_1 = [[1, 3], 'ISO-8859-1'];
protected const ISO8859_2 = [[4], 'ISO-8859-2'];
protected const ISO8859_3 = [[5], 'ISO-8859-3'];
protected const ISO8859_4 = [[6], 'ISO-8859-4'];
protected const ISO8859_5 = [[7], 'ISO-8859-5'];
protected const ISO8859_6 = [[8], 'ISO-8859-6'];
protected const ISO8859_7 = [[9], 'ISO-8859-7'];
protected const ISO8859_8 = [[10], 'ISO-8859-8'];
protected const ISO8859_9 = [[11], 'ISO-8859-9'];
protected const ISO8859_10 = [[12], 'ISO-8859-10'];
protected const ISO8859_11 = [[13], 'ISO-8859-11'];
protected const ISO8859_12 = [[14], 'ISO-8859-12'];
protected const ISO8859_13 = [[15], 'ISO-8859-13'];
protected const ISO8859_14 = [[16], 'ISO-8859-14'];
protected const ISO8859_15 = [[17], 'ISO-8859-15'];
protected const ISO8859_16 = [[18], 'ISO-8859-16'];
protected const SJIS = [[20], 'Shift_JIS'];
protected const CP1250 = [[21], 'windows-1250'];
protected const CP1251 = [[22], 'windows-1251'];
protected const CP1252 = [[23], 'windows-1252'];
protected const CP1256 = [[24], 'windows-1256'];
protected const UNICODE_BIG_UNMARKED = [[25], 'UTF-16BE', 'UnicodeBig'];
protected const UTF8 = [[26], 'UTF-8'];
protected const ASCII = [[27, 170], 'US-ASCII'];
protected const BIG5 = [[28]];
protected const GB18030 = [[29], 'GB2312', 'EUC_CN', 'GBK'];
protected const EUC_KR = [[30], 'EUC-KR'];
/**
* @var int[]
*/
private $values;
/**
* @var string[]
*/
private $otherEncodingNames;
/**
* @var array<int, self>|null
*/
private static $valueToEci;
/**
* @var array<string, self>|null
*/
private static $nameToEci;
public function __construct(array $values, string ...$otherEncodingNames)
{
$this->values = $values;
$this->otherEncodingNames = $otherEncodingNames;
}
/**
* Returns the primary value.
*/
public function getValue() : int
{
return $this->values[0];
}
/**
* Gets character set ECI by value.
*
* Returns the representing ECI of a given value, or null if it is legal but unsupported.
*
* @throws InvalidArgumentException if value is not between 0 and 900
*/
public static function getCharacterSetEciByValue(int $value) : ?self
{
if ($value < 0 || $value >= 900) {
throw new InvalidArgumentException('Value must be between 0 and 900');
}
$valueToEci = self::valueToEci();
if (! array_key_exists($value, $valueToEci)) {
return null;
}
return $valueToEci[$value];
}
/**
* Returns character set ECI by name.
*
* Returns the representing ECI of a given name, or null if it is legal but unsupported
*/
public static function getCharacterSetEciByName(string $name) : ?self
{
$nameToEci = self::nameToEci();
$name = strtolower($name);
if (! array_key_exists($name, $nameToEci)) {
return null;
}
return $nameToEci[$name];
}
private static function valueToEci() : array
{
if (null !== self::$valueToEci) {
return self::$valueToEci;
}
self::$valueToEci = [];
foreach (self::values() as $eci) {
foreach ($eci->values as $value) {
self::$valueToEci[$value] = $eci;
}
}
return self::$valueToEci;
}
private static function nameToEci() : array
{
if (null !== self::$nameToEci) {
return self::$nameToEci;
}
self::$nameToEci = [];
foreach (self::values() as $eci) {
self::$nameToEci[strtolower($eci->name())] = $eci;
foreach ($eci->otherEncodingNames as $name) {
self::$nameToEci[strtolower($name)] = $eci;
}
}
return self::$nameToEci;
}
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Common;
/**
* Encapsulates the parameters for one error-correction block in one symbol version.
*
* This includes the number of data codewords, and the number of times a block with these parameters is used
* consecutively in the QR code version's format.
*/
final class EcBlock
{
/**
* How many times the block is used.
*
* @var int
*/
private $count;
/**
* Number of data codewords.
*
* @var int
*/
private $dataCodewords;
public function __construct(int $count, int $dataCodewords)
{
$this->count = $count;
$this->dataCodewords = $dataCodewords;
}
/**
* Returns how many times the block is used.
*/
public function getCount() : int
{
return $this->count;
}
/**
* Returns the number of data codewords.
*/
public function getDataCodewords() : int
{
return $this->dataCodewords;
}
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Common;
/**
* Encapsulates a set of error-correction blocks in one symbol version.
*
* Most versions will use blocks of differing sizes within one version, so, this encapsulates the parameters for each
* set of blocks. It also holds the number of error-correction codewords per block since it will be the same across all
* blocks within one version.
*/
final class EcBlocks
{
/**
* Number of EC codewords per block.
*
* @var int
*/
private $ecCodewordsPerBlock;
/**
* List of EC blocks.
*
* @var EcBlock[]
*/
private $ecBlocks;
public function __construct(int $ecCodewordsPerBlock, EcBlock ...$ecBlocks)
{
$this->ecCodewordsPerBlock = $ecCodewordsPerBlock;
$this->ecBlocks = $ecBlocks;
}
/**
* Returns the number of EC codewords per block.
*/
public function getEcCodewordsPerBlock() : int
{
return $this->ecCodewordsPerBlock;
}
/**
* Returns the total number of EC block appearances.
*/
public function getNumBlocks() : int
{
$total = 0;
foreach ($this->ecBlocks as $ecBlock) {
$total += $ecBlock->getCount();
}
return $total;
}
/**
* Returns the total count of EC codewords.
*/
public function getTotalEcCodewords() : int
{
return $this->ecCodewordsPerBlock * $this->getNumBlocks();
}
/**
* Returns the EC blocks included in this collection.
*
* @return EcBlock[]
*/
public function getEcBlocks() : array
{
return $this->ecBlocks;
}
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Common;
use BaconQrCode\Exception\OutOfBoundsException;
use DASPRiD\Enum\AbstractEnum;
/**
* Enum representing the four error correction levels.
*
* @method static self L() ~7% correction
* @method static self M() ~15% correction
* @method static self Q() ~25% correction
* @method static self H() ~30% correction
*/
final class ErrorCorrectionLevel extends AbstractEnum
{
protected const L = [0x01];
protected const M = [0x00];
protected const Q = [0x03];
protected const H = [0x02];
/**
* @var int
*/
private $bits;
protected function __construct(int $bits)
{
$this->bits = $bits;
}
/**
* @throws OutOfBoundsException if number of bits is invalid
*/
public static function forBits(int $bits) : self
{
switch ($bits) {
case 0:
return self::M();
case 1:
return self::L();
case 2:
return self::H();
case 3:
return self::Q();
}
throw new OutOfBoundsException('Invalid number of bits');
}
/**
* Returns the two bits used to encode this error correction level.
*/
public function getBits() : int
{
return $this->bits;
}
}
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
/**
* Encapsulates a QR Code's format information, including the data mask used and error correction level.
*/
class FormatInformation
{
/**
* Mask for format information.
*/
private const FORMAT_INFO_MASK_QR = 0x5412;
/**
* Lookup table for decoding format information.
*
* See ISO 18004:2006, Annex C, Table C.1
*/
private const FORMAT_INFO_DECODE_LOOKUP = [
[0x5412, 0x00],
[0x5125, 0x01],
[0x5e7c, 0x02],
[0x5b4b, 0x03],
[0x45f9, 0x04],
[0x40ce, 0x05],
[0x4f97, 0x06],
[0x4aa0, 0x07],
[0x77c4, 0x08],
[0x72f3, 0x09],
[0x7daa, 0x0a],
[0x789d, 0x0b],
[0x662f, 0x0c],
[0x6318, 0x0d],
[0x6c41, 0x0e],
[0x6976, 0x0f],
[0x1689, 0x10],
[0x13be, 0x11],
[0x1ce7, 0x12],
[0x19d0, 0x13],
[0x0762, 0x14],
[0x0255, 0x15],
[0x0d0c, 0x16],
[0x083b, 0x17],
[0x355f, 0x18],
[0x3068, 0x19],
[0x3f31, 0x1a],
[0x3a06, 0x1b],
[0x24b4, 0x1c],
[0x2183, 0x1d],
[0x2eda, 0x1e],
[0x2bed, 0x1f],
];
/**
* Offset i holds the number of 1 bits in the binary representation of i.
*
* @var array
*/
private const BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];
/**
* Error correction level.
*
* @var ErrorCorrectionLevel
*/
private $ecLevel;
/**
* Data mask.
*
* @var int
*/
private $dataMask;
protected function __construct(int $formatInfo)
{
$this->ecLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x3);
$this->dataMask = $formatInfo & 0x7;
}
/**
* Checks how many bits are different between two integers.
*/
public static function numBitsDiffering(int $a, int $b) : int
{
$a ^= $b;
return (
self::BITS_SET_IN_HALF_BYTE[$a & 0xf]
+ self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 4) & 0xf)]
+ self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 8) & 0xf)]
+ self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 12) & 0xf)]
+ self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 16) & 0xf)]
+ self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 20) & 0xf)]
+ self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 24) & 0xf)]
+ self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 28) & 0xf)]
);
}
/**
* Decodes format information.
*/
public static function decodeFormatInformation(int $maskedFormatInfo1, int $maskedFormatInfo2) : ?self
{
$formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2);
if (null !== $formatInfo) {
return $formatInfo;
}
// Should return null, but, some QR codes apparently do not mask this info. Try again by actually masking the
// pattern first.
return self::doDecodeFormatInformation(
$maskedFormatInfo1 ^ self::FORMAT_INFO_MASK_QR,
$maskedFormatInfo2 ^ self::FORMAT_INFO_MASK_QR
);
}
/**
* Internal method for decoding format information.
*/
private static function doDecodeFormatInformation(int $maskedFormatInfo1, int $maskedFormatInfo2) : ?self
{
$bestDifference = PHP_INT_MAX;
$bestFormatInfo = 0;
foreach (self::FORMAT_INFO_DECODE_LOOKUP as $decodeInfo) {
$targetInfo = $decodeInfo[0];
if ($targetInfo === $maskedFormatInfo1 || $targetInfo === $maskedFormatInfo2) {
// Found an exact match
return new self($decodeInfo[1]);
}
$bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo);
if ($bitsDifference < $bestDifference) {
$bestFormatInfo = $decodeInfo[1];
$bestDifference = $bitsDifference;
}
if ($maskedFormatInfo1 !== $maskedFormatInfo2) {
// Also try the other option
$bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo);
if ($bitsDifference < $bestDifference) {
$bestFormatInfo = $decodeInfo[1];
$bestDifference = $bitsDifference;
}
}
}
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match.
if ($bestDifference <= 3) {
return new self($bestFormatInfo);
}
return null;
}
/**
* Returns the error correction level.
*/
public function getErrorCorrectionLevel() : ErrorCorrectionLevel
{
return $this->ecLevel;
}
/**
* Returns the data mask.
*/
public function getDataMask() : int
{
return $this->dataMask;
}
/**
* Hashes the code of the EC level.
*/
public function hashCode() : int
{
return ($this->ecLevel->getBits() << 3) | $this->dataMask;
}
/**
* Verifies if this instance equals another one.
*/
public function equals(self $other) : bool
{
return (
$this->ecLevel === $other->ecLevel
&& $this->dataMask === $other->dataMask
);
}
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Common;
use DASPRiD\Enum\AbstractEnum;
/**
* Enum representing various modes in which data can be encoded to bits.
*
* @method static self TERMINATOR()
* @method static self NUMERIC()
* @method static self ALPHANUMERIC()
* @method static self STRUCTURED_APPEND()
* @method static self BYTE()
* @method static self ECI()
* @method static self KANJI()
* @method static self FNC1_FIRST_POSITION()
* @method static self FNC1_SECOND_POSITION()
* @method static self HANZI()
*/
final class Mode extends AbstractEnum
{
protected const TERMINATOR = [[0, 0, 0], 0x00];
protected const NUMERIC = [[10, 12, 14], 0x01];
protected const ALPHANUMERIC = [[9, 11, 13], 0x02];
protected const STRUCTURED_APPEND = [[0, 0, 0], 0x03];
protected const BYTE = [[8, 16, 16], 0x04];
protected const ECI = [[0, 0, 0], 0x07];
protected const KANJI = [[8, 10, 12], 0x08];
protected const FNC1_FIRST_POSITION = [[0, 0, 0], 0x05];
protected const FNC1_SECOND_POSITION = [[0, 0, 0], 0x09];
protected const HANZI = [[8, 10, 12], 0x0d];
/**
* @var int[]
*/
private $characterCountBitsForVersions;
/**
* @var int
*/
private $bits;
protected function __construct(array $characterCountBitsForVersions, int $bits)
{
$this->characterCountBitsForVersions = $characterCountBitsForVersions;
$this->bits = $bits;
}
/**
* Returns the number of bits used in a specific QR code version.
*/
public function getCharacterCountBits(Version $version) : int
{
$number = $version->getVersionNumber();
if ($number <= 9) {
$offset = 0;
} elseif ($number <= 26) {
$offset = 1;
} else {
$offset = 2;
}
return $this->characterCountBitsForVersions[$offset];
}
/**
* Returns the four bits used to encode this mode.
*/
public function getBits() : int
{
return $this->bits;
}
}
This diff is collapsed.
This diff is collapsed.
<?php
declare(strict_types = 1);
namespace BaconQrCode\Encoder;
use SplFixedArray;
/**
* Block pair.
*/
final class BlockPair
{
/**
* Data bytes in the block.
*
* @var SplFixedArray<int>
*/
private $dataBytes;
/**
* Error correction bytes in the block.
*
* @var SplFixedArray<int>
*/
private $errorCorrectionBytes;
/**
* Creates a new block pair.
*
* @param SplFixedArray<int> $data
* @param SplFixedArray<int> $errorCorrection
*/
public function __construct(SplFixedArray $data, SplFixedArray $errorCorrection)
{
$this->dataBytes = $data;
$this->errorCorrectionBytes = $errorCorrection;
}
/**
* Gets the data bytes.
*
* @return SplFixedArray<int>
*/
public function getDataBytes() : SplFixedArray
{
return $this->dataBytes;
}
/**
* Gets the error correction bytes.
*
* @return SplFixedArray<int>
*/
public function getErrorCorrectionBytes() : SplFixedArray
{
return $this->errorCorrectionBytes;
}
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Encoder;
use SplFixedArray;
use Traversable;
/**
* Byte matrix.
*/
final class ByteMatrix
{
/**
* Bytes in the matrix, represented as array.
*
* @var SplFixedArray<SplFixedArray<int>>
*/
private $bytes;
/**
* Width of the matrix.
*
* @var int
*/
private $width;
/**
* Height of the matrix.
*
* @var int
*/
private $height;
public function __construct(int $width, int $height)
{
$this->height = $height;
$this->width = $width;
$this->bytes = new SplFixedArray($height);
for ($y = 0; $y < $height; ++$y) {
$this->bytes[$y] = SplFixedArray::fromArray(array_fill(0, $width, 0));
}
}
/**
* Gets the width of the matrix.
*/
public function getWidth() : int
{
return $this->width;
}
/**
* Gets the height of the matrix.
*/
public function getHeight() : int
{
return $this->height;
}
/**
* Gets the internal representation of the matrix.
*
* @return SplFixedArray<SplFixedArray<int>>
*/
public function getArray() : SplFixedArray
{
return $this->bytes;
}
/**
* @return Traversable<int>
*/
public function getBytes() : Traversable
{
foreach ($this->bytes as $row) {
foreach ($row as $byte) {
yield $byte;
}
}
}
/**
* Gets the byte for a specific position.
*/
public function get(int $x, int $y) : int
{
return $this->bytes[$y][$x];
}
/**
* Sets the byte for a specific position.
*/
public function set(int $x, int $y, int $value) : void
{
$this->bytes[$y][$x] = $value;
}
/**
* Clears the matrix with a specific value.
*/
public function clear(int $value) : void
{
for ($y = 0; $y < $this->height; ++$y) {
for ($x = 0; $x < $this->width; ++$x) {
$this->bytes[$y][$x] = $value;
}
}
}
public function __clone()
{
$this->bytes = clone $this->bytes;
foreach ($this->bytes as $index => $row) {
$this->bytes[$index] = clone $row;
}
}
/**
* Returns a string representation of the matrix.
*/
public function __toString() : string
{
$result = '';
for ($y = 0; $y < $this->height; $y++) {
for ($x = 0; $x < $this->width; $x++) {
switch ($this->bytes[$y][$x]) {
case 0:
$result .= ' 0';
break;
case 1:
$result .= ' 1';
break;
default:
$result .= ' ';
break;
}
}
$result .= "\n";
}
return $result;
}
}
This diff is collapsed.
<?php
declare(strict_types = 1);
namespace BaconQrCode\Encoder;
use BaconQrCode\Common\BitUtils;
use BaconQrCode\Exception\InvalidArgumentException;
/**
* Mask utility.
*/
final class MaskUtil
{
/**#@+
* Penalty weights from section 6.8.2.1
*/
const N1 = 3;
const N2 = 3;
const N3 = 40;
const N4 = 10;
/**#@-*/
private function __construct()
{
}
/**
* Applies mask penalty rule 1 and returns the penalty.
*
* Finds repetitive cells with the same color and gives penalty to them.
* Example: 00000 or 11111.
*/
public static function applyMaskPenaltyRule1(ByteMatrix $matrix) : int
{
return (
self::applyMaskPenaltyRule1Internal($matrix, true)
+ self::applyMaskPenaltyRule1Internal($matrix, false)
);
}
/**
* Applies mask penalty rule 2 and returns the penalty.
*
* Finds 2x2 blocks with the same color and gives penalty to them. This is
* actually equivalent to the spec's rule, which is to find MxN blocks and
* give a penalty proportional to (M-1)x(N-1), because this is the number of
* 2x2 blocks inside such a block.
*/
public static function applyMaskPenaltyRule2(ByteMatrix $matrix) : int
{
$penalty = 0;
$array = $matrix->getArray();
$width = $matrix->getWidth();
$height = $matrix->getHeight();
for ($y = 0; $y < $height - 1; ++$y) {
for ($x = 0; $x < $width - 1; ++$x) {
$value = $array[$y][$x];
if ($value === $array[$y][$x + 1]
&& $value === $array[$y + 1][$x]
&& $value === $array[$y + 1][$x + 1]
) {
++$penalty;
}
}
}
return self::N2 * $penalty;
}
/**
* Applies mask penalty rule 3 and returns the penalty.
*
* Finds consecutive cells of 00001011101 or 10111010000, and gives penalty
* to them. If we find patterns like 000010111010000, we give penalties
* twice (i.e. 40 * 2).
*/
public static function applyMaskPenaltyRule3(ByteMatrix $matrix) : int
{
$penalty = 0;
$array = $matrix->getArray();
$width = $matrix->getWidth();
$height = $matrix->getHeight();
for ($y = 0; $y < $height; ++$y) {
for ($x = 0; $x < $width; ++$x) {
if ($x + 6 < $width
&& 1 === $array[$y][$x]
&& 0 === $array[$y][$x + 1]
&& 1 === $array[$y][$x + 2]
&& 1 === $array[$y][$x + 3]
&& 1 === $array[$y][$x + 4]
&& 0 === $array[$y][$x + 5]
&& 1 === $array[$y][$x + 6]
&& (
(
$x + 10 < $width
&& 0 === $array[$y][$x + 7]
&& 0 === $array[$y][$x + 8]
&& 0 === $array[$y][$x + 9]
&& 0 === $array[$y][$x + 10]
)
|| (
$x - 4 >= 0
&& 0 === $array[$y][$x - 1]
&& 0 === $array[$y][$x - 2]
&& 0 === $array[$y][$x - 3]
&& 0 === $array[$y][$x - 4]
)
)
) {
$penalty += self::N3;
}
if ($y + 6 < $height
&& 1 === $array[$y][$x]
&& 0 === $array[$y + 1][$x]
&& 1 === $array[$y + 2][$x]
&& 1 === $array[$y + 3][$x]
&& 1 === $array[$y + 4][$x]
&& 0 === $array[$y + 5][$x]
&& 1 === $array[$y + 6][$x]
&& (
(
$y + 10 < $height
&& 0 === $array[$y + 7][$x]
&& 0 === $array[$y + 8][$x]
&& 0 === $array[$y + 9][$x]
&& 0 === $array[$y + 10][$x]
)
|| (
$y - 4 >= 0
&& 0 === $array[$y - 1][$x]
&& 0 === $array[$y - 2][$x]
&& 0 === $array[$y - 3][$x]
&& 0 === $array[$y - 4][$x]
)
)
) {
$penalty += self::N3;
}
}
}
return $penalty;
}
/**
* Applies mask penalty rule 4 and returns the penalty.
*
* Calculates the ratio of dark cells and gives penalty if the ratio is far
* from 50%. It gives 10 penalty for 5% distance.
*/
public static function applyMaskPenaltyRule4(ByteMatrix $matrix) : int
{
$numDarkCells = 0;
$array = $matrix->getArray();
$width = $matrix->getWidth();
$height = $matrix->getHeight();
for ($y = 0; $y < $height; ++$y) {
$arrayY = $array[$y];
for ($x = 0; $x < $width; ++$x) {
if (1 === $arrayY[$x]) {
++$numDarkCells;
}
}
}
$numTotalCells = $height * $width;
$darkRatio = $numDarkCells / $numTotalCells;
$fixedPercentVariances = (int) (abs($darkRatio - 0.5) * 20);
return $fixedPercentVariances * self::N4;
}
/**
* Returns the mask bit for "getMaskPattern" at "x" and "y".
*
* See 8.8 of JISX0510:2004 for mask pattern conditions.
*
* @throws InvalidArgumentException if an invalid mask pattern was supplied
*/
public static function getDataMaskBit(int $maskPattern, int $x, int $y) : bool
{
switch ($maskPattern) {
case 0:
$intermediate = ($y + $x) & 0x1;
break;
case 1:
$intermediate = $y & 0x1;
break;
case 2:
$intermediate = $x % 3;
break;
case 3:
$intermediate = ($y + $x) % 3;
break;
case 4:
$intermediate = (BitUtils::unsignedRightShift($y, 1) + (int) ($x / 3)) & 0x1;
break;
case 5:
$temp = $y * $x;
$intermediate = ($temp & 0x1) + ($temp % 3);
break;
case 6:
$temp = $y * $x;
$intermediate = (($temp & 0x1) + ($temp % 3)) & 0x1;
break;
case 7:
$temp = $y * $x;
$intermediate = (($temp % 3) + (($y + $x) & 0x1)) & 0x1;
break;
default:
throw new InvalidArgumentException('Invalid mask pattern: ' . $maskPattern);
}
return 0 == $intermediate;
}
/**
* Helper function for applyMaskPenaltyRule1.
*
* We need this for doing this calculation in both vertical and horizontal
* orders respectively.
*/
private static function applyMaskPenaltyRule1Internal(ByteMatrix $matrix, bool $isHorizontal) : int
{
$penalty = 0;
$iLimit = $isHorizontal ? $matrix->getHeight() : $matrix->getWidth();
$jLimit = $isHorizontal ? $matrix->getWidth() : $matrix->getHeight();
$array = $matrix->getArray();
for ($i = 0; $i < $iLimit; ++$i) {
$numSameBitCells = 0;
$prevBit = -1;
for ($j = 0; $j < $jLimit; $j++) {
$bit = $isHorizontal ? $array[$i][$j] : $array[$j][$i];
if ($bit === $prevBit) {
++$numSameBitCells;
} else {
if ($numSameBitCells >= 5) {
$penalty += self::N1 + ($numSameBitCells - 5);
}
$numSameBitCells = 1;
$prevBit = $bit;
}
}
if ($numSameBitCells >= 5) {
$penalty += self::N1 + ($numSameBitCells - 5);
}
}
return $penalty;
}
}
This diff is collapsed.
<?php
declare(strict_types = 1);
namespace BaconQrCode\Encoder;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\Mode;
use BaconQrCode\Common\Version;
/**
* QR code.
*/
final class QrCode
{
/**
* Number of possible mask patterns.
*/
public const NUM_MASK_PATTERNS = 8;
/**
* Mode of the QR code.
*
* @var Mode
*/
private $mode;
/**
* EC level of the QR code.
*
* @var ErrorCorrectionLevel
*/
private $errorCorrectionLevel;
/**
* Version of the QR code.
*
* @var Version
*/
private $version;
/**
* Mask pattern of the QR code.
*
* @var int
*/
private $maskPattern = -1;
/**
* Matrix of the QR code.
*
* @var ByteMatrix
*/
private $matrix;
public function __construct(
Mode $mode,
ErrorCorrectionLevel $errorCorrectionLevel,
Version $version,
int $maskPattern,
ByteMatrix $matrix
) {
$this->mode = $mode;
$this->errorCorrectionLevel = $errorCorrectionLevel;
$this->version = $version;
$this->maskPattern = $maskPattern;
$this->matrix = $matrix;
}
/**
* Gets the mode.
*/
public function getMode() : Mode
{
return $this->mode;
}
/**
* Gets the EC level.
*/
public function getErrorCorrectionLevel() : ErrorCorrectionLevel
{
return $this->errorCorrectionLevel;
}
/**
* Gets the version.
*/
public function getVersion() : Version
{
return $this->version;
}
/**
* Gets the mask pattern.
*/
public function getMaskPattern() : int
{
return $this->maskPattern;
}
/**
* Gets the matrix.
*
* @return ByteMatrix
*/
public function getMatrix()
{
return $this->matrix;
}
/**
* Validates whether a mask pattern is valid.
*/
public static function isValidMaskPattern(int $maskPattern) : bool
{
return $maskPattern > 0 && $maskPattern < self::NUM_MASK_PATTERNS;
}
/**
* Returns a string representation of the QR code.
*/
public function __toString() : string
{
$result = "<<\n"
. ' mode: ' . $this->mode . "\n"
. ' ecLevel: ' . $this->errorCorrectionLevel . "\n"
. ' version: ' . $this->version . "\n"
. ' maskPattern: ' . $this->maskPattern . "\n";
if ($this->matrix === null) {
$result .= " matrix: null\n";
} else {
$result .= " matrix:\n";
$result .= $this->matrix;
}
$result .= ">>\n";
return $result;
}
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Exception;
use Throwable;
interface ExceptionInterface extends Throwable
{
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Exception;
final class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Exception;
final class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Exception;
final class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Exception;
final class UnexpectedValueException extends \UnexpectedValueException implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Exception;
final class WriterException extends \RuntimeException implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);
namespace BaconQrCode\Renderer\Color;
use BaconQrCode\Exception;
final class Alpha implements ColorInterface
{
/**
* @var int
*/
private $alpha;
/**
* @var ColorInterface
*/
private $baseColor;
/**
* @param int $alpha the alpha value, 0 to 100
*/
public function __construct(int $alpha, ColorInterface $baseColor)
{
if ($alpha < 0 || $alpha > 100) {
throw new Exception\InvalidArgumentException('Alpha must be between 0 and 100');
}
$this->alpha = $alpha;
$this->baseColor = $baseColor;
}
public function getAlpha() : int
{
return $this->alpha;
}
public function getBaseColor() : ColorInterface
{
return $this->baseColor;
}
public function toRgb() : Rgb
{
return $this->baseColor->toRgb();
}
public function toCmyk() : Cmyk
{
return $this->baseColor->toCmyk();
}
public function toGray() : Gray
{
return $this->baseColor->toGray();
}
}
This diff is collapsed.
<?php
declare(strict_types = 1);
namespace BaconQrCode\Renderer\Color;
interface ColorInterface
{
/**
* Converts the color to RGB.
*/
public function toRgb() : Rgb;
/**
* Converts the color to CMYK.
*/
public function toCmyk() : Cmyk;
/**
* Converts the color to gray.
*/
public function toGray() : Gray;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<?php
declare(strict_types = 1);
namespace BaconQrCode\Renderer\Eye;
use BaconQrCode\Renderer\Path\Path;
/**
* Interface for describing the look of an eye.
*/
interface EyeInterface
{
/**
* Returns the path of the external eye element.
*
* The path origin point (0, 0) must be anchored at the middle of the path.
*/
public function getExternalPath() : Path;
/**
* Returns the path of the internal eye element.
*
* The path origin point (0, 0) must be anchored at the middle of the path.
*/
public function getInternalPath() : Path;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<?php
declare(strict_types = 1);
namespace BaconQrCode\Renderer;
use BaconQrCode\Encoder\QrCode;
interface RendererInterface
{
public function render(QrCode $qrCode) : string;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment