Skip to content

Commit

Permalink
Fix sharpness issues
Browse files Browse the repository at this point in the history
  • Loading branch information
endroid committed Jan 13, 2018
1 parent 4764d80 commit 26416b9
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 198 deletions.
13 changes: 13 additions & 0 deletions src/QrCode.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,18 @@ class QrCode implements QrCodeInterface
'r' => 0,
'g' => 0,
'b' => 0,
'a' => 0,
];

private $backgroundColor = [
'r' => 255,
'g' => 255,
'b' => 255,
'a' => 127,
];

private $encoding = 'UTF-8';
private $roundBlockSize = true;
private $errorCorrectionLevel;

private $logoPath;
Expand Down Expand Up @@ -123,6 +126,16 @@ public function getEncoding(): string
return $this->encoding;
}

public function setRoundBlockSize(bool $roundBlockSize): void
{
$this->roundBlockSize = $roundBlockSize;
}

public function getRoundBlockSize(): bool
{
return $this->roundBlockSize;
}

public function setErrorCorrectionLevel(string $errorCorrectionLevel): void
{
$this->errorCorrectionLevel = new ErrorCorrectionLevel($errorCorrectionLevel);
Expand Down
2 changes: 2 additions & 0 deletions src/QrCodeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public function getBackgroundColor(): array;

public function getEncoding(): string;

public function getRoundBlockSize(): bool;

public function getErrorCorrectionLevel(): string;

public function getLogoPath(): ?string;
Expand Down
30 changes: 0 additions & 30 deletions src/Traits/BaconConversionTrait.php

This file was deleted.

39 changes: 39 additions & 0 deletions src/Writer/AbstractWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,49 @@

namespace Endroid\QrCode\Writer;

use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Encoder\Encoder;
use Endroid\QrCode\QrCodeInterface;

abstract class AbstractWriter implements WriterInterface
{
private $data = [];

protected function getData(QrCodeInterface $qrCode): array
{
if (0 !== count($this->data)) {
return $this->data;
}

$name = strtoupper(substr($qrCode->getErrorCorrectionLevel(), 0, 1));
$errorCorrectionLevel = constant('BaconQrCode\Common\ErrorCorrectionLevel::'.$name);

$baconQrCode = Encoder::encode($qrCode->getText(), new ErrorCorrectionLevel($errorCorrectionLevel), $qrCode->getEncoding());

$matrix = $baconQrCode->getMatrix()->getArray()->toArray();
foreach ($matrix as &$row) {
$row = $row->toArray();
}

$this->data['matrix'] = $matrix;
$this->data['block_count'] = count($matrix[0]);
$this->data['block_size'] = $qrCode->getSize() / $this->data['block_count'];
if ($qrCode->getRoundBlockSize()) {
$this->data['block_size'] = intval(floor($this->data['block_size']));
}
$this->data['inner_width'] = $this->data['block_size'] * $this->data['block_count'];
$this->data['inner_height'] = $this->data['block_size'] * $this->data['block_count'];
$this->data['outer_width'] = $qrCode->getSize() + 2 * $qrCode->getMargin();
$this->data['outer_height'] = $qrCode->getSize() + 2 * $qrCode->getMargin();
$this->data['margin_left'] = ($this->data['outer_width'] - $this->data['inner_width']) / 2;
if ($qrCode->getRoundBlockSize()) {
$this->data['margin_left'] = intval(floor($this->data['margin_left']));
}
$this->data['margin_right'] = $this->data['outer_width'] - $this->data['inner_width'] - $this->data['margin_left'];

return $this->data;
}

public function writeDataUri(QrCodeInterface $qrCode): string
{
$dataUri = 'data:'.$this->getContentType().';base64,'.base64_encode($this->writeString($qrCode));
Expand Down
26 changes: 12 additions & 14 deletions src/Writer/BinaryWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,18 @@ class BinaryWriter extends AbstractWriter
{
public function writeString(QrCodeInterface $qrCode): string
{
$string = '
0001010101
0001010101
1000101010
0001010101
0101010101
0001010101
0001010101
0001010101
0001010101
1000101010
';

return $string;
$data = $this->getData($qrCode);

$rows = [];
foreach ($data['matrix'] as $row) {
$values = '';
foreach ($row as $value) {
$values .= $value;
}
$rows[] = $values;
}

return implode("\n", $rows);
}

public static function getContentType(): string
Expand Down
71 changes: 18 additions & 53 deletions src/Writer/EpsWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,68 +9,33 @@

namespace Endroid\QrCode\Writer;

use BaconQrCode\Renderer\Image\Eps;
use BaconQrCode\Writer;
use Endroid\QrCode\QrCodeInterface;
use Endroid\QrCode\Traits\BaconConversionTrait;

class EpsWriter extends AbstractWriter
{
use BaconConversionTrait;

public function writeString(QrCodeInterface $qrCode): string
{
$renderer = new Eps();
$renderer->setWidth($qrCode->getSize());
$renderer->setHeight($qrCode->getSize());
$renderer->setMargin(0);
$renderer->setForegroundColor($this->convertColor($qrCode->getForegroundColor()));
$renderer->setBackgroundColor($this->convertColor($qrCode->getBackgroundColor()));

$writer = new Writer($renderer);
$string = $writer->writeString($qrCode->getText(), $qrCode->getEncoding(), $this->convertErrorCorrectionLevel($qrCode->getErrorCorrectionLevel()));
$string = $this->addMargin($string, $qrCode);

return $string;
}

private function addMargin(string $string, QrCodeInterface $qrCode): string
{
$targetSize = $qrCode->getSize() + $qrCode->getMargin() * 2;

$lines = explode("\n", $string);

$sourceBlockSize = 0;
$additionalWhitespace = $qrCode->getSize();
foreach ($lines as $line) {
if (preg_match('#[0-9]+ [0-9]+ [0-9]+ [0-9]+ F#i', $line) && false === strpos($line, $qrCode->getSize().' '.$qrCode->getSize().' F')) {
$parts = explode(' ', $line);
$sourceBlockSize = $parts[2];
$additionalWhitespace = min($additionalWhitespace, $parts[0]);
}
}

$blockCount = ($qrCode->getSize() - 2 * $additionalWhitespace) / $sourceBlockSize;
$targetBlockSize = $qrCode->getSize() / $blockCount;

foreach ($lines as &$line) {
if (false !== strpos($line, 'BoundingBox')) {
$line = '%%BoundingBox: 0 0 '.$targetSize.' '.$targetSize;
} elseif (false !== strpos($line, $qrCode->getSize().' '.$qrCode->getSize().' F')) {
$line = '0 0 '.$targetSize.' '.$targetSize.' F';
} elseif (preg_match('#[0-9]+ [0-9]+ [0-9]+ [0-9]+ F#i', $line)) {
$parts = explode(' ', $line);
$parts[0] = $qrCode->getMargin() + $targetBlockSize * ($parts[0] - $additionalWhitespace) / $sourceBlockSize;
$parts[1] = $qrCode->getMargin() + $targetBlockSize * ($parts[1] - $additionalWhitespace) / $sourceBlockSize;
$parts[2] = $targetBlockSize;
$parts[3] = $targetBlockSize;
$line = implode(' ', $parts);
$data = $this->getData($qrCode);

$epsData = [];
$epsData[] = '%!PS-Adobe-3.0 EPSF-3.0';
$epsData[] = '%%BoundingBox: 0 0 '.$data['outer_width'].' '.$data['outer_height'];
$epsData[] = '/F { rectfill } def';
$epsData[] = ($qrCode->getBackgroundColor()['r'] / 100).' '.($qrCode->getBackgroundColor()['g'] / 100).' '.($qrCode->getBackgroundColor()['b'] / 100).' setrgbcolor';
$epsData[] = '0 0 '.$data['outer_width'].' '.$data['outer_height'].' F';
$epsData[] = ($qrCode->getForegroundColor()['r'] / 100).' '.($qrCode->getForegroundColor()['g'] / 100).' '.($qrCode->getForegroundColor()['b'] / 100).' setrgbcolor';

foreach ($data['matrix'] as $row => $values) {
foreach ($values as $column => $value) {
if (1 === $value) {
$x = $data['margin_left'] + $data['block_size'] * $column;
$y = $data['margin_left'] + $data['block_size'] * $row;
$epsData[] = $x.' '.$y.' '.$data['block_size'].' '.$data['block_size'].' F';
}
}
}

$string = implode("\n", $lines);

return $string;
return implode("\n", $epsData);
}

public static function getContentType(): string
Expand Down
70 changes: 16 additions & 54 deletions src/Writer/PngWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,32 @@

namespace Endroid\QrCode\Writer;

use BaconQrCode\Renderer\Image\Png;
use BaconQrCode\Writer;
use Endroid\QrCode\Exception\MissingFunctionException;
use Endroid\QrCode\Exception\ValidationException;
use Endroid\QrCode\LabelAlignment;
use Endroid\QrCode\QrCodeInterface;
use Endroid\QrCode\Traits\BaconConversionTrait;
use QrReader;

class PngWriter extends AbstractWriter
{
use BaconConversionTrait;

public function writeString(QrCodeInterface $qrCode): string
{
$renderer = new Png();
$renderer->setWidth($qrCode->getSize());
$renderer->setHeight($qrCode->getSize());
$renderer->setMargin(0);
$renderer->setForegroundColor($this->convertColor($qrCode->getForegroundColor()));
$renderer->setBackgroundColor($this->convertColor($qrCode->getBackgroundColor()));

$writer = new Writer($renderer);
$string = $writer->writeString($qrCode->getText(), $qrCode->getEncoding(), $this->convertErrorCorrectionLevel($qrCode->getErrorCorrectionLevel()));

$image = imagecreatefromstring($string);
$image = $this->addMargin($image, $qrCode->getMargin(), $qrCode->getSize(), $qrCode->getForegroundColor(), $qrCode->getBackgroundColor());
$data = $this->getData($qrCode);

$image = imagecreatetruecolor($data['outer_width'], $data['outer_height']);
$foregroundColor = imagecolorallocatealpha($image, $qrCode->getForegroundColor()['r'], $qrCode->getForegroundColor()['g'], $qrCode->getForegroundColor()['b'], $qrCode->getForegroundColor()['a']);
$backgroundColor = imagecolorallocatealpha($image, $qrCode->getBackgroundColor()['r'], $qrCode->getBackgroundColor()['g'], $qrCode->getBackgroundColor()['b'], $qrCode->getBackgroundColor()['a']);
imagefill($image, 0, 0, $backgroundColor);

foreach ($data['matrix'] as $row => $values) {
foreach ($values as $column => $value) {
if (1 === $value) {
$x = $data['margin_left'] + $data['block_size'] * $column;
$y = $data['margin_left'] + $data['block_size'] * $row;
imagefilledrectangle($image, $x, $y, $x + $data['block_size'], $y + $data['block_size'], $foregroundColor);
}
}
}

if ($qrCode->getLogoPath()) {
$image = $this->addLogo($image, $qrCode->getLogoPath(), $qrCode->getLogoWidth());
Expand All @@ -60,43 +59,6 @@ public function writeString(QrCodeInterface $qrCode): string
return $string;
}

private function addMargin($sourceImage, int $margin, int $size, array $foregroundColor, array $backgroundColor)
{
$additionalWhitespace = $this->calculateAdditionalWhiteSpace($sourceImage, $foregroundColor);

if (0 == $additionalWhitespace && 0 == $margin) {
return $sourceImage;
}

$targetImage = imagecreatetruecolor($size + $margin * 2, $size + $margin * 2);
$backgroundColor = imagecolorallocate($targetImage, $backgroundColor['r'], $backgroundColor['g'], $backgroundColor['b']);
imagefill($targetImage, 0, 0, $backgroundColor);
imagecopyresampled($targetImage, $sourceImage, $margin, $margin, $additionalWhitespace, $additionalWhitespace, $size, $size, $size - 2 * $additionalWhitespace, $size - 2 * $additionalWhitespace);

return $targetImage;
}

private function calculateAdditionalWhiteSpace($image, array $foregroundColor): int
{
$width = imagesx($image);
$height = imagesy($image);

$foregroundColor = imagecolorallocate($image, $foregroundColor['r'], $foregroundColor['g'], $foregroundColor['b']);

$whitespace = $width;
for ($y = 0; $y < $height; ++$y) {
for ($x = 0; $x < $width; ++$x) {
$color = imagecolorat($image, $x, $y);
if ($color == $foregroundColor || $x == $whitespace) {
$whitespace = min($whitespace, $x);
break;
}
}
}

return $whitespace;
}

private function addLogo($sourceImage, string $logoPath, int $logoWidth = null)
{
$logoImage = imagecreatefromstring(file_get_contents($logoPath));
Expand Down
Loading

0 comments on commit 26416b9

Please sign in to comment.