| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- <?php
- /**
- * Class QRCode
- *
- * @created 26.11.2015
- * @author Smiley <smiley@chillerlan.net>
- * @copyright 2015 Smiley
- * @license MIT
- */
- namespace chillerlan\QRCode;
- use chillerlan\QRCode\Common\{EccLevel, ECICharset, MaskPattern, Mode, Version};
- use chillerlan\QRCode\Data\{
- AlphaNum, Byte, ECI, Hanzi, Kanji, Number, QRData, QRDataModeInterface, QRMatrix
- };
- use chillerlan\QRCode\Decoder\{Decoder, DecoderResult, GDLuminanceSource, IMagickLuminanceSource, LuminanceSourceInterface};
- use chillerlan\QRCode\Output\{QRCodeOutputException, QROutputInterface};
- use chillerlan\Settings\SettingsContainerInterface;
- use function class_exists, class_implements, in_array, mb_convert_encoding, mb_internal_encoding;
- /**
- * Turns a text string into a Model 2 QR Code
- *
- * @see https://github.com/kazuhikoarase/qrcode-generator/tree/master/php
- * @see http://www.qrcode.com/en/codes/model12.html
- * @see https://www.swisseduc.ch/informatik/theoretische_informatik/qr_codes/docs/qr_standard.pdf
- * @see https://en.wikipedia.org/wiki/QR_code
- * @see http://www.thonky.com/qr-code-tutorial/
- */
- class QRCode{
- /**
- * @deprecated 5.0.0 use Version::AUTO instead
- * @see \chillerlan\QRCode\Common\Version::AUTO
- * @var int
- */
- public const VERSION_AUTO = Version::AUTO;
- /**
- * @deprecated 5.0.0 use MaskPattern::AUTO instead
- * @see \chillerlan\QRCode\Common\MaskPattern::AUTO
- * @var int
- */
- public const MASK_PATTERN_AUTO = MaskPattern::AUTO;
- /**
- * @deprecated 5.0.0 use EccLevel::L instead
- * @see \chillerlan\QRCode\Common\EccLevel::L
- * @var int
- */
- public const ECC_L = EccLevel::L;
- /**
- * @deprecated 5.0.0 use EccLevel::M instead
- * @see \chillerlan\QRCode\Common\EccLevel::M
- * @var int
- */
- public const ECC_M = EccLevel::M;
- /**
- * @deprecated 5.0.0 use EccLevel::Q instead
- * @see \chillerlan\QRCode\Common\EccLevel::Q
- * @var int
- */
- public const ECC_Q = EccLevel::Q;
- /**
- * @deprecated 5.0.0 use EccLevel::H instead
- * @see \chillerlan\QRCode\Common\EccLevel::H
- * @var int
- */
- public const ECC_H = EccLevel::H;
- /**
- * @deprecated 5.0.0 use QROutputInterface::MARKUP_HTML instead
- * @see \chillerlan\QRCode\Output\QROutputInterface::MARKUP_HTML
- * @var string
- */
- public const OUTPUT_MARKUP_HTML = QROutputInterface::MARKUP_HTML;
- /**
- * @deprecated 5.0.0 use QROutputInterface::MARKUP_SVG instead
- * @see \chillerlan\QRCode\Output\QROutputInterface::MARKUP_SVG
- * @var string
- */
- public const OUTPUT_MARKUP_SVG = QROutputInterface::MARKUP_SVG;
- /**
- * @deprecated 5.0.0 use QROutputInterface::GDIMAGE_PNG instead
- * @see \chillerlan\QRCode\Output\QROutputInterface::GDIMAGE_PNG
- * @var string
- */
- public const OUTPUT_IMAGE_PNG = QROutputInterface::GDIMAGE_PNG;
- /**
- * @deprecated 5.0.0 use QROutputInterface::GDIMAGE_JPG instead
- * @see \chillerlan\QRCode\Output\QROutputInterface::GDIMAGE_JPG
- * @var string
- */
- public const OUTPUT_IMAGE_JPG = QROutputInterface::GDIMAGE_JPG;
- /**
- * @deprecated 5.0.0 use QROutputInterface::GDIMAGE_GIF instead
- * @see \chillerlan\QRCode\Output\QROutputInterface::GDIMAGE_GIF
- * @var string
- */
- public const OUTPUT_IMAGE_GIF = QROutputInterface::GDIMAGE_GIF;
- /**
- * @deprecated 5.0.0 use QROutputInterface::STRING_JSON instead
- * @see \chillerlan\QRCode\Output\QROutputInterface::STRING_JSON
- * @var string
- */
- public const OUTPUT_STRING_JSON = QROutputInterface::STRING_JSON;
- /**
- * @deprecated 5.0.0 use QROutputInterface::STRING_TEXT instead
- * @see \chillerlan\QRCode\Output\QROutputInterface::STRING_TEXT
- * @var string
- */
- public const OUTPUT_STRING_TEXT = QROutputInterface::STRING_TEXT;
- /**
- * @deprecated 5.0.0 use QROutputInterface::IMAGICK instead
- * @see \chillerlan\QRCode\Output\QROutputInterface::IMAGICK
- * @var string
- */
- public const OUTPUT_IMAGICK = QROutputInterface::IMAGICK;
- /**
- * @deprecated 5.0.0 use QROutputInterface::FPDF instead
- * @see \chillerlan\QRCode\Output\QROutputInterface::FPDF
- * @var string
- */
- public const OUTPUT_FPDF = QROutputInterface::FPDF;
- /**
- * @deprecated 5.0.0 use QROutputInterface::EPS instead
- * @see \chillerlan\QRCode\Output\QROutputInterface::EPS
- * @var string
- */
- public const OUTPUT_EPS = QROutputInterface::EPS;
- /**
- * @deprecated 5.0.0 use QROutputInterface::CUSTOM instead
- * @see \chillerlan\QRCode\Output\QROutputInterface::CUSTOM
- * @var string
- */
- public const OUTPUT_CUSTOM = QROutputInterface::CUSTOM;
- /**
- * @deprecated 5.0.0 use QROutputInterface::MODES instead
- * @see \chillerlan\QRCode\Output\QROutputInterface::MODES
- * @var string[]
- */
- public const OUTPUT_MODES = QROutputInterface::MODES;
- /**
- * The settings container
- *
- * @var \chillerlan\QRCode\QROptions|\chillerlan\Settings\SettingsContainerInterface
- */
- protected SettingsContainerInterface $options;
- /**
- * A collection of one or more data segments of [classname, data] to write
- *
- * @see \chillerlan\QRCode\Data\QRDataModeInterface
- *
- * @var \chillerlan\QRCode\Data\QRDataModeInterface[]
- */
- protected array $dataSegments = [];
- /**
- * The luminance source for the reader
- */
- protected string $luminanceSourceFQN = GDLuminanceSource::class;
- /**
- * QRCode constructor.
- *
- * Sets the options instance
- */
- public function __construct(SettingsContainerInterface $options = null){
- $this->setOptions($options ?? new QROptions);
- }
- /**
- * Sets an options instance
- */
- public function setOptions(SettingsContainerInterface $options):self{
- $this->options = $options;
- if($this->options->readerUseImagickIfAvailable){
- $this->luminanceSourceFQN = IMagickLuminanceSource::class;
- }
- return $this;
- }
- /**
- * Renders a QR Code for the given $data and QROptions, saves $file optionally
- *
- * @return mixed
- */
- public function render(string $data = null, string $file = null){
- if($data !== null){
- /** @var \chillerlan\QRCode\Data\QRDataModeInterface $dataInterface */
- foreach(Mode::INTERFACES as $dataInterface){
- if($dataInterface::validateString($data)){
- $this->addSegment(new $dataInterface($data));
- break;
- }
- }
- }
- return $this->renderMatrix($this->getMatrix(), $file);
- }
- /**
- * Renders a QR Code for the given QRMatrix and QROptions, saves $file optionally
- *
- * @return mixed
- */
- public function renderMatrix(QRMatrix $matrix, string $file = null){
- return $this->initOutputInterface($matrix)->dump($file);
- }
- /**
- * Returns a QRMatrix object for the given $data and current QROptions
- *
- * @throws \chillerlan\QRCode\Data\QRCodeDataException
- */
- public function getMatrix():QRMatrix{
- $dataInterface = new QRData($this->options, $this->dataSegments);
- $maskPattern = $this->options->maskPattern === MaskPattern::AUTO
- ? MaskPattern::getBestPattern($dataInterface)
- : new MaskPattern($this->options->maskPattern);
- $matrix = $dataInterface->writeMatrix($maskPattern);
- // add matrix modifications after mask pattern evaluation and before handing over to output
- if($this->options->addLogoSpace){
- $logoSpaceWidth = $this->options->logoSpaceWidth;
- $logoSpaceHeight = $this->options->logoSpaceHeight;
- // check whether one of the dimensions was omitted
- if($logoSpaceWidth === null || $logoSpaceHeight === null){
- $logoSpaceWidth = $logoSpaceWidth ?? $logoSpaceHeight ?? 0;
- $logoSpaceHeight = null;
- }
- $matrix->setLogoSpace(
- $logoSpaceWidth,
- $logoSpaceHeight,
- $this->options->logoSpaceStartX,
- $this->options->logoSpaceStartY
- );
- }
- if($this->options->addQuietzone){
- $matrix->setQuietZone($this->options->quietzoneSize);
- }
- return $matrix;
- }
- /**
- * initializes a fresh built-in or custom QROutputInterface
- *
- * @throws \chillerlan\QRCode\Output\QRCodeOutputException
- */
- protected function initOutputInterface(QRMatrix $matrix):QROutputInterface{
- $outputInterface = QROutputInterface::MODES[$this->options->outputType] ?? null;
- if($this->options->outputType === QROutputInterface::CUSTOM){
- $outputInterface = $this->options->outputInterface;
- }
- if(!$outputInterface || !class_exists($outputInterface)){
- throw new QRCodeOutputException('invalid output module');
- }
- if(!in_array(QROutputInterface::class, class_implements($outputInterface))){
- throw new QRCodeOutputException('output module does not implement QROutputInterface');
- }
- return new $outputInterface($this->options, $matrix);
- }
- /**
- * checks if a string qualifies as numeric (convenience method)
- *
- * @deprecated 5.0.0 use Number::validateString() instead
- * @see \chillerlan\QRCode\Data\Number::validateString()
- * @codeCoverageIgnore
- */
- public function isNumber(string $string):bool{
- return Number::validateString($string);
- }
- /**
- * checks if a string qualifies as alphanumeric (convenience method)
- *
- * @deprecated 5.0.0 use AlphaNum::validateString() instead
- * @see \chillerlan\QRCode\Data\AlphaNum::validateString()
- * @codeCoverageIgnore
- */
- public function isAlphaNum(string $string):bool{
- return AlphaNum::validateString($string);
- }
- /**
- * checks if a string qualifies as Kanji (convenience method)
- *
- * @deprecated 5.0.0 use Kanji::validateString() instead
- * @see \chillerlan\QRCode\Data\Kanji::validateString()
- * @codeCoverageIgnore
- */
- public function isKanji(string $string):bool{
- return Kanji::validateString($string);
- }
- /**
- * a dummy (convenience method)
- *
- * @deprecated 5.0.0 use Byte::validateString() instead
- * @see \chillerlan\QRCode\Data\Byte::validateString()
- * @codeCoverageIgnore
- */
- public function isByte(string $string):bool{
- return Byte::validateString($string);
- }
- /**
- * Adds a data segment
- *
- * ISO/IEC 18004:2000 8.3.6 - Mixing modes
- * ISO/IEC 18004:2000 Annex H - Optimisation of bit stream length
- */
- public function addSegment(QRDataModeInterface $segment):void{
- $this->dataSegments[] = $segment;
- }
- /**
- * Clears the data segments array
- */
- public function clearSegments():self{
- $this->dataSegments = [];
- return $this;
- }
- /**
- * Adds a numeric data segment
- *
- * ISO/IEC 18004:2000 8.3.2 - Numeric Mode
- */
- public function addNumericSegment(string $data):self{
- $this->addSegment(new Number($data));
- return $this;
- }
- /**
- * Adds an alphanumeric data segment
- *
- * ISO/IEC 18004:2000 8.3.3 - Alphanumeric Mode
- */
- public function addAlphaNumSegment(string $data):self{
- $this->addSegment(new AlphaNum($data));
- return $this;
- }
- /**
- * Adds a Kanji data segment (Japanese 13-bit double-byte characters, Shift-JIS)
- *
- * ISO/IEC 18004:2000 8.3.5 - Kanji Mode
- */
- public function addKanjiSegment(string $data):self{
- $this->addSegment(new Kanji($data));
- return $this;
- }
- /**
- * Adds a Hanzi data segment (simplified Chinese 13-bit double-byte characters, GB2312/GB18030)
- *
- * GBT18284-2000 Hanzi Mode
- */
- public function addHanziSegment(string $data):self{
- $this->addSegment(new Hanzi($data));
- return $this;
- }
- /**
- * Adds an 8-bit byte data segment
- *
- * ISO/IEC 18004:2000 8.3.4 - 8-bit Byte Mode
- */
- public function addByteSegment(string $data):self{
- $this->addSegment(new Byte($data));
- return $this;
- }
- /**
- * Adds a standalone ECI designator
- *
- * The ECI designator must be followed by a Byte segment that contains the string encoded according to the given ECI charset
- *
- * ISO/IEC 18004:2000 8.3.1 - Extended Channel Interpretation (ECI) Mode
- */
- public function addEciDesignator(int $encoding):self{
- $this->addSegment(new ECI($encoding));
- return $this;
- }
- /**
- * Adds an ECI data segment (including designator)
- *
- * The given string will be encoded from mb_internal_encoding() to the given ECI character set
- *
- * I hate this somehow, but I'll leave it for now
- *
- * @throws \chillerlan\QRCode\QRCodeException
- */
- public function addEciSegment(int $encoding, string $data):self{
- // validate the encoding id
- $eciCharset = new ECICharset($encoding);
- // get charset name
- $eciCharsetName = $eciCharset->getName();
- // convert the string to the given charset
- if($eciCharsetName !== null){
- $data = mb_convert_encoding($data, $eciCharsetName, mb_internal_encoding());
- // add ECI designator
- $this->addSegment(new ECI($eciCharset->getID()));
- $this->addSegment(new Byte($data));
- return $this;
- }
- throw new QRCodeException('unable to add ECI segment');
- }
- /**
- * Reads a QR Code from a given file
- *
- * @noinspection PhpUndefinedMethodInspection
- */
- public function readFromFile(string $path):DecoderResult{
- return $this->readFromSource($this->luminanceSourceFQN::fromFile($path, $this->options));
- }
- /**
- * Reads a QR Code from the given data blob
- *
- * @noinspection PhpUndefinedMethodInspection
- */
- public function readFromBlob(string $blob):DecoderResult{
- return $this->readFromSource($this->luminanceSourceFQN::fromBlob($blob, $this->options));
- }
- /**
- * Reads a QR Code from the given luminance source
- */
- public function readFromSource(LuminanceSourceInterface $source):DecoderResult{
- return (new Decoder)->decode($source);
- }
- }
|