From 6d42677732628fca7a2e0bc646a9631f53eacbd2 Mon Sep 17 00:00:00 2001 From: Shine Date: Sat, 3 Aug 2024 23:20:42 +0200 Subject: [PATCH] Instead using external API (api.qrserver.com) .. use chillerlan/php-qrcode --- Dockerfile | 97 +++++++++++++-- src/forms/qrcode_options.php | 4 +- src/forms/table_static.php | 2 +- src/lib/Qrcode/Qrcode.php | 231 ++++++++++++++++++++++++++++++++--- 4 files changed, 303 insertions(+), 31 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7e801d9..5668259 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,82 @@ -FROM php:latest +FROM php:8.3 -RUN apt-get update && \ - apt-get install -y --no-install-recommends less nano tini curl bash tar git zip unzip && \ - #echo "**** # To install networking tools for testing purpose ****" && \ - apt-get install -y --no-install-recommends iputils-ping dnsutils net-tools procps && \ - #echo "**** cleanup ****" && \ - apt-get autoremove -y && \ - apt-get clean -y +RUN if [ "$(grep '^VERSION_ID=' /etc/os-release | cut -d '=' -f 2 | tr -d '"')" -eq "9" ]; then \ + sed -i -e 's/deb.debian.org/archive.debian.org/g' \ + -e 's/security.debian.org/archive.debian.org/g' \ + -e '/stretch-updates/d' /etc/apt/sources.list; \ + fi -RUN apt-get update -RUN apt-get install -y --no-install-recommends libzip-dev libjpeg62-turbo-dev libpng-dev libfreetype6-dev +# Download script to install PHP extensions and dependencies +ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/ + +RUN chmod +x /usr/local/bin/install-php-extensions + +RUN DEBIAN_FRONTEND=noninteractive apt-get update -q \ + && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y \ + curl \ + dnsutils \ + git \ + iputils-ping \ + less \ + libzip-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libfreetype6-dev \ + nano \ + net-tools \ + procps \ + tar \ + tini \ + zip unzip \ +# iconv, mbstring and pdo_sqlite are omitted as they are already installed + && PHP_EXTENSIONS=" \ + amqp \ + bcmath \ + bz2 \ + calendar \ + event \ + exif \ + gd \ + gettext \ + intl \ + ldap \ + memcached \ + mysqli \ + opcache \ + pdo_mysql \ + pdo_pgsql \ + pgsql \ + redis \ + soap \ + sockets \ + xsl \ + zip \ + " \ + && case "$PHP_VERSION" in \ + 5.6.*) PHP_EXTENSIONS="$PHP_EXTENSIONS mcrypt mysql";; \ + 7.0.*|7.1.*) PHP_EXTENSIONS="$PHP_EXTENSIONS mcrypt";; \ + esac \ + # Install Imagick from master on PHP >= 8.3, because imagick 3.7.0 broke on latest PHP releases and Imagick maintainers don't care to tag a newer release + && if [ $(php -r 'echo PHP_VERSION_ID;') -lt 80300 ]; then \ + PHP_EXTENSIONS="$PHP_EXTENSIONS imagick"; \ + else PHP_EXTENSIONS="$PHP_EXTENSIONS https://api.github.com/repos/Imagick/imagick/tarball/28f27044e435a2b203e32675e942eb8de620ee58"; \ + fi \ + && install-php-extensions $PHP_EXTENSIONS \ + && if command -v a2enmod; then a2enmod rewrite; fi + +# Install Composer. +ENV PATH=$PATH:/root/composer/vendor/bin \ + COMPOSER_ALLOW_SUPERUSER=1 \ + COMPOSER_HOME=/root/composer +RUN cd /opt \ + # Download installer and check for its integrity. + && curl -sSL https://getcomposer.org/installer > composer-setup.php \ + && curl -sSL https://composer.github.io/installer.sha384sum > composer-setup.sha384sum \ + && sha384sum --check composer-setup.sha384sum \ + # Install Composer 2. + && php composer-setup.php --install-dir=/usr/local/bin --filename=composer --2 \ + # Remove installer files. + && rm /opt/composer-setup.php /opt/composer-setup.sha384sum RUN docker-php-source extract RUN docker-php-ext-install pdo_mysql zip exif pcntl gd @@ -17,7 +84,17 @@ RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli RUN docker-php-ext-install gettext && docker-php-ext-enable gettext RUN docker-php-ext-install sockets && docker-php-ext-enable sockets +RUN mkdir -p /opt && chmod 777 /opt +WORKDIR /opt +RUN git clone https://github.com/chillerlan/php-qrcode.git \ + && chmod -R 777 ./php-qrcode +RUN cp ./php-qrcode/composer.json /var/www/html/composer.json +RUN mkdir -p /var/www/html/test && chmod 777 /var/www/html/test +RUN cp ./php-qrcode/examples/image.php /var/www/html/test/image.php +RUN cp -R ./php-qrcode/src /var/www/html/ + WORKDIR /var/www/html +RUN composer update COPY ./src ./ RUN chmod 755 *; EXPOSE 80 diff --git a/src/forms/qrcode_options.php b/src/forms/qrcode_options.php index d918862..b7f3b34 100644 --- a/src/forms/qrcode_options.php +++ b/src/forms/qrcode_options.php @@ -45,6 +45,7 @@ + @@ -75,6 +76,7 @@ + @@ -107,4 +109,4 @@ "/> -
\ No newline at end of file +
diff --git a/src/forms/table_static.php b/src/forms/table_static.php index 4fa8e18..23a72ad 100644 --- a/src/forms/table_static.php +++ b/src/forms/table_static.php @@ -58,7 +58,7 @@ - + '; ?> diff --git a/src/lib/Qrcode/Qrcode.php b/src/lib/Qrcode/Qrcode.php index 8686d09..0043258 100644 --- a/src/lib/Qrcode/Qrcode.php +++ b/src/lib/Qrcode/Qrcode.php @@ -1,5 +1,19 @@ setOptions($input_data); - if(!file_exists(SAVED_QRCODE_DIRECTORY.$data_to_db['filename'].'.'.$data_to_db['format'])){ - $url = - 'https://api.qrserver.com/v1/create-qr-code/?data='. - $data_to_qrcode. - '&&size='.$options['size'].'x'.$options['size']. - '&ecc='.$options['errorCorrectionLevel']. - '&margin=0&color='.$options['foreground']. - '&bgcolor='.$options['background']. - '&qzone=2'. - '&format='.$data_to_db['format']; + $outputInterface = QRGdImagePNG::class; + $imageFormat = strtolower($data_to_db['format']); + $fileExt = $imageFormat; + $forceBlackWhite = false; - $content = file_get_contents($url); - - $filename = SAVED_QRCODE_DIRECTORY.$data_to_db['filename'].'.'.$data_to_db['format']; - - try{ - file_put_contents($filename, $content); + + switch ($imageFormat) + { + case 'png': + $outputInterface = QRGdImagePNG::class; + $imageFormat = 'png'; + break; + case 'gif': + $outputInterface = QRImagick::class; + $imageFormat = 'gif'; + break; + case 'jpg': + $outputInterface = QRImagick::class; + $imageFormat = 'jpg'; + break; + case 'jpeg': + $outputInterface = QRImagick::class; + $imageFormat = 'jpeg'; + break; + case 'svg': + # $outputInterface = QRImagick::class; + $outputInterface = QRMarkupSVG::class; + $imageFormat = 'svg'; + break; + case 'svgbw': + $outputInterface = QRMarkupSVG::class; + $imageFormat = 'svg'; + $fileExt = 'svg'; + $forceBlackWhite = true; + $data_to_db['format'] = $fileExt; + $data_to_db['qrcode'] = str_replace('.svgbw', '.svg', $data_to_db['qrcode']); + break; + case 'eps': + $outputInterface = QREps::class; + $imageFormat = 'eps'; + break; + } + + + if(!file_exists(SAVED_QRCODE_DIRECTORY.$data_to_db['filename'].'.' . $fileExt)){ + $eccLevel = constant(EccLevel::class.'::'.strtoupper($options['errorCorrectionLevel'])); + + $qroptions = new QROptions; + $qroptions->outputInterface = $outputInterface; + $qroptions->outputBase64 = false; + $qroptions->eccLevel = $eccLevel; + $qroptions->quietzoneSize = 2; + + $moduleValues = [ + // finder + QRMatrix::M_FINDER_DARK => $options['foreground'], + QRMatrix::M_FINDER_DOT => $options['foreground'], + QRMatrix::M_FINDER => $options['background'], + // alignment + QRMatrix::M_ALIGNMENT_DARK => $options['foreground'], + QRMatrix::M_ALIGNMENT => $options['background'], + // timing + QRMatrix::M_TIMING_DARK => $options['foreground'], + QRMatrix::M_TIMING => $options['background'], + // format + QRMatrix::M_FORMAT_DARK => $options['foreground'], + QRMatrix::M_FORMAT => $options['background'], + // version + QRMatrix::M_VERSION_DARK => $options['foreground'], + QRMatrix::M_VERSION => $options['background'], + // data + QRMatrix::M_DATA_DARK => $options['foreground'], + QRMatrix::M_DATA => $options['background'], + // darkmodule + QRMatrix::M_DARKMODULE => $options['foreground'], + // separator + QRMatrix::M_SEPARATOR => $options['background'], + // quietzone + QRMatrix::M_QUIETZONE => $options['background'], + ]; + + if(in_array($data_to_db['format'], ['png', 'jpg', 'gif'], true)) + { + $moduleValues = array_map(function($v) + { + if(preg_match('/[a-f\d]{6}/i', $v) === 1) + { + return array_map('hexdec', str_split($v, 2)); + } + + return null; + }, $moduleValues); + + $qroptions->moduleValues = $moduleValues; } - catch(Exception $e){ + else + { + $moduleValues = array_map(function($v) + { + if(preg_match('/[a-f\d]{6}/i', $v) === 1) + { + return '#' . $v ; + } + + return null; + }, $moduleValues); + + $qroptions->moduleValues = $moduleValues; + } + + if ($outputInterface === QRMarkupSVG::class) + { + $qroptions->version = 7; + // if set to false, the light modules won't be rendered + $qroptions->drawLightModules = true; + $qroptions->svgUseFillAttributes = false; + // draw the modules as circles isntead of squares + $qroptions->drawCircularModules = true; + $qroptions->circleRadius = 0.4; + // connect paths + $qroptions->connectPaths = true; + // keep modules of these types as square + $qroptions->keepAsSquare = [ + QRMatrix::M_FINDER_DARK, + QRMatrix::M_FINDER_DOT, + QRMatrix::M_ALIGNMENT_DARK, + ]; + + if($forceBlackWhite) + { + $qroptions->drawLightModules = false; + $qroptions->drawCircularModules = false; + // https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient + $qroptions->svgDefs = ' + + + + + '; + } + else + { + // https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient + $qroptions->svgDefs = ' + + + + + + + + + '; + } + } + else + { + $qroptions->version = -1; + $qroptions->scale = 20; + $qroptions->quality = 83; + } + + if ($outputInterface === QRImagick::class) + { + $qroptions->imagickFormat = $imageFormat; + $qroptions->returnResource = true; + $imagick = (new QRCodeEx($qroptions))->render(urldecode($data_to_qrcode)); + $imagick->scaleImage($options['size'], $options['size'], true); + $content = $imagick->getImageBlob(); + $imagick->destroy(); + } + else + { + $content = (new QRCodeEx($qroptions))->render(urldecode($data_to_qrcode)); + } + + $filename = SAVED_QRCODE_DIRECTORY.$data_to_db['filename'].'.' . $fileExt; + + try + { + file_put_contents($filename, $content); + if ($outputInterface !== QRImagick::class && + $outputInterface !== QRMarkupSVG::class) + { + $imagick = new \Imagick(realpath($filename)); + $imagick->resizeImage($options['size'], $options['size'], imagick::FILTER_LANCZOS, 1, false); + $imagick->writeImage($filename); + $imagick->destroy(); + } + } + catch(Exception $e) + { $this->failure($e->getMessage()); } - + // If you want you can customiaddLogo($data_to_db['qrcode'], $options['optionlogo']);