diff --git a/examples/eps.php b/examples/eps.php new file mode 100644 index 000000000..feed60652 --- /dev/null +++ b/examples/eps.php @@ -0,0 +1,62 @@ + + * @copyright 2022 Smiley + * @license MIT + */ + +namespace chillerlan\QRCodeExamples; + +use chillerlan\QRCode\{QRCode, QROptions}; +use chillerlan\QRCode\Common\EccLevel; +use chillerlan\QRCode\Data\QRMatrix; + +require_once __DIR__.'/../vendor/autoload.php'; + +$data = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'; + +$options = new QROptions([ + 'version' => 7, + 'outputType' => QRCode::OUTPUT_EPS, + 'eccLevel' => EccLevel::L, + 'scale' => 5, + 'addQuietzone' => true, + 'cachefile' => __DIR__.'/test.eps', + 'moduleValues' => [ + // finder + QRMatrix::M_FINDER | QRMatrix::IS_DARK => 0xA71111, // dark (true) + QRMatrix::M_FINDER => 0xFFBFBF, // light (false) + QRMatrix::M_FINDER_DOT | QRMatrix::IS_DARK => 0xA71111, // finder dot, dark (true) + // alignment + QRMatrix::M_ALIGNMENT | QRMatrix::IS_DARK => 0xA70364, + QRMatrix::M_ALIGNMENT => 0xFFC9C9, + // timing + QRMatrix::M_TIMING | QRMatrix::IS_DARK => 0x98005D, + QRMatrix::M_TIMING => 0xFFB8E9, + // format + QRMatrix::M_FORMAT | QRMatrix::IS_DARK => 0x003804, + QRMatrix::M_FORMAT => 0x00FB12, + // version + QRMatrix::M_VERSION | QRMatrix::IS_DARK => 0x650098, + QRMatrix::M_VERSION => 0xE0B8FF, + // data + QRMatrix::M_DATA | QRMatrix::IS_DARK => 0x4A6000, + QRMatrix::M_DATA => 0xECF9BE, + // darkmodule + QRMatrix::M_DARKMODULE | QRMatrix::IS_DARK => 0x080063, + // separator + QRMatrix::M_SEPARATOR => 0xAFBFBF, + // quietzone + QRMatrix::M_QUIETZONE => 0xDDDDDD, + ], +]); + +// if viewed in the browser, we should push it as file download as EPS isn't usually supported +header('Content-type: application/postscript'); +header('Content-Disposition: filename="qrcode.eps"'); + +echo (new QRCode($options))->render($data); + + + diff --git a/src/Output/QREps.php b/src/Output/QREps.php new file mode 100644 index 000000000..0e6ffddac --- /dev/null +++ b/src/Output/QREps.php @@ -0,0 +1,101 @@ + + * @copyright 2022 smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use function date, implode, is_int, sprintf; + +/** + * Encapsulated Postscript (EPS) output + * + * @see https://github.com/t0k4rt/phpqrcode/blob/bb29e6eb77e0a2a85bb0eb62725e0adc11ff5a90/qrvect.php#L52-L137 + * @see https://web.archive.org/web/20170818010030/http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/postscript/pdfs/5002.EPSF_Spec.pdf + * @see https://web.archive.org/web/20210419003859/https://www.adobe.com/content/dam/acom/en/devnet/actionscript/articles/PLRM.pdf + */ +class QREps extends QROutputAbstract{ + + /** + * @inheritDoc + */ + protected function moduleValueIsValid($value):bool{ + return is_int($value) && $value >= 0 && $value <= 0xffffff; + } + + /** + * @inheritDoc + */ + protected function getModuleValue($value):array{ + return [ + round((($value & 0xff0000) >> 16) / 255, 5), + round((($value & 0x00ff00) >> 8) / 255, 5), + round(($value & 0x0000ff) / 255, 5) + ]; + } + + /** + * @inheritDoc + */ + protected function getDefaultModuleValue(bool $isDark):array{ + return $isDark ? [0.0, 0.0, 0.0] : [1.0, 1.0, 1.0]; + } + + /** + * @inheritDoc + */ + public function dump(string $file = null):string{ + $file ??= $this->options->cachefile; + $saveToFile = $file !== null; + + $eps = [ + // main header + '%!PS-Adobe-3.0 EPSF-3.0', + '%%Creator: php-qrcode (https://github.com/chillerlan/php-qrcode)', + '%%Title: QR Code', + sprintf('%%%%CreationDate: %1$s', date('c')), + '%%DocumentData: Clean7Bit', + '%%LanguageLevel: 3', + sprintf('%%%%BoundingBox: 0 -%1$s %1$s 0', $this->length), + '%%EndComments', + // function definitions + '%%BeginProlog', + '/F { rectfill } def', + '/S { setrgbcolor } def', + '%%EndProlog', + // rotate into the proper orientation and scale to fit + '-90 rotate', + sprintf('%1$s %1$s scale', $this->scale), + ]; + + // create the path elements + $paths = $this->collectModules(fn(int $x, int $y):string => sprintf('%s %s 1 1 F', $x, $y)); + + foreach($paths as $M_TYPE => $path){ + + if(empty($path)){ + continue; + } + + $eps[] = sprintf('%f %f %f S', ...$this->moduleValues[$M_TYPE]); + $eps[] = implode("\n", $path); + } + + // end file + $eps[] = '%%EOF'; + + $data = implode("\n", $eps); + + if($saveToFile){ + $this->saveToFile($data, $file); + } + + return $data; + } + +} diff --git a/src/QRCode.php b/src/QRCode.php index 92a957af4..dd3091617 100755 --- a/src/QRCode.php +++ b/src/QRCode.php @@ -13,7 +13,7 @@ namespace chillerlan\QRCode; use chillerlan\QRCode\Common\{EccLevel, ECICharset, MaskPattern, Mode}; use chillerlan\QRCode\Data\{AlphaNum, Byte, ECI, Kanji, Number, QRCodeDataException, QRData, QRDataModeInterface, QRMatrix}; use chillerlan\QRCode\Decoder\{Decoder, DecoderResult, LuminanceSourceInterface}; -use chillerlan\QRCode\Output\{QRCodeOutputException, QRFpdf, QRGdImage, QRImagick, QRMarkup, QROutputInterface, QRString}; +use chillerlan\QRCode\Output\{QRCodeOutputException, QRFpdf, QRGdImage, QRImagick, QRMarkup, QREps, QROutputInterface, QRString}; use chillerlan\Settings\SettingsContainerInterface; use function class_exists, class_implements, in_array, mb_convert_encoding, mb_detect_encoding; @@ -80,6 +80,8 @@ class QRCode{ /** @var string */ public const OUTPUT_FPDF = 'fpdf'; /** @var string */ + public const OUTPUT_EPS = 'eps'; + /** @var string */ public const OUTPUT_CUSTOM = 'custom'; /** @@ -97,6 +99,7 @@ class QRCode{ self::OUTPUT_STRING_TEXT => QRString::class, self::OUTPUT_IMAGICK => QRImagick::class, self::OUTPUT_FPDF => QRFpdf::class, + self::OUTPUT_EPS => QREps::class, ]; /**