diff --git a/src/Ajaxray/PHPWatermark/CommandBuilders/AbstractCommandBuilder.php b/src/Ajaxray/PHPWatermark/CommandBuilders/AbstractCommandBuilder.php index fa3b82b..ad6b996 100644 --- a/src/Ajaxray/PHPWatermark/CommandBuilders/AbstractCommandBuilder.php +++ b/src/Ajaxray/PHPWatermark/CommandBuilders/AbstractCommandBuilder.php @@ -8,7 +8,7 @@ namespace Ajaxray\PHPWatermark\CommandBuilders; - +use Ajaxray\PHPWatermark\Watermark; use Ajaxray\PHPWatermark\Requirements\RequirementsChecker; abstract class AbstractCommandBuilder @@ -58,7 +58,7 @@ abstract public function getTextMarkCommand($text, $output, array $options); */ protected function getSource() { - return escapeshellarg($this->source); + return Watermark::escapeShellArg($this->source); } /** @@ -69,7 +69,7 @@ protected function getSource() protected function prepareContext($output, array $options) { $this->options = $options; - return array($this->getSource(), escapeshellarg($output)); + return array($this->getSource(), Watermark::escapeShellArg($output)); } protected function getAnchor() @@ -115,7 +115,16 @@ protected function getTextTileSize() protected function getFont() { return '-pointsize '.intval($this->options['fontSize']). - ' -font '.escapeshellarg($this->options['font']); + ' -font '.Watermark::escapeShellArg($this->options['font']); + } + + /** + * @return string + */ + protected function getEncoding() + { + if(empty($this->options['encoding'])) return ''; + return '-encoding '.Watermark::escapeShellArg($this->options['encoding']); } protected function getDuelTextOffset() @@ -148,4 +157,31 @@ protected function getTile() { return empty($this->isTiled()) ? '' : '-tile'; } + + + protected function getDuelTextColor(array $options=[]) + { + $textShadowRGBA=[255,255,255,$this->getOpacity()];//白色 + $textColorRGBA=[0,0,0,$this->getOpacity()];//黑色 + if(isset($options['textShadowRGBA'])){ + if(!is_array($options['textShadowRGBA'])) $options['textShadowRGBA']=explode(',',$options['textShadowRGBA'],4); + for($i=0;$i<4;$i++){ + if(!isset($options['textShadowRGBA'][$i])) break; + $textShadowRGBA[$i]=$options['textShadowRGBA'][$i]; + } + } + if(isset($options['textColorRGBA'])){ + if(!is_array($options['textColorRGBA'])) $options['textColorRGBA']=explode(',',$options['textColorRGBA'],4); + for($i=0;$i<4;$i++){ + if(!isset($options['textColorRGBA'][$i])) break; + $textColorRGBA[$i]=$options['textColorRGBA'][$i]; + } + } + $textShadow="rgba({$textShadowRGBA[0]},{$textShadowRGBA[1]},{$textShadowRGBA[2]},{$textShadowRGBA[3]})"; + $textColor="rgba({$textColorRGBA[0]},{$textColorRGBA[1]},{$textColorRGBA[2]},{$textColorRGBA[3]})"; + return [ + "fill \"$textShadow\"",//text shadow + "fill \"$textColor\"",//text color + ]; + } } diff --git a/src/Ajaxray/PHPWatermark/CommandBuilders/ImageCommandBuilder.php b/src/Ajaxray/PHPWatermark/CommandBuilders/ImageCommandBuilder.php index e746dfb..ee9dab5 100644 --- a/src/Ajaxray/PHPWatermark/CommandBuilders/ImageCommandBuilder.php +++ b/src/Ajaxray/PHPWatermark/CommandBuilders/ImageCommandBuilder.php @@ -24,7 +24,7 @@ class ImageCommandBuilder extends AbstractCommandBuilder public function getImageMarkCommand($markerImage, $output, array $options) { list($source, $destination) = $this->prepareContext($output, $options); - $marker = escapeshellarg($markerImage); + $marker = Watermark::escapeShellArg($markerImage); $anchor = $this->getAnchor(); $offset = $this->getImageOffset(); @@ -32,7 +32,7 @@ public function getImageMarkCommand($markerImage, $output, array $options) $tile = $this->getTile(); $opacity = $this->getImageOpacity(); - return "composite -$anchor -$offset -$opacity $tile $marker $source $destination"; + return Watermark::$commandPrefix."composite -$anchor -$offset -$opacity $tile $marker $source $destination"; } /** @@ -46,36 +46,30 @@ public function getImageMarkCommand($markerImage, $output, array $options) public function getTextMarkCommand($text, $output, array $options) { list($source, $destination) = $this->prepareContext($output, $options); - $text = escapeshellarg($text); + $text = Watermark::escapeShellArg($text,Watermark::$isWindows); + $encoding = $this->getEncoding(); $anchor = $this->getAnchor(); $rotate = $this->getRotate(); $font = $this->getFont(); - list($light, $dark) = $this->getDuelTextColor(); + list($light, $dark) = $this->getDuelTextColor($options); list($offsetLight, $offsetDark) = $this->getDuelTextOffset(); - - $draw = " -draw \"$rotate $anchor $light text $offsetLight $text $dark text $offsetDark $text\" "; + $draw = "$rotate $anchor $light text $offsetLight $text $dark text $offsetDark $text"; + $draw = str_replace('"',"'",$draw); + $draw = "$encoding -draw \"$draw\" "; if($this->isTiled()) { $size = $this->getTextTileSize(); - $command = "convert $size xc:none $font -$anchor $draw miff:- "; - $command .= " | composite -tile - $source $destination"; + $command = Watermark::$commandPrefix."convert $size xc:none $font -$anchor $draw miff:- "; + $command .= " | ".Watermark::$commandPrefix."composite -tile - $source $destination"; } else { - $command = "convert $source $font $draw $destination"; + $command = Watermark::$commandPrefix."convert $source $font $draw $destination"; } return $command; } - protected function getDuelTextColor() - { - return [ - "fill \"rgba\\(255,255,255,{$this->getOpacity()}\\)\"", - "fill \"rgba\\(0,0,0,{$this->getOpacity()}\\)\"", - ]; - } - /** * @return string */ diff --git a/src/Ajaxray/PHPWatermark/CommandBuilders/PDFCommandBuilder.php b/src/Ajaxray/PHPWatermark/CommandBuilders/PDFCommandBuilder.php index 605eaf1..3388b07 100644 --- a/src/Ajaxray/PHPWatermark/CommandBuilders/PDFCommandBuilder.php +++ b/src/Ajaxray/PHPWatermark/CommandBuilders/PDFCommandBuilder.php @@ -8,6 +8,7 @@ namespace Ajaxray\PHPWatermark\CommandBuilders; +use Ajaxray\PHPWatermark\Watermark; class PDFCommandBuilder extends AbstractCommandBuilder { @@ -23,13 +24,13 @@ class PDFCommandBuilder extends AbstractCommandBuilder public function getImageMarkCommand($markerImage, $output, array $options) { list($source, $destination) = $this->prepareContext($output, $options); - $marker = escapeshellarg($markerImage); + $marker = Watermark::escapeShellArg($markerImage); $opacity = $this->getMarkerOpacity(); $anchor = $this->getAnchor(); $offset = $this->getImageOffset(); - return "convert $marker $opacity miff:- | convert -density 100 $source null: - -$anchor -$offset -quality 100 -compose multiply -layers composite $destination"; + return Watermark::$commandPrefix."convert $marker $opacity miff:- | convert -density 100 $source null: - -$anchor -$offset -quality 100 -compose multiply -layers composite $destination"; } /** @@ -43,15 +44,16 @@ public function getImageMarkCommand($markerImage, $output, array $options) public function getTextMarkCommand($text, $output, array $options) { list($source, $destination) = $this->prepareContext($output, $options); - $text = escapeshellarg($text); + $text = Watermark::escapeShellArg($text); + $encoding = $this->getEncoding(); $anchor = $this->getAnchor(); $rotate = $this->getRotate(); $font = $this->getFont(); - list($light, $dark) = $this->getDuelTextColor(); + list($light, $dark) = $this->getDuelTextColor($options); list($offsetLight, $offsetDark) = $this->getDuelTextOffset(); - return "convert $source -$anchor -quality 100 -density 100 $font -$light -annotate {$rotate}{$offsetLight} $text -$dark -annotate {$rotate}{$offsetDark} $text $destination"; + return Watermark::$commandPrefix."convert $encoding $source -$anchor -quality 100 -density 100 $font -$light -annotate {$rotate}{$offsetLight} $text -$dark -annotate {$rotate}{$offsetDark} $text $destination"; } private function getMarkerOpacity() @@ -74,11 +76,4 @@ protected function getRotate() return empty($this->options['rotate']) ? '' : "{$this->options['rotate']}x{$this->options['rotate']}"; } - protected function getDuelTextColor() - { - return [ - "fill \"rgba(255,255,255,{$this->getOpacity()})\"", - "fill \"rgba(0,0,0,{$this->getOpacity()})\"", - ]; - } } diff --git a/src/Ajaxray/PHPWatermark/Requirements/RequirementsChecker.php b/src/Ajaxray/PHPWatermark/Requirements/RequirementsChecker.php index 9e15e7e..5af4b45 100644 --- a/src/Ajaxray/PHPWatermark/Requirements/RequirementsChecker.php +++ b/src/Ajaxray/PHPWatermark/Requirements/RequirementsChecker.php @@ -9,12 +9,14 @@ namespace Ajaxray\PHPWatermark\Requirements; +use Ajaxray\PHPWatermark\Watermark; + class RequirementsChecker { public function checkImagemagickInstallation() { - exec("convert -version", $out, $rcode); + exec(Watermark::$commandPrefix.'convert -version', $out, $rcode); if ($rcode) { throw new \BadFunctionCallException("ImageMagick not found in this system."); diff --git a/src/Ajaxray/PHPWatermark/Watermark.php b/src/Ajaxray/PHPWatermark/Watermark.php index b8bdea5..60ea3fa 100644 --- a/src/Ajaxray/PHPWatermark/Watermark.php +++ b/src/Ajaxray/PHPWatermark/Watermark.php @@ -53,6 +53,9 @@ class Watermark private $commander; private $debug = false; + static $commandPrefix=''; + static $isWindows=null; + /** * Watermark constructor. * @@ -60,25 +63,29 @@ class Watermark */ public function __construct($source) { + if(self::$isWindows===null) self::$isWindows=stripos(PHP_OS,'win')!==false; $this->source = $source; $this->commander = $this->getCommandBuilder($source); return $this; } + static public function escapeShellArg($arg,$singleQuotes=false){ + if(!$singleQuotes) return escapeshellarg($arg); + return "'".addcslashes($arg,"'")."'"; + } + public function withText($text, $writeTo = null) { $destination = $writeTo ?: $this->source; $this->ensureWritable(($writeTo ? dirname($destination) : $destination)); - + $command = $this->commander->getTextMarkCommand($text, $destination, $this->options); + $output = $returnCode = null; + exec($command, $output, $returnCode); if($this->debug) { - return $this->commander->getTextMarkCommand($text, $destination, $this->options); - } else { - $output = $returnCode = null; - exec($this->commander->getTextMarkCommand($text, $destination, $this->options), $output, $returnCode); - - return (empty($output) && $returnCode === 0); + echo var_dump(compact('command','output','returnCode')),PHP_EOL; } + return (empty($output) && $returnCode === 0); } public function withImage($marker, $writeTo = null) @@ -86,15 +93,13 @@ public function withImage($marker, $writeTo = null) $destination = $writeTo ?: $this->source; $this->ensureExists($marker); $this->ensureWritable(($writeTo ? dirname($destination) : $destination)); - + $command = $this->commander->getImageMarkCommand($marker, $destination, $this->options); + $output = $returnCode = null; + exec($command, $output, $returnCode); if($this->debug) { - return $this->commander->getImageMarkCommand($marker, $destination, $this->options); - } else { - $output = $returnCode = null; - exec($this->commander->getImageMarkCommand($marker, $destination, $this->options), $output, $returnCode); - - return (empty($output) && $returnCode === 0); + echo var_dump(compact('command','output','returnCode')),PHP_EOL; } + return (empty($output) && $returnCode === 0); } /** @@ -110,11 +115,11 @@ protected function getCommandBuilder($sourcePath) if (preg_match(self::PATTERN_MIME_IMAGE, $mimeType)) { return new CommandBuilders\ImageCommandBuilder($sourcePath); - } elseif (preg_match(self::PATTERN_MIME_PDF, $mimeType)) { + } + if (preg_match(self::PATTERN_MIME_PDF, $mimeType)) { return new CommandBuilders\PDFCommandBuilder($sourcePath); - } else { - throw new \InvalidArgumentException("The source file type $mimeType is not supported."); - } + } + throw new \InvalidArgumentException("The source file type $mimeType is not supported."); } /** @@ -233,6 +238,39 @@ public function setRotate($rotate) return $this; } + /** + * @param array|string $textShadowRGBA Text Shadow RGBA + * @return Watermark + */ + public function setTextShadow($textShadowRGBA) + { + $this->options['textShadowRGBA'] = $textShadowRGBA; + + return $this; + } + + /** + * @param array|string $textColorRGBA Text Color RGBA + * @return Watermark + */ + public function setTextColor($textColorRGBA) + { + $this->options['textColorRGBA'] = $textColorRGBA; + + return $this; + } + + /** + * @param string $encoding Text Color RGBA + * @return Watermark + */ + public function setEncoding($encoding='UTF-8') + { + $this->options['encoding']=$encoding; + + return $this; + } + /** * @param bool $debug * @return Watermark diff --git a/src/Ajaxray/PHPWatermark/import.php b/src/Ajaxray/PHPWatermark/import.php new file mode 100644 index 0000000..19e6bcd --- /dev/null +++ b/src/Ajaxray/PHPWatermark/import.php @@ -0,0 +1,12 @@ +getTxtCommandWithOption([]); - $this->assertEquals($this->cmdText, $execCommand); + $this->assertEquals(Watermark::$commandPrefix.$this->cmdText, $execCommand); } public function testWatermarkingWithChangingTextLocation() @@ -60,41 +60,41 @@ public function testWatermarkingWithChangingTextLocation() 'offsetX' => 10, 'offsetY' => 15, ]); - $expected = str_replace(['gravity Center', 'text 0,0', 'text 1,1'], ['gravity SouthWest', 'text 10,15', 'text 11,16'], $this->cmdText); + $expected = str_replace(['gravity Center', 'text 0,0', 'text 1,1'], ['gravity SouthWest', 'text 10,15', 'text 11,16'], Watermark::$commandPrefix.$this->cmdText); $this->assertEquals($expected, $execCommand); } public function testWatermarkingWithChangingTextOpacity() { $execCommand = $this->getTxtCommandWithOption(['opacity' => .7]); - $expected = str_replace(['255,255,255,0.3', '0,0,0,0.3'], ['255,255,255,0.7', '0,0,0,0.7'], $this->cmdText); + $expected = str_replace(['255,255,255,0.3', '0,0,0,0.3'], ['255,255,255,0.7', '0,0,0,0.7'], Watermark::$commandPrefix.$this->cmdText); $this->assertEquals($expected, $execCommand); } public function testWatermarkingWithChangingTextRotation() { $execCommand = $this->getTxtCommandWithOption(['rotate' => 15]); - $expected = str_replace('-draw "', '-draw "rotate 15', $this->cmdText); + $expected = str_replace('-draw "', '-draw "rotate 15', Watermark::$commandPrefix.$this->cmdText); $this->assertEquals($expected, $execCommand); } public function testWatermarkingWithChangingTextFont() { $execCommand = $this->getTxtCommandWithOption(['font' => 'sans-serif', 'fontSize' => 36]); - $expected = str_replace('-pointsize 24 -font \'Arial\'', '-pointsize 36 -font \'sans-serif\'', $this->cmdText); + $expected = str_replace('-pointsize 24 -font \'Arial\'', '-pointsize 36 -font \'sans-serif\'', Watermark::$commandPrefix.$this->cmdText); $this->assertEquals($expected, $execCommand); } public function testWatermarkingWithTiledText() { $execCommand = $this->getTxtCommandWithOption(['tiled' => true]); - $this->assertEquals($this->cmdTiledText, $execCommand); + $this->assertEquals(Watermark::$commandPrefix.$this->cmdTiledText, $execCommand); } public function testBasicWatermarkingWithImage() { $execCommand = $this->getImgCommandWithOption([]); - $this->assertEquals($this->cmdImg, $execCommand); + $this->assertEquals(Watermark::$commandPrefix.$this->cmdImg, $execCommand); } public function testWatermarkingWithChangingImageLocation() @@ -104,21 +104,21 @@ public function testWatermarkingWithChangingImageLocation() 'offsetX' => 100, 'offsetY' => 150, ]); - $expected = str_replace(['gravity Center', '+0+0'], ['gravity NorthEast', '+100+150'], $this->cmdImg); + $expected = str_replace(['gravity Center', '+0+0'], ['gravity NorthEast', '+100+150'], Watermark::$commandPrefix.$this->cmdImg); $this->assertEquals($expected, $execCommand); } public function testWatermarkingWithChangingImageOpacity() { $execCommand = $this->getImgCommandWithOption(['opacity' => .5]); - $expected = str_replace('30%', '50%', $this->cmdImg); + $expected = str_replace('30%', '50%', Watermark::$commandPrefix.$this->cmdImg); $this->assertEquals($expected, $execCommand); } public function testWatermarkingWithChangingImageStyle() { $execCommand = $this->getImgCommandWithOption(['style' => Watermark::STYLE_IMG_COLORLESS]); - $expected = str_replace('-dissolve', '-watermark', $this->cmdImg); + $expected = str_replace('-dissolve', '-watermark', Watermark::$commandPrefix.$this->cmdImg); $this->assertEquals($expected, $execCommand); } diff --git a/tests/Ajaxray/PHPWatermark/Tests/CommandBuilders/PDFCommandBuilderTest.php b/tests/Ajaxray/PHPWatermark/Tests/CommandBuilders/PDFCommandBuilderTest.php index c9af757..e7efe73 100644 --- a/tests/Ajaxray/PHPWatermark/Tests/CommandBuilders/PDFCommandBuilderTest.php +++ b/tests/Ajaxray/PHPWatermark/Tests/CommandBuilders/PDFCommandBuilderTest.php @@ -49,25 +49,25 @@ public function __construct() public function testTextWatermarking() { $execCommand = $this->getTxtCommandWithOption([]); - $this->assertEquals($this->cmdText, $execCommand); + $this->assertEquals(Watermark::$commandPrefix.$this->cmdText, $execCommand); } public function testTextWatermarkingWithRotate() { $execCommand = $this->getTxtCommandWithOption(['rotate' => 30]); - $this->assertEquals(str_replace('-annotate ', '-annotate 30x30', $this->cmdText), $execCommand); + $this->assertEquals(str_replace('-annotate ', '-annotate 30x30', Watermark::$commandPrefix.$this->cmdText), $execCommand); } public function testTextWatermarkingConfigureOpacity() { $execCommand = $this->getTxtCommandWithOption(['opacity' => .6]); - $this->assertEquals(str_replace(',0.3)', ',0.6)', $this->cmdText), $execCommand); + $this->assertEquals(str_replace(',0.3)', ',0.6)', Watermark::$commandPrefix.$this->cmdText), $execCommand); } public function testTextWatermarkingConfigureFont() { $execCommand = $this->getTxtCommandWithOption(['fontSize' => 48, 'font' => 'monospace']); - $this->assertEquals(str_replace(['24', 'Arial'], ['48', 'monospace'], $this->cmdText), $execCommand); + $this->assertEquals(str_replace(['24', 'Arial'], ['48', 'monospace'], Watermark::$commandPrefix.$this->cmdText), $execCommand); } public function testTextWatermarkingConfigurePosition() @@ -77,13 +77,13 @@ public function testTextWatermarkingConfigurePosition() 'offsetX' => '220', 'offsetY' => '50', ]); - $this->assertEquals(str_replace(['Center', '+0+0', '+1+1'], ['SouthEast', '+220+50', '+221+51'], $this->cmdText), $execCommand); + $this->assertEquals(str_replace(['Center', '+0+0', '+1+1'], ['SouthEast', '+220+50', '+221+51'], Watermark::$commandPrefix.$this->cmdText), $execCommand); } public function testImageWatermarkingBasic() { $execCommand = $this->builder->getImageMarkCommand('path/logo.png', 'path/output.pdf', $this->options); - $this->assertEquals($this->cmdImg, $execCommand); + $this->assertEquals(Watermark::$commandPrefix.$this->cmdImg, $execCommand); } public function testImageWatermarkingWithLocationChange() @@ -93,14 +93,14 @@ public function testImageWatermarkingWithLocationChange() 'offsetX' => 50, 'offsetY' => 100, ]); - $expected = str_replace('-gravity Center -geometry +0+0', '-gravity SouthEast -geometry +50+100', $this->cmdImg); + $expected = str_replace('-gravity Center -geometry +0+0', '-gravity SouthEast -geometry +50+100', Watermark::$commandPrefix.$this->cmdImg); $this->assertEquals($expected, $execCommand); } public function testImageWatermarkingWithOpacityChange() { $execCommand = $this->getImgCommandWithOption(['opacity' => .7]); - $expected = str_replace('-evaluate set 30%', '-evaluate set 70%', $this->cmdImg); + $expected = str_replace('-evaluate set 30%', '-evaluate set 70%', Watermark::$commandPrefix.$this->cmdImg); $this->assertEquals($expected, $execCommand); }