@@ -32,7 +32,8 @@ class QRMatrix{
3232 public const M_FORMAT = 0x0e ;
3333 public const M_VERSION = 0x10 ;
3434 public const M_QUIETZONE = 0x12 ;
35- # public const M_LOGO = 0x14; // @todo
35+ public const M_LOGO = 0x14 ;
36+ public const M_FINDER_DOT = 0x16 ;
3637
3738 public const M_TEST = 0xff ;
3839
@@ -352,12 +353,18 @@ public function setFinderPattern():QRMatrix{
352353 foreach ($ pos as $ c ){
353354 for ($ y = 0 ; $ y < 7 ; $ y ++){
354355 for ($ x = 0 ; $ x < 7 ; $ x ++){
355- $ this ->set (
356- $ c [0 ] + $ y ,
357- $ c [1 ] + $ x ,
358- !(($ x > 0 && $ x < 6 && ($ y === 1 || $ y === 5 )) || ($ y > 0 && $ y < 6 && ($ x === 1 || $ x === 5 ))),
359- $ this ::M_FINDER
360- );
356+ // outer (dark) 7*7 square
357+ if ($ x === 0 || $ x === 6 || $ y === 0 || $ y === 6 ){
358+ $ this ->set ($ c [0 ] + $ y , $ c [1 ] + $ x , true , $ this ::M_FINDER );
359+ }
360+ // inner (light) 5*5 square
361+ elseif ($ x === 1 || $ x === 5 || $ y === 1 || $ y === 5 ){
362+ $ this ->set ($ c [0 ] + $ y , $ c [1 ] + $ x , false , $ this ::M_FINDER );
363+ }
364+ // 3*3 dot
365+ else {
366+ $ this ->set ($ c [0 ] + $ y , $ c [1 ] + $ x , true , $ this ::M_FINDER_DOT );
367+ }
361368 }
362369 }
363370 }
@@ -551,6 +558,82 @@ public function setQuietZone(int $size = null):QRMatrix{
551558 return $ this ;
552559 }
553560
561+ /**
562+ * Clears a space of $width * $height in order to add a logo or text.
563+ *
564+ * Additionally, the logo space can be positioned within the QR Code - respecting the main functional patterns -
565+ * using $startX and $startY. If either of these are null, the logo space will be centered in that direction.
566+ * ECC level "H" (30%) is required.
567+ *
568+ * Please note that adding a logo space minimizes the error correction capacity of the QR Code and
569+ * created images may become unreadable, especially when printed with a chance to receive damage.
570+ * Please test thoroughly before using this feature in production.
571+ *
572+ * This method should be called from within an output module (after the matrix has been filled with data).
573+ * Note that there is no restiction on how many times this method could be called on the same matrix instance.
574+ *
575+ * @link https://github.com/chillerlan/php-qrcode/issues/52
576+ *
577+ * @param int $width
578+ * @param int $height
579+ * @param int|null $startX
580+ * @param int|null $startY
581+ *
582+ * @return \chillerlan\QRCode\Data\QRMatrix
583+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
584+ */
585+ public function setLogoSpace (int $ width , int $ height , int $ startX = null , int $ startY = null ):QRMatrix {
586+
587+ // for logos we operate in ECC H (30%) only
588+ if ($ this ->eclevel !== 0b10 ){
589+ throw new QRCodeDataException ('ECC level "H" required to add logo space ' );
590+ }
591+
592+ // we need uneven sizes, adjust if needed
593+ if (($ width % 2 ) === 0 ){
594+ $ width ++;
595+ }
596+
597+ if (($ height % 2 ) === 0 ){
598+ $ height ++;
599+ }
600+
601+ // $this->moduleCount includes the quiet zone (if created), we need the QR size here
602+ $ length = $ this ->version * 4 + 17 ;
603+
604+ // throw if the logo space exceeds the maximum error correction capacity
605+ if ($ width * $ height > floor ($ length * $ length * 0.2 )){
606+ throw new QRCodeDataException ('logo space exceeds the maximum error correction capacity ' );
607+ }
608+
609+ // quiet zone size
610+ $ qz = ($ this ->moduleCount - $ length ) / 2 ;
611+ // skip quiet zone and the first 9 rows/columns (finder-, mode-, version- and timing patterns)
612+ $ start = $ qz + 9 ;
613+ // skip quiet zone
614+ $ end = $ this ->moduleCount - $ qz ;
615+
616+ // determine start coordinates
617+ $ startX = ($ startX !== null ? $ startX : ($ length - $ width ) / 2 ) + $ qz ;
618+ $ startY = ($ startY !== null ? $ startY : ($ length - $ height ) / 2 ) + $ qz ;
619+
620+ // clear the space
621+ foreach ($ this ->matrix as $ y => $ row ){
622+ foreach ($ row as $ x => $ val ){
623+ // out of bounds, skip
624+ if ($ x < $ start || $ y < $ start ||$ x >= $ end || $ y >= $ end ){
625+ continue ;
626+ }
627+ // a match
628+ if ($ x >= $ startX && $ x < ($ startX + $ width ) && $ y >= $ startY && $ y < ($ startY + $ height )){
629+ $ this ->set ($ x , $ y , false , $ this ::M_LOGO );
630+ }
631+ }
632+ }
633+
634+ return $ this ;
635+ }
636+
554637 /**
555638 * Maps the binary $data array from QRDataInterface::maskECC() on the matrix, using $maskPattern
556639 *
0 commit comments