From c55c64dfa035a3c3ad91a4e13ed10bcd5a3c6807 Mon Sep 17 00:00:00 2001
From: smiley
Date: Sun, 23 Nov 2025 23:26:24 +0100
Subject: [PATCH] :book:
---
README.md | 34 -------
docs/Appendix/Performance-considerations.md | 46 ++++-----
docs/Appendix/Terminology.md | 2 +-
docs/Readme.md | 2 +
docs/Usage/Advanced-usage.md | 42 +-------
docs/Usage/Configuration-settings.md | 4 +-
docs/Usage/Logos.md | 74 ++++++++++++++
docs/Usage/Quickstart.md | 20 ----
docs/Usage/Reading-QRCodes.md | 101 ++++++++++++++++++++
docs/conf.py | 2 +-
docs/index.rst | 2 +
11 files changed, 209 insertions(+), 120 deletions(-)
create mode 100644 docs/Usage/Logos.md
create mode 100644 docs/Usage/Reading-QRCodes.md
diff --git a/README.md b/README.md
index f3fb749d7..b97c5e300 100644
--- a/README.md
+++ b/README.md
@@ -125,40 +125,6 @@ Also, have a look [in the examples folder](https://github.com/chillerlan/php-qrc
-## Reading QR Codes
-
-Using the built-in QR Code reader is pretty straight-forward:
-
-```php
-// it's generally a good idea to wrap the reader in a try/catch block because it WILL throw eventually
-try{
- $result = (new QRCode)->readFromFile('path/to/file.png'); // -> DecoderResult
-
- // you can now use the result instance...
- $content = $result->data;
- $matrix = $result->getMatrix(); // -> QRMatrix
-
- // ...or simply cast it to string to get the content:
- $content = (string)$result;
-}
-catch(Throwable $e){
- // oopsies!
-}
-```
-
-
-# Shameless advertising
-
-Hi, please check out some of my other projects that are way cooler than qrcodes!
-
-- [js-qrcode](https://github.com/chillerlan/js-qrcode) - a javascript port of this library
-- [php-authenticator](https://github.com/chillerlan/php-authenticator) - a Google Authenticator implementation (see [authenticator example](https://github.com/chillerlan/php-qrcode/blob/main/examples/authenticator.php))
-- [php-httpinterface](https://github.com/chillerlan/php-httpinterface) - a PSR-7/15/17/18 implemetation
-- [php-oauth](https://github.com/chillerlan/php-oauth) - an OAuth 1/2 client library, fully PSR-7/PSR-17/PSR-18 compatible
-- [php-database](https://github.com/chillerlan/php-database) - a database client & querybuilder for MySQL, Postgres, SQLite, MSSQL, Firebird
-- [php-tootbot](https://github.com/php-tootbot/tootbot-template) - a Mastodon bot library (see [@dwil](https://github.com/php-tootbot/dwil))
-
-
# Disclaimer!
I don't take responsibility for molten CPUs, misled applications, failed log-ins etc.. Use at your own risk!
diff --git a/docs/Appendix/Performance-considerations.md b/docs/Appendix/Performance-considerations.md
index 56bfb79dc..3fa21e790 100644
--- a/docs/Appendix/Performance-considerations.md
+++ b/docs/Appendix/Performance-considerations.md
@@ -44,35 +44,37 @@ is a complex and costly operation that is necessary to ensure the symbol is read
to override the evaluation and manually set a mask pattern, this is not recommended unless you know exactly what you're doing
as it can render a QR symbol unreadable.
-The table below shows the performance impact (in miliseconds) of the mask pattern evaluation for each version, the times may vary between systems.
+The table below shows the performance impact (in miliseconds) of the mask pattern evaluation for select versions, the times may vary between systems.
-| version | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
-|-----------|--------:|--------:|--------:|--------:|--------:|--------:|--------:|--------:|--------:|--------:|
-| **1-10** | 4.414 | 5.697 | 7.986 | 9.221 | 10.877 | 11.293 | 13.901 | 15.563 | 18.142 | 20.501 |
-| **11-20** | 22.662 | 27.779 | 29.622 | 33.017 | 36.358 | 39.712 | 43.685 | 47.121 | 51.389 | 57.865 |
-| **21-30** | 59.753 | 68.502 | 68.523 | 72.866 | 78.245 | 83.593 | 88.327 | 94.921 | 103.394 | 106.358 |
-| **31-40** | 113.311 | 120.484 | 126.215 | 132.931 | 139.783 | 145.617 | 170.576 | 165.996 | 167.365 | 175.821 |
+| version | time (ms) |
+|---------|----------:|
+| **1** | 2.285 |
+| **5** | 5.867 |
+| **10** | 12.737 |
+| **20** | 34.045 |
+| **30** | 64.914 |
+| **40** | 107.027 |
## Output
Output rendering depends heavily on the size of the QR matrix, the desired type and the underlying libraries and/or PHP extensions.
-Especially the rendering of raster images through GD or ImagMagick can be very slow, depending on [the scale setting](../Usage/Configuration-settings.md#scale),
+Especially the rendering of raster images through GD or ImageMagick can be very slow, depending on [the scale setting](../Usage/Configuration-settings.md#scale),
filters and image type.
Below a comparison of the performance for the several built-in output classes (times in miliseconds, scale = 5):
-| | v5 | v10 | v15 | v20 | v25 | v30 | v35 | v40 |
-|-------------------|-------:|-------:|--------:|--------:|--------:|--------:|--------:|--------:|
-| **QRMarkupSVG** | 3.732 | 8.645 | 13.846 | 21.127 | 32.842 | 43.753 | 56.584 | 73.885 |
-| **QRMarkupHTML** | 0.522 | 1.308 | 2.062 | 2.761 | 3.907 | 5.201 | 7.931 | 9.572 |
-| **QRGdImageBMP** | 5.998 | 12.541 | 20.728 | 32.336 | 46.345 | 62.842 | 81.555 | 106.482 |
-| **QRGdImageGIF** | 3.427 | 6.817 | 12.226 | 17.925 | 25.453 | 35.136 | 44.706 | 57.477 |
-| **QRGdImageJPEG** | 2.284 | 4.882 | 8.161 | 12.097 | 17.333 | 23.862 | 30.327 | 40.226 |
-| **QRGdImagePNG** | 4.523 | 9.377 | 16.581 | 26.207 | 36.516 | 49.066 | 63.765 | 82.074 |
-| **QRGdImageWEBP** | 8.211 | 17.367 | 30.079 | 47.095 | 69.668 | 91.378 | 119.869 | 150.288 |
-| **QRStringJSON** | 0.043 | 0.066 | 0.107 | 0.158 | 0.215 | 0.301 | 0.369 | 0.492 |
-| **QRStringText** | 0.229 | 0.387 | 0.628 | 0.952 | 1.312 | 1.759 | 2.329 | 3.045 |
-| **QRImagick** | 37.694 | 68.808 | 114.415 | 172.962 | 242.338 | 325.085 | 419.999 | 529.897 |
-| **QRFpdf** | 6.578 | 12.466 | 21.169 | 33.021 | 45.469 | 61.198 | 80.092 | 100.059 |
-| **QREps** | 1.269 | 2.694 | 4.515 | 6.933 | 11.049 | 14.181 | 20.799 | 25.886 |
+| | v5 | v10 | v20 | v30 | v40 |
+|-------------------|-------:|-------:|--------:|--------:|--------:|
+| **QRMarkupSVG** | 3.732 | 8.645 | 21.127 | 43.753 | 73.885 |
+| **QRMarkupHTML** | 0.522 | 1.308 | 2.761 | 5.201 | 9.572 |
+| **QRGdImageBMP** | 5.998 | 12.541 | 32.336 | 62.842 | 106.482 |
+| **QRGdImageGIF** | 3.427 | 6.817 | 17.925 | 35.136 | 57.477 |
+| **QRGdImageJPEG** | 2.284 | 4.882 | 12.097 | 23.862 | 40.226 |
+| **QRGdImagePNG** | 4.523 | 9.377 | 26.207 | 49.066 | 82.074 |
+| **QRGdImageWEBP** | 8.211 | 17.367 | 47.095 | 91.378 | 150.288 |
+| **QRStringJSON** | 0.043 | 0.066 | 0.158 | 0.301 | 0.492 |
+| **QRStringText** | 0.229 | 0.387 | 0.952 | 1.759 | 3.045 |
+| **QRImagick** | 37.694 | 68.808 | 172.962 | 325.085 | 529.897 |
+| **QRFpdf** | 6.578 | 12.466 | 33.021 | 61.198 | 100.059 |
+| **QREps** | 1.269 | 2.694 | 6.933 | 14.181 | 25.886 |
diff --git a/docs/Appendix/Terminology.md b/docs/Appendix/Terminology.md
index 7ee03b23f..a7e4127e4 100644
--- a/docs/Appendix/Terminology.md
+++ b/docs/Appendix/Terminology.md
@@ -2,7 +2,7 @@
## QR Code
-A [*QR code*](https://en.wikipedia.org/wiki/QR_code) (quick-response code) is a type of two-dimensional matrix barcode, invented
+A [*QR code*](https://en.wikipedia.org/wiki/QR_code) (quick-response code), sometimes referred to as *QR symbol* or simply just *symbol*, is a type of two-dimensional matrix barcode, invented
in 1994 by Japanese company [Denso Wave](https://www.qrcode.com/en/faq.html#patentH2Title) for labelling automobile parts.
The QR labelling system was applied beyond the automobile industry due to its fast readability and greater storage capacity
compared to standard UPC barcodes.
diff --git a/docs/Readme.md b/docs/Readme.md
index 139831d5f..b0f18ede9 100644
--- a/docs/Readme.md
+++ b/docs/Readme.md
@@ -62,6 +62,8 @@ The markdown sources for the [Read the Docs online manual](https://php-qrcode.re
- [Quickstart](./Usage/Quickstart.md)
- [Advanced usage](./Usage/Advanced-usage.md)
- [Configuration settings](./Usage/Configuration-settings.md)
+- [Reading QR Codes](./Usage/Reading-QRCodes.md)
+- [Logos and logo space](./Usage/Logos.md)
### Customizing output
diff --git a/docs/Usage/Advanced-usage.md b/docs/Usage/Advanced-usage.md
index 07e2f5d21..c3c29eb4f 100644
--- a/docs/Usage/Advanced-usage.md
+++ b/docs/Usage/Advanced-usage.md
@@ -173,7 +173,7 @@ $output = $qrcode->render();
$qrcode->render(null, '/path/to/qrcode.svg');
```
-The [`QRDataModeInterface`](https://github.com/chillerlan/php-qrcode/blob/main/src/Data/QRDataModeInterface.php) offers the `validateString()` method (implemended for `AlphaNum`, `Byte`, `Hanzi`, `Kanji` and `Number`).
+The [`QRDataModeInterface`](https://github.com/chillerlan/php-qrcode/blob/main/src/Data/QRDataModeInterface.php) offers the `validateString()` method (implemented for `AlphaNum`, `Byte`, `Hanzi`, `Kanji` and `Number`).
This method is used internally when a data mode is invoked, but it can come in handy if you need to check input data beforehand.
```php
@@ -185,33 +185,6 @@ $qrcode->addHanziSegment($data);
```
-### QR Code reader
-
-In some cases it might be necessary to increase the contrast of a QR Code image:
-
-```php
-$options->readerUseImagickIfAvailable = true;
-$options->readerIncreaseContrast = true;
-$options->readerGrayscale = true;
-
-$qrcode = new QRCode($options);
-
-$result = $qrcode->readFromFile('path/to/qrcode.png');
-$result = $qrcode->readFromBlob($imagedata);
-```
-
-The `QRMatrix` object from the [`DecoderResult`](https://github.com/chillerlan/php-qrcode/blob/main/src/Decoder/DecoderResult.php) can be reused:
-
-```php
-$matrix = $result->getQRMatrix();
-
-// ...matrix modification...
-
-$output = (new QRCode($options))->renderMatrix($matrix);
-
-// ...output
-```
-
## Common output options
### Save to file
@@ -266,16 +239,3 @@ $imagick = $qrcode->render($data);
echo $imagick->getImageBlob();
```
-
-
-### Add a logo space
-
-
-```php
-$options->addLogoSpace = true;
-$options->logoSpaceWidth = 9;
-$options->logoSpaceHeight = 9;
-$options->logoSpaceStartX = 10;
-$options->logoSpaceStartY = 10;
-
-```
diff --git a/docs/Usage/Configuration-settings.md b/docs/Usage/Configuration-settings.md
index 7edfd7043..9b24b0636 100644
--- a/docs/Usage/Configuration-settings.md
+++ b/docs/Usage/Configuration-settings.md
@@ -191,6 +191,7 @@ Specifies which module types to exclude when `QROptions::$drawCircularModules` i
(default: `[]`)
+
## connectPaths
Whether to connect the paths for the several module types to avoid weird glitches when using gradients etc.
@@ -215,6 +216,7 @@ Currentty used in `QREps` and `QRMarkupSVG`.
Specify which paths/patterns to exclude from connecting if `QROptions::$connectPaths` is set to `true`
+
**See also:**
- `\chillerlan\QRCode\QROptionsTrait::$connectPaths`
@@ -229,6 +231,7 @@ Module values map
- `QREps`: `[C, M, Y, K]` // 0-255
+
**See also:**
- `\chillerlan\QRCode\Output\QROutputAbstract::setModuleValues()`
@@ -296,7 +299,6 @@ Defaults to `QROptions::$bgColor`.
- `QRImagick`: `"color_str"`, this color is set in `Imagick::transparentPaintImage()`
-
**See also:**
- [php.net: `\imagecolortransparent()`](https://www.php.net/manual/function.imagecolortransparent)
diff --git a/docs/Usage/Logos.md b/docs/Usage/Logos.md
new file mode 100644
index 000000000..90721d23a
--- /dev/null
+++ b/docs/Usage/Logos.md
@@ -0,0 +1,74 @@
+# Logos and logo space
+
+## Info
+
+Brand logos on QR Codes are a common sight and it's quite simple to produce them, however, there are some pitfalls to avoid in order to properly create branded QR Codes.
+
+**Logos are not part of any specification, instead, placing a logo on a QR symbol is merely abusing the error correction capacity and the symbol may become unreadable, especially in environments with chances to receive damage, such as prints** (bug reports regarding unreadable logo QR Codes will not be accepted, you have been warned).
+
+As a general rule, the [ECC level](./../Appendix/Terminology.md#ecc-error-correction-coding) should be set to **H** (30%) and a minimum version number of 7 or higher is recommended, even though the data would fit in a much smaller symbol:
+
+```php
+$options = new QROptions;
+
+$options->version = Version::AUTO;
+$options->versionMin = 7;
+$options->eccLevel = EccLevel::H;
+```
+
+For best results, the logo (-space) should not exceed 1/5 of the symbol width and height, excluding the quiet zone. Overwriting the [function patterns](./../Appendix/Terminology.md#function-patterns) should be avoided,
+however, overwriting one of the [alignment patterns](./../Appendix/Terminology.md#alignment-pattern) is almost inevitable (but also not much of an issue).
+
+
+
+## Adding a logo space
+
+A logo space might not be necessary if the QR Code is rendered in a raster format via GD or ImageMagick, where a logo and a possibly required space can be added with the respective built-in functions.
+For vector/markup based formats it can be useful to prevent the rendering of modules in the area where the logo is supposed to be.
+
+The `QRMatrix` instance offers a method to clear a rectangular space, that can be utilized from the options:
+
+```php
+$options->addLogoSpace = true;
+
+// either width or height must be given, if only one dimension is given, the space is assumed square
+$options->logoSpaceWidth = 9;
+$options->logoSpaceHeight = 9;
+
+// the top left corner of the space, both values are optional
+$options->logoSpaceStartX = 10;
+$options->logoSpaceStartY = 10;
+```
+
+It's also possible to call `QRMatrix::setLogoSpace()` from within a custom output class, e.g. to [dynamically scale the logo space](https://github.com/chillerlan/php-qrcode/blob/d54ebd8b18520525c4fd4e05fffc1768aefed187/examples/svgWithLogo.php#L31-L34):
+
+```php
+$size = (int)ceil($this->moduleCount * $logoScale);
+
+$this->matrix->setLogoSpace($size, $size);
+```
+
+Further, the `QRMatrix` instance can be modified from outside too:
+
+```php
+$qrcode = new QRCode($options);
+
+// create a matrix instance
+$matrix = $qrcode
+ ->addByteSegment('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
+ ->getQRMatrix();
+
+// modify
+for($y = $startY; $y < $endY; $y++){
+ for($x = $startX; $x < $endX; $x++){
+ $matrix->set($x, $y, false, QRMatrix::M_LOGO);
+ }
+}
+
+// render the QR Code
+$out = $qrcode->renderMatrix($matrix);
+```
+
+
+
+
diff --git a/docs/Usage/Quickstart.md b/docs/Usage/Quickstart.md
index b85f911ef..fb7b67f3f 100644
--- a/docs/Usage/Quickstart.md
+++ b/docs/Usage/Quickstart.md
@@ -41,26 +41,6 @@ and [configuration settings](../Usage/Configuration-settings.md) for a list of a
Also, have a look [in the examples folder](https://github.com/chillerlan/php-qrcode/tree/main/examples) for some more usage examples.
-## Reading QR Codes
-
-Using the built-in QR Code reader is pretty straight-forward:
-```php
-try{
- $result = (new QRCode)->readFromFile('path/to/file.png'); // -> DecoderResult
-
- // you can now use the result instance...
- $content = $result->data;
-
- // ...or simply cast the result instance to string to get the content
- $content = (string)$result;
-}
-catch(Throwable $exception){
- // handle exception...
-}
-```
-It's generally a good idea to wrap the reading in a try/catch block to handle any errors that may occur in the process.
-
-
## Notes
The QR encoder, especially the subroutines for mask pattern testing, can cause high CPU load on increased matrix size.
You can avoid a part of this load by choosing a fast output module, like SVG.
diff --git a/docs/Usage/Reading-QRCodes.md b/docs/Usage/Reading-QRCodes.md
new file mode 100644
index 000000000..3cac2333b
--- /dev/null
+++ b/docs/Usage/Reading-QRCodes.md
@@ -0,0 +1,101 @@
+# Reading QR Codes
+
+## Basic usage
+
+The QR Code reader can be called either from a `QRCode` instance or invoked directly via the `Decoder` class with a `QROptions` (`QRCodeReaderOptionsTrait`) instance.
+
+```php
+$options = new QROptions
+
+$options->readerUseImagickIfAvailable = true;
+$options->readerIncreaseContrast = true;
+$options->readerGrayscale = true;
+$options->readerInvertColors = false;
+```
+
+The option `QROptions::$readerUseImagickIfAvailable` is exclusive to the `QRCode` instance in order to decide which `LuminanceSourceInterface` to use.
+The `QRCode` instance has 3 convenience methods related to the reader:
+```php
+$qrcode = new QRCode($options)
+
+$result = $qrcode->readFromFile('path/to/qrcode.png');
+$result = $qrcode->readFromBlob($imagedata);
+
+// from a luminance source instance
+$source = IMagickLuminanceSource::fromBlob($imagedata, $options);
+$result = $qrcode->readFromSource($source);
+```
+
+## The `LuminanceSourceInterface`
+
+The method `QRCode::readFromSource()` takes a `LuminanceSourceInterface` instance as parameter and is mainly for internal use, as you can invoke and call the decoder directly with it.
+Each `LuminanceSourceInterface` has the static convenience methods `fromFile()` and `fromBlob()` that will invoke the instance with the respective parameters, alternatively the instance(s) can be invoked manually:
+
+from an `Imagick` instance:
+```php
+$imagick = new Imagick;
+$imagick->readImageBlob($imagedata);
+
+$source = new IMagickLuminanceSource($imagick, $options);
+```
+
+from a `GdImage` instance:
+
+```php
+$gdimage = imagecreatefromstring($imagedata);
+
+$source = new GDLuminanceSource($gdimage, $options);
+```
+
+## The `Decoder`
+
+The `Decoder` takes a `QROptions` instance as parameter, which currently has no use - it is only handed over for possible future uses.
+
+```php
+$result = (new Decoder($options))->decode($source);
+```
+
+That is all! The decoder will either return a `DecoderResult` instance or throw an exception.
+It is generally a good practice to wrap the reading in a try/catch block:
+
+```php
+try{
+ $result = $qrcode->readFromFile('path/to/file.png'); // -> DecoderResult
+
+ // ... do stuff with the result
+}
+catch(QRCodeDecoderException){
+ // ... adjust input image (position, contrast, invert, sharpen) and repeat the process
+}
+```
+
+You can now use the result instance:
+
+```php
+$content = $result->data;
+// ...or simply cast it to string:
+$content = (string)$result;
+```
+
+The result instance also holds `Version`, `EccLevel`, `MaskPattern` and `BitBuffer` instances, as well as an array of `FinderPattern` instances,
+it also offers a method that returns a `QRMatrix` instance populated with the detected settings:
+
+```php
+$matrix = $result->getQRMatrix();
+
+// ...matrix modification...
+
+$output = (new QRCode($options))->renderMatrix($matrix);
+
+// ...dump output
+```
+
+
+## General considerations
+
+The QR Code reader reads the given QR symbol in a single pass, unlike e.g. a reader app on a mobile device with a camera, which repeats the reading process for a sequence of frames from the video input and takes the "best" result.
+It means that this reader may fail to properly decode a symbol that reads perfectly fine on a mobile - you'd need to emulate the same process of sequential reading while adjusting the input image to get a similar result.
+
+Further it seems ([per observation](https://github.com/chillerlan/php-qrcode/blob/92346420a5a88aeeb8dc16f731ef1f93331635d3/tests/QRCodeReaderTestAbstract.php#L168-L172)) that the reader favors smaller module sizes (1-2 pixel side length) from version 20 onwards.
+The test data set seemed to randomly produce errors depending on the given module scale, independent of the luminance source.
+However, scaling down to get a smaller module size isn't the best solution as it may produce additional challenges due to filter artifacts.
diff --git a/docs/conf.py b/docs/conf.py
index 091f0cc9e..40f662f13 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -44,7 +44,7 @@ lexers['php-annotations'] = PhpLexer(startinline=True)
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = u'PHP-QRCode'
-copyright = u'2023, smiley'
+copyright = u'2025, smiley'
author = u'smiley'
epub_author = u'smiley'
diff --git a/docs/index.rst b/docs/index.rst
index 3b4f36e07..52a97c5e3 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -21,6 +21,8 @@ This work is licensed under the Creative Commons Attribution 4.0 International (
Usage/Quickstart.md
Usage/Advanced-usage.md
Usage/Configuration-settings.md
+ Usage/Reading-QRCodes.md
+ Usage/Logos.md
.. toctree::
:maxdepth: 3