Merge pull request #461 from romainneutron/stamp-watermark

[3.8] Remove dependency on convert and composite binaries
This commit is contained in:
Romain Neutron
2013-07-04 05:54:12 -07:00
12 changed files with 575 additions and 320 deletions

View File

@@ -30,8 +30,6 @@ main:
binaries:
ghostscript_binary: null
php_binary: null
convert_binary: null
composite_binary: null
swf_extract_binary: null
pdf2swf_binary: null
swf_render_binary: null

View File

@@ -254,8 +254,6 @@ class Install extends Command
return array(
'php_binary' => $this->executableFinder->find('php'),
'phraseanet_indexer' => $this->executableFinder->find('phraseanet_indexer'),
'convert_binary' => $this->executableFinder->find('convert'),
'composite_binary' => $this->executableFinder->find('composite'),
'pdf2swf_binary' => $this->executableFinder->find('pdf2swf'),
'swf_extract_binary' => $this->executableFinder->find('swfextract'),
'swf_render_binary' => $this->executableFinder->find('swfrender'),

View File

@@ -147,8 +147,6 @@ class Setup implements ControllerProviderInterface
foreach (array(
'php_binary' => $request->request->get('binary_php'),
'phraseanet_indexer' => $request->request->get('binary_phraseanet_indexer'),
'convert_binary' => $request->request->get('binary_convert'),
'composite_binary' => $request->request->get('binary_composite'),
'swf_extract_binary' => $request->request->get('binary_swfextract'),
'pdf2swf_binary' => $request->request->get('binary_pdf2swf'),
'swf_render_binary' => $request->request->get('binary_swfrender'),

View File

@@ -18,7 +18,7 @@ namespace Alchemy\Phrasea\Core;
*/
class Version
{
protected static $number = '3.8.0.a14';
protected static $number = '3.8.0.a15';
protected static $name = 'Carnosaurus';
public static function getNumber()

View File

@@ -20,14 +20,12 @@ class BinariesProbe extends BinariesRequirements implements ProbeInterface
{
parent::__construct(array_filter(array(
'php_binary' => isset($binaries['php_binary']) ? $binaries['php_binary'] : null,
'convert_binary' => isset($binaries['convert_binary']) ? $binaries['convert_binary'] : null,
'pdf2swf_binary' => isset($binaries['pdf2swf_binary']) ? $binaries['pdf2swf_binary'] : null,
'unoconv_binary' => isset($binaries['unoconv_binary']) ? $binaries['unoconv_binary'] : null,
'swf_extract_binary' => isset($binaries['swf_extract_binary']) ? $binaries['swf_extract_binary'] : null,
'swf_render_binary' => isset($binaries['swf_render_binary']) ? $binaries['swf_render_binary'] : null,
'mp4box_binary' => isset($binaries['mp4box_binary']) ? $binaries['mp4box_binary'] : null,
'pdftotext_binary' => isset($binaries['pdftotext_binary']) ? $binaries['pdftotext_binary'] : null,
'composite_binary' => isset($binaries['composite_binary']) ? $binaries['composite_binary'] : null,
'ffmpeg_binary' => isset($binaries['ffmpeg_binary']) ? $binaries['ffmpeg_binary'] : null,
'ffprobe_binary' => isset($binaries['ffprobe_binary']) ? $binaries['ffprobe_binary'] : null,
)));

View File

@@ -58,48 +58,6 @@ class BinariesRequirements extends RequirementCollection implements RequirementI
);
}
$convert = isset($binaries['convert_binary']) ? $binaries['convert_binary'] : $finder->find('convert');
$this->addRequirement(
null !== $convert && is_executable($convert),
'ImageMagick Convert is required',
'Please install ImageMagick'
);
if (null !== $convert) {
$output = null;
exec($convert . ' --version', $output);
$data = sscanf($output[0], 'Version: ImageMagick %d.%d.%d');
$version = sprintf('%d.%d.%d', $data[0], $data[1], $data[2]);
$this->addRequirement(
version_compare(static::IMAGICK_VERSION, $version, '<'),
sprintf('Convert version %s or higher is required (%s provided)', static::IMAGICK_VERSION, $version),
'Please update to a more recent version'
);
}
$composite = isset($binaries['composite_binary']) ? $binaries['composite_binary'] : $finder->find('composite');
$this->addRequirement(
null !== $composite && is_executable($composite),
'ImageMagick Composite is required',
'Please install ImageMagick'
);
if (null !== $composite) {
$output = null;
exec($composite . ' --version', $output);
$data = sscanf($output[0], 'Version: ImageMagick %d.%d.%d');
$version = sprintf('%d.%d.%d', $data[0], $data[1], $data[2]);
$this->addRequirement(
version_compare(static::IMAGICK_VERSION, $version, '<'),
sprintf('Composite version %s or higher is required (%s provided)', static::IMAGICK_VERSION, $version),
'Please update to a more recent version.'
);
}
$exiftool = __DIR__ . '/../../../../../vendor/phpexiftool/exiftool/exiftool' . (defined('PHP_WINDOWS_VERSION_BUILD') ? '.exe' : '');
$this->addRequirement(

View File

@@ -425,7 +425,6 @@ class API_V1_adapter extends API_V1_Abstract
'binary' => array(
'phpCli' => isset($binaries['php_binary']) ? $binaries['php_binary'] : null,
'phpIni' => $app['phraseanet.registry']->get('GV_PHP_INI'),
'imagick' => $app['phraseanet.registry']->get('convert_binary'),
'swfExtract' => isset($binaries['swf_extract_binary']) ? $binaries['swf_extract_binary'] : null,
'pdf2swf' => isset($binaries['pdf2swf_binary']) ? $binaries['pdf2swf_binary'] : null,
'swfRender' => isset($binaries['swf_render_binary']) ? $binaries['swf_render_binary'] : null,

View File

@@ -78,8 +78,6 @@ class patch_373 implements patchInterface
$mapping = array(
'GV_cli' => 'php_binary',
'GV_imagick' => 'convert_binary',
'GV_pathcomposite' => 'composite_binary',
'GV_swf_extract' => 'swf_extract_binary',
'GV_pdf2swf' => 'pdf2swf_binary',
'GV_swf_render' => 'swf_render_binary',

View File

@@ -0,0 +1,57 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2012 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Application;
class patch_3815 implements patchInterface
{
/** @var string */
private $release = '3.8.0.a15';
/** @var array */
private $concern = array(base::APPLICATION_BOX);
/**
* {@inheritdoc}
*/
public function get_release()
{
return $this->release;
}
/**
* {@inheritdoc}
*/
public function require_all_upgrades()
{
return false;
}
/**
* {@inheritdoc}
*/
public function concern()
{
return $this->concern;
}
/**
* {@inheritdoc}
*/
public function apply(base $appbox, Application $app)
{
$binaries = $app['phraseanet.configuration']['binaries'];
unset($binaries['composite_binary'], $binaries['convert_binary']);
$app['phraseanet.configuration']['binaries'] = $binaries;
return true;
}
}

View File

@@ -10,83 +10,63 @@
*/
use Alchemy\Phrasea\Application;
use Symfony\Component\Process\ProcessBuilder;
use Imagine\Image\ImagineInterface;
use Imagine\Image\Palette\RGB;
use Imagine\Image\Box;
use Imagine\Image\Point;
use Imagine\Exception\Exception as ImagineException;
use MediaVorus\Media\MediaInterface;
use MediaVorus\Media\Image;
/**
*
*
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com
*/
class recordutils_image extends recordutils
{
/**
*
* @param int $fontSize
* @param int $angle
* @param string $fontFace
* @param string $string
* @param int $width
* @return Array
*/
protected function wrap($fontSize, $angle, $fontFace, $string, $width)
{
$ret = array();
// str 'Op' used to calculate linespace
$testbox = imagettfbbox($fontSize, $angle, $fontFace, 'Op');
$height = abs($testbox[1] - ($dy = $testbox[7]));
foreach (explode("\n", $string) as $lig) {
if ($lig == '') {
$ret[] = '';
} else {
$buff = '';
foreach (explode(' ', $lig) as $wrd) {
$test = $buff . ($buff ? ' ' : '') . $wrd;
$testbox = imagettfbbox($fontSize, $angle, $fontFace, $test);
if (abs($testbox[2] - $testbox[0]) > $width) {
if ($buff == '') {
$ret[] = $test;
} else {
$ret[] = $buff;
$buff = $wrd;
}
} else {
$buff = $test;
}
}
if ($buff != '')
$ret[] = $buff;
}
}
return(array('l' => $ret, 'h' => $height, 'dy' => $dy));
}
/**
*
* @param Application $app
* @param \media_subdef $subdef
*
* @return string
* @return string The path to the stamped file
*/
public static function stamp(Application $app, \media_subdef $subdef)
{
static $palette;
if (null === $palette) {
$palette = new RGB();
}
$xmlToColor = function($attr, $ret = array(255, 255, 255, 0)) use ($palette) {
foreach (explode(',', $attr) as $i => $v) {
if ($i > 3) {
break;
}
$v = (int) (trim($v));
if ($v >= 0 && ($v <= 100 || ($i < 3 && $v < 256))) {
$ret[$i] = $v;
}
}
$alpha = array_pop($ret);
return $palette->color($ret, $alpha);
};
$base_id = $subdef->get_record()->get_base_id();
$binaries = $app['phraseanet.configuration']['binaries'];
if ($subdef->get_type() !== \media_subdef::TYPE_IMAGE) {
return $subdef->get_pathfile();
}
if ( ! $subdef->is_physically_present()) {
if (!$subdef->is_physically_present()) {
return $subdef->get_pathfile();
}
if ( ! isset($binaries['convert_binary'])) {
return $subdef->get_pathfile();
$rotation = null;
try {
$image = $app['mediavorus']->guess($subdef->get_pathfile());
if (MediaInterface::TYPE_IMAGE === $image->getType()) {
$rotation = $image->getOrientation();
}
} catch (Exception $e) {
// getting orientation failed but we don't care the reason
}
$domprefs = new DOMDocument();
@@ -100,205 +80,282 @@ class recordutils_image extends recordutils
}
$xpprefs = new DOMXPath($domprefs);
$pathIn = $subdef->get_path() . $subdef->get_file();
$pathOut = $subdef->get_path() . 'stamp_' . $subdef->get_file();
$pathTmpStamp = $app['root.path'] . '/tmp/' . time() . '-stamptmp_' . $subdef->get_file();
if ($xpprefs->query('/baseprefs/stamp')->length == 0) {
$stampNodes = $xpprefs->query('/baseprefs/stamp');
if ($stampNodes->length == 0) {
return $subdef->get_pathfile();
}
$pathIn = $subdef->get_path() . $subdef->get_file();
$pathOut = $subdef->get_path() . 'stamp_' . $subdef->get_file();
$vars = $xpprefs->query('/baseprefs/stamp/*/var');
for ($i = 0; $i < $vars->length; $i ++) {
$varval = '';
$n = $vars->item($i);
switch (strtoupper($n->getAttribute('name'))) {
case 'DATE':
if ( ! ($format = $n->getAttribute('format')))
$format = 'Y/m/d H:i:s';
$varval = date($format);
@unlink($pathOut);
break;
case 'RECORD_ID':
$varval = $subdef->get_record()->get_record_id();
break;
// no way to cache when date changes
for ($i = 0; $i < $vars->length; $i++) {
if (strtoupper($vars->item($i)->getAttribute('name')) == 'DATE') {
@unlink($pathOut);
break;
}
$n->parentNode->replaceChild($domprefs->createTextNode($varval), $n);
}
// get from cache ?
if (is_file($pathOut)) {
return $pathOut;
}
$fields = $xpprefs->query('/baseprefs/stamp/*/field');
for ($i = 0; $i < $fields->length; $i ++) {
$fldval = '';
$n = $fields->item($i);
$fieldname = $n->getAttribute('name');
$x = $sxxml->description->{$fieldname};
if (is_array($x)) {
foreach ($x as $v)
$fldval .= ( $fldval ? '; ' : '') . (string) $v;
} else {
$fldval .= ( $fldval ? '; ' : '') . (string) $x;
}
$n->parentNode->replaceChild($domprefs->createTextNode($fldval), $n);
// open the document
$image_in = $app['imagine']->open($pathIn);
$image_size = $image_in->getSize();
switch ($rotation) {
case Image::ORIENTATION_90:
$image_width = $image_size->getHeight();
$image_height = $image_size->getWidth();
$image_in->rotate(90);
$rotation = '90';
break;
case Image::ORIENTATION_270:
$image_width = $image_size->getHeight();
$image_height = $image_size->getWidth();
$image_in->rotate(270);
break;
case Image::ORIENTATION_180:
$image_width = $image_size->getWidth();
$image_height = $image_size->getHeight();
$image_in->rotate(180);
break;
default:
$image_width = $image_size->getWidth();
$image_height = $image_size->getHeight();
break;
}
$domprefs->normalizeDocument();
if ( ! ($tailleimg = @getimagesize($pathIn))) {
return false;
}
$image_width = $tailleimg[0];
$image_height = $tailleimg[1];
$text_xpos = 0;
$text_width = $image_width;
$logofile = $app['root.path'] . '/config/stamp/' . $base_id;
$logopos = null;
$imlogo = null; // gd image
// open the logo
$logo_phywidth = $logo_phyheight = 0; // physical size
$logo_reswidth = $logo_resheight = 0; // resized size
$logo_file = $app['root.path'] . '/config/stamp/' . $base_id;
try {
$logo_obj = $app['imagine']->open($logo_file);
$logo_size = $logo_obj->getSize();
$logo_phywidth = $logo_size->getWidth();
$logo_phyheight = $logo_size->getHeight();
} catch (ImagineException $e) {
if (is_array($logosize = @getimagesize($logofile))) {
$logo_reswidth = $logo_phywidth = $logosize[0];
$logo_resheight = $logo_phyheight = $logosize[1];
}
$v = $xpprefs->query('/baseprefs/stamp/logo');
if ($v->length > 0) {
$logopos = @strtoupper($v->item(0)->getAttribute('position'));
if (($logowidth = trim($v->item(0)->getAttribute('width'))) != '') {
if (substr($logowidth, -1) == '%')
$logo_reswidth = (int) ($logowidth * $image_width / 100);
else
$logo_reswidth = (int) $logowidth;
$logo_resheight = (int) ($logo_phyheight *
($logo_reswidth / $logo_phywidth));
$tables = array(
'TOP' => array('h' => 0, 'rows' => array()),
'TOP-OVER' => array('h' => 0, 'rows' => array()),
'BOTTOM' => array('h' => 0, 'rows' => array()),
'BOTTOM-OVER' => array('h' => 0, 'rows' => array())
);
for ($istamp = 0; $istamp < $stampNodes->length; $istamp++) {
$stamp = $stampNodes->item($istamp);
$stamp_background = $xmlToColor($stamp->getAttribute('background'), array(255, 255, 255, 0));
$stamp_position = strtoupper(trim($stamp->getAttribute('position')));
if (!in_array($stamp_position, array('TOP', 'TOP-OVER', 'BOTTOM-OVER', 'BOTTOM'))) {
$stamp_position = 'BOTTOM';
}
// replace "var" nodes with their value
$vars = $xpprefs->query('*/var', $stamp);
for ($i = 0; $i < $vars->length; $i++) {
$varval = '';
$n = $vars->item($i);
switch (strtoupper($n->getAttribute('name'))) {
case 'DATE':
if (!($format = $n->getAttribute('format'))) {
$format = 'Y/m/d H:i:s';
}
$varval = date($format);
@unlink($pathOut); // no cache possible when date changes
break;
case 'RECORD_ID':
$varval = $subdef->get_record()->get_record_id();
break;
}
$n->parentNode->replaceChild($domprefs->createTextNode($varval), $n);
}
if (($logopos == 'LEFT' || $logopos == 'RIGHT') &&
$logo_phywidth > 0 && $logo_phyheight > 0) {
switch ($logosize['mime']) {
case 'image/gif':
$imlogo = @imagecreatefromgif($logofile);
break;
case 'image/png':
$imlogo = @imagecreatefrompng($logofile);
break;
case 'image/jpeg':
case 'image/pjpeg':
$imlogo = @imagecreatefromjpeg($logofile);
break;
// replace "field" nodes with their values
$fields = $xpprefs->query('*/field', $stamp);
for ($i = 0; $i < $fields->length; $i++) {
$fldval = '';
$n = $fields->item($i);
$fieldname = $n->getAttribute('name');
$x = $sxxml->description->{$fieldname};
if (is_array($x)) {
foreach ($x as $v) {
$fldval .= ( $fldval ? '; ' : '') . (string) $v;
}
if ($imlogo) {
} else {
$fldval .= ( $fldval ? '; ' : '') . (string) $x;
}
$n->parentNode->replaceChild($domprefs->createTextNode($fldval), $n);
}
$domprefs->normalizeDocument();
$text_xpos = 0;
$text_width = $image_width;
$logopos = null;
// compute logo position / size
$logo_reswidth = 0;
$logo_resheight = 0;
if ($logo_phywidth > 0 && $logo_phyheight > 0) {
$v = $xpprefs->query('logo', $stamp);
if ($v->length > 0) {
$logo_reswidth = $logo_phywidth;
$logo_resheight = $logo_phyheight;
$logopos = @strtoupper($v->item(0)->getAttribute('position'));
if (($logowidth = trim($v->item(0)->getAttribute('width'))) != '') {
if (substr($logowidth, -1) == '%') {
$logo_reswidth = (int) ($logowidth * $image_width / 100);
} else {
$logo_reswidth = (int) $logowidth;
}
$logo_resheight = (int) ($logo_phyheight * ($logo_reswidth / $logo_phywidth));
}
if ($logopos == 'LEFT' || $logopos == 'RIGHT') {
if ($logo_reswidth > $image_width / 2) {
// logo too large, resize please
$logo_reswidth = (int) ($image_width / 2);
$logo_resheight = (int) ($logo_phyheight *
($logo_reswidth / $logo_phywidth));
$logo_resheight = (int) ($logo_phyheight * ($logo_reswidth / $logo_phywidth));
}
$text_width -= $logo_reswidth;
if ($logopos == 'LEFT') {
$logo_xpos = 0;
$text_xpos = $logo_reswidth;
} else {
$text_xpos = 0;
$logo_xpos = ($image_width - $logo_reswidth);
}
$text_width -= ( $text_xpos = $logo_reswidth);
}
}
}
}
$txth = 0;
$txtblock = array();
$texts = $xpprefs->query('/baseprefs/stamp/text');
$fontsize = "100%";
for ($i = 0; $i < $texts->length; $i ++) {
if (($tmpfontsize = trim($texts->item($i)->getAttribute('size'))) != '') {
if (substr($tmpfontsize, -1) == '%')
$tmpfontsize = (int) ($tmpfontsize * $image_width / 4000);
else
$tmpfontsize = (int) $tmpfontsize;
$fontsize = $tmpfontsize;
// compute text blocks
$txth = 0;
$txtblock = array();
$texts = $xpprefs->query('text', $stamp);
$fontsize = "100%";
for ($i = 0; $i < $texts->length; $i++) {
if (($tmpfontsize = trim($texts->item($i)->getAttribute('size'))) != '') {
if (substr($tmpfontsize, -1) == '%') {
$tmpfontsize = (int) ($tmpfontsize * $image_width / 4000);
} else {
$tmpfontsize = (int) $tmpfontsize;
}
$fontsize = $tmpfontsize;
}
if ($fontsize < 2) {
$fontsize = 2;
} elseif ($fontsize > 300) {
$fontsize = 300;
}
$txtline = $texts->item($i)->nodeValue;
if ($txtline != '') {
$wrap = static::wrap($app['imagine'], $fontsize, 0, __DIR__ . '/arial.ttf', $txtline, $text_width);
$txtblock[] = array(
'fontsize' => $fontsize,
'fontcolor' => $xmlToColor($texts->item($i)->getAttribute('color'), array(0, 0, 0, 0)),
'h' => $wrap['toth'],
'lines' => $wrap['l']
);
$txth += $wrap['toth'];
}
}
if ($fontsize < 2)
$fontsize = 2;
elseif ($fontsize > 300)
$fontsize = 300;
$stampheight = max($logo_resheight, $txth);
$txtline = $texts->item($i)->nodeValue;
if ($stamp_position == 'TOP-OVER' || $stamp_position == 'BOTTOM-OVER') {
if ($tables[$stamp_position]['h'] + $stampheight > $image_height) {
$stampheight = $image_height - $tables[$stamp_position]['h'];
}
}
if ($stampheight <= 0) {
continue;
}
if ($txtline != '') {
$txtlines = recordutils_image::wrap(
$fontsize, 0, __DIR__ . '/arial.ttf', $txtline, $text_width
);
// create the block
$imfg = $app['imagine']->create(new Box($image_width, $stampheight), $stamp_background);
foreach ($txtlines['l'] as $txtline) {
$txtblock[] = array(
'x' => $text_xpos,
'dy' => $txtlines['dy'],
'w' => $text_width,
'h' => $txtlines['h'],
't' => $txtline,
's' => $fontsize
// copy the logo
if ($logo_reswidth > 0 && $logo_resheight > 0) {
if ($logo_reswidth != $logo_phywidth) {
$imfg->paste(
$logo_obj->copy()->resize(new Box($logo_reswidth, $logo_resheight)),
new Point($logo_xpos, 0)
);
$txth += $txtlines['h'];
} else {
$imfg->paste($logo_obj, new Point($logo_xpos, 0));
}
}
// fill with text
$draw = $imfg->draw();
$txt_ypos = 0;
foreach ($txtblock as $block) {
$font = $app['imagine']->font(__DIR__ . '/arial.ttf', $block['fontsize'], $block['fontcolor']);
foreach ($block['lines'] as $line) {
if ($line['t'] != '') {
$draw->text($line['t'], $font, new Point(0, $txt_ypos), 0);
}
$txt_ypos += $line['h'];
}
}
// memo into one of the 4 buffer
$tables[$stamp_position]['rows'][] = array(
'x0' => 0,
'y0' => $tables[$stamp_position]['h'],
'w' => $image_width,
'h' => $stampheight,
'img' => $imfg
);
$tables[$stamp_position]['h'] += $stampheight;
}
$newh = $tables['TOP']['h'] + $image_height + $tables['BOTTOM']['h'];
// create the output image
$image_out = $app['imagine']->create(new Box($image_width, $newh), $palette->color("FFFFFF", 64));
// paste the input image into
$image_out->paste($image_in, new Point(0, $tables['TOP']['h']));
// fix the coordinates
foreach ($tables['TOP-OVER']['rows'] as $k => $row) {
$tables['TOP-OVER']['rows'][$k]['y0'] += $tables['TOP']['h'];
}
foreach ($tables['BOTTOM-OVER']['rows'] as $k => $row) {
$tables['BOTTOM-OVER']['rows'][$k]['y0'] += $tables['TOP']['h'] + $image_height - $tables['BOTTOM-OVER']['h'];
}
foreach ($tables['BOTTOM']['rows'] as $k => $row) {
$tables['BOTTOM']['rows'][$k]['y0'] += $tables['TOP']['h'] + $image_height;
}
// paste blocks
foreach (array('TOP', 'TOP-OVER', 'BOTTOM-OVER', 'BOTTOM') as $ta) {
foreach ($tables[$ta]['rows'] as $row) {
if ($row['h'] > 0) {
$image_out->paste($row['img'], new Point($row['x0'], $row['y0']));
}
}
}
$stampheight = max($logo_resheight, $txth);
$im = imagecreatetruecolor($image_width, $stampheight);
$white = imagecolorallocate($im, 255, 255, 255);
imagefilledrectangle($im, 0, 0, $image_width, $stampheight, $white);
imagecolordeallocate($im, $white);
if ($imlogo) {
if ($logo_reswidth != $logo_phywidth) {
imagecopyresampled($im, $imlogo, 0, 0, // dst_x, dst_y
0, 0, // src_x, src_y
$logo_reswidth, // dst_w
$logo_resheight, // dst_h
$logo_phywidth, // src_w
$logo_phyheight // src_h
);
} else {
imagecopy($im, $imlogo, 0, 0, // dst_x, dst_y
0, 0, // src_x, src_y
$logo_phywidth, // src_w
$logo_phyheight // src_h
);
}
}
if (count($txtblock) >= 0) {
$black = imagecolorallocate($im, 0, 0, 0);
$txt_ypos = 0; //$txtblock[0]['h'];
foreach ($txtblock as $block) {
imagettftext($im, $block['s'], 0, $block['x'], $txt_ypos - $block['dy'], $black, __DIR__ . '/arial.ttf', $block['t']);
$txt_ypos += $block['h'];
}
imagecolordeallocate($im, $black);
}
imagejpeg($im, $pathTmpStamp, 80);
imagedestroy($im);
$newh = $image_height + $stampheight;
$builder = ProcessBuilder::create(array($binaries['convert_binary']));
$builder->add('-extent')
->add($image_width . 'x' . $newh)
->add('-draw')
->add('image SrcOver 0,' . $image_height . ' ' . $image_width . ',' . $stampheight . '"' . $pathTmpStamp . '"')
->add($pathIn)
->add($pathOut);
$builder->getProcess()->run();
unlink($pathTmpStamp);
// save the output
$image_out->save($pathOut);
if (is_file($pathOut)) {
return $pathOut;
@@ -316,8 +373,13 @@ class recordutils_image extends recordutils
*/
public static function watermark(Application $app, \media_subdef $subdef)
{
static $palette;
if (null === $palette) {
$palette = new RGB();
}
$base_id = $subdef->get_record()->get_base_id();
$binaries = $app['phraseanet.configuration']['binaries'];
if ($subdef->get_name() !== 'preview') {
return $subdef->get_pathfile();
@@ -327,72 +389,155 @@ class recordutils_image extends recordutils
return $subdef->get_pathfile();
}
if ( ! $subdef->is_physically_present()) {
return $subdef->get_pathfile();
if (!$subdef->is_physically_present()) {
return false;
}
$pathIn = $subdef->get_path() . $subdef->get_file();
$pathOut = $subdef->get_path() . 'watermark_' . $subdef->get_file();
if (!is_file($pathIn)) {
return false;
}
$pathOut = $subdef->get_path() . 'watermark_' . $subdef->get_file();
// cache
if (is_file($pathOut)) {
return $pathOut;
}
if (isset($binaries['composite_binary']) &&
file_exists($app['root.path'] . '/config/wm/' . $base_id)) {
$in_image = $app['imagine']->open($pathIn);
$in_size = $in_image->getSize();
$in_w = $in_size->getWidth();
$in_h = $in_size->getHeight();
$builder = ProcessBuilder::create(array(
$binaries['composite_binary'],
$app['root.path'] . '/config/wm/' . $base_id,
$pathIn,
'-strip', '-watermark', '90%', '-gravity', 'center',
$pathOut
));
$wm_file = $app['root.path'] . '/config/wm/' . $base_id;
if (file_exists($wm_file)) {
$wm_image = $app['imagine']->open($wm_file);
$wm_size = $wm_image->getSize();
$wm_w = $wm_size->getWidth();
$wm_h = $wm_size->getHeight();
$builder->getProcess()->run();
} elseif (isset($binaries['convert_binary'])) {
$collname = phrasea::bas_labels($base_id, $app);
$tailleimg = @getimagesize($pathIn);
$max = ($tailleimg[0] > $tailleimg[1] ? $tailleimg[0] : $tailleimg[1]);
if (($wm_w / $wm_h) > ($in_w / $in_h)) {
$wm_size = $wm_size->widen($in_w);
} else {
$wm_size = $wm_size->heighten($in_h);
}
$wm_image->resize($wm_size);
$tailleText = (int) ($max / 30);
$in_image->paste($wm_image, new Point(($in_w - $wm_size->getWidth()) >> 1, ($in_h - $wm_size->getHeight()) >> 1))->save($pathOut);
} else {
$collname = $subdef->get_record()->get_collection()->get_name();
$draw = $in_image->draw();
$black = $palette->color("000000");
$white = $palette->color("FFFFFF");
$draw->line(new Point(0, 1), new Point($in_w - 2, $in_h - 1), $black);
$draw->line(new Point(1, 0), new Point($in_w - 1, $in_h - 2), $white);
$draw->line(new Point(0, $in_h - 2), new Point($in_w - 2, 0), $black);
$draw->line(new Point(1, $in_h - 1), new Point($in_w - 1, 1), $white);
if ($tailleText < 8)
$tailleText = 8;
$fsize = max(8, (int) (max($in_w, $in_h) / 30));
$fonts = array(
$app['imagine']->font(__DIR__ . '/arial.ttf', $fsize, $black),
$app['imagine']->font(__DIR__ . '/arial.ttf', $fsize, $white)
);
$testbox = $fonts[0]->box($collname, 0);
$tx_w = min($in_w, $testbox->getWidth());
$tx_h = min($in_h, $testbox->getHeight());
if ($tailleText > 12)
$decalage = 2;
else
$decalage = 1;
$x0 = max(1, ($in_w - $tx_w) >> 1);
$y0 = max(1, ($in_h - $tx_h) >> 1);
for ($i = 0; $i <= 1; $i++) {
$x = max(1, ($in_w >> 2) - ($tx_w >> 1));
$draw->text($collname, $fonts[$i], new Point($x - $i, $y0 - $i), 0);
$x = max(1, $in_w - $x - $tx_w);
$draw->text($collname, $fonts[$i], new Point($x - $i, $y0 - $i), 0);
$builder = ProcessBuilder::create(array(
$binaries['convert_binary'],
'-fill', 'white', '-draw', 'line 0,0 ' . $tailleimg[0] . ',' . $tailleimg[1] . '',
'-fill', 'black', '-draw', 'line 1,0 ' . $tailleimg[0] + 1 . ',' . $tailleimg[1] . '',
'-fill', 'white', '-draw', 'line ' . $tailleimg[0] . ',0 0,' . $tailleimg[1] . '',
'-fill', 'black', '-draw', 'line ' . ($tailleimg[0] + 1) . ',0 0,' . $tailleimg[1] . '',
'-fill', 'white', '-gravity', 'NorthWest', '-pointsize', $tailleText, '-draw', 'text 0,0 ' . $collname,
'-fill', 'black', '-gravity', 'NorthWest', '-pointsize', $tailleText, '-draw', 'text ' . $decalage . ', 1 ' . $collname,
'-fill', 'white', '-gravity', 'center', '-pointsize', $tailleText, '-draw', 'text 0,0 ' . $collname,
'-fill', 'black', '-gravity', 'center', '-pointsize', $tailleText, '-draw', 'text ' . $decalage . ', 1 ' . $collname,
'-fill', 'white', '-gravity', 'SouthEast', '-pointsize', $tailleText, '-draw', 'text 0,0 ' . $collname,
'-fill', 'black', '-gravity', 'SouthEast', '-pointsize', $tailleText, '-draw', 'text ' . $decalage . ', 1 ' . $collname,
$pathIn, $pathOut
));
$process = $builder->getProcess();
$process->run();
$y = max(1, ($in_h >> 2) - ($tx_h >> 1));
$draw->text($collname, $fonts[$i], new Point($x0 - $i, $y - $i), 0);
$y = max(1, $in_h - $y - $tx_h);
$draw->text($collname, $fonts[$i], new Point($x0 - $i, $y - $i), 0);
}
}
$in_image->save($pathOut);
if (is_file($pathOut)) {
return $pathOut;
}
return false;
}
/**
*
* @param int $fontSize
* @param int $angle
* @param string $fontFace
* @param string $string
* @param int $width
* @return array
*/
protected static function wrap(ImagineInterface $imagine, $fontSize, $angle, $fontFace, $string, $width)
{
static $palette;
if (null === $palette) {
$palette = new RGB();
}
// str 'Op' used to calculate linespace
$font = $imagine->font($fontFace, $fontSize, $palette->color("000000", 0));
$testbox = $font->box("0p", $angle);
$height = $testbox->getHeight();
$testbox = $font->box("M", $angle); // 1 em
$dy = $testbox->getHeight();
$toth = 0;
$ret = array();
foreach (explode("\n", $string) as $lig) {
if ($lig == '') {
$ret[] = array('w' => 0, 'h' => $dy, 't' => '');
$toth += $dy;
} else {
$twords = array();
$iword = -1;
$lastc = '';
for ($i = 0; $i < strlen($lig); $i++) {
$c = $lig[$i];
if ($iword == -1 || (ctype_space($c) && !ctype_space($lastc))) {
$twords[++$iword] = array(($part = 0) => '', 1 => '');
}
if (!ctype_space($c) && $part == 0) {
$part++;
}
$twords[$iword][$part] .= $lastc = $c;
}
if ($iword >= 0 && $twords[0][1] != '') {
$buff = '';
$lastw = $lasth = 0;
foreach ($twords as $i => $wrd) {
$test = $buff . $wrd[0] . $wrd[1];
$testbox = $font->box($test, $angle);
$w = $testbox->getWidth();
$h = $testbox->getHeight();
if ($i > 0 && $testbox->getWidth() > $width) {
$ret[] = array('w' => $lastw, 'h' => $lasth, 't' => $buff);
$toth += $lasth;
$buff = $wrd[1];
} else {
$buff = $test;
}
$lastw = $w;
$lasth = $h;
}
if ($buff != '') {
$ret[] = array('w' => $lastw, 'h' => $lasth, 't' => $buff);
$toth += $lasth;
}
}
}
}
return array('toth' => $toth, 'l' => $ret, 'h' => $height, 'dy' => $dy);
}
}

View File

@@ -33,8 +33,6 @@ debugger:
binaries:
ghostscript_binary: null
php_binary: null
convert_binary: null
composite_binary: null
swf_extract_binary: null
pdf2swf_binary: null
swf_render_binary: null

View File

@@ -0,0 +1,108 @@
<?php
use Symfony\Component\HttpFoundation\File\File as SymfoFile;
/**
* @covers recordutils_image
*/
class recordutils_imageTest extends PhraseanetPHPUnitAbstract
{
public function testWatermarkWithoutFile()
{
self::$DI['app']['phraseanet.appbox']->write_collection_pic(
self::$DI['app']['media-alchemyst'],
self::$DI['app']['filesystem'],
self::$DI['record_1']->get_collection(),
null,
\collection::PIC_WM
);
$path = recordutils_image::watermark(self::$DI['app'], self::$DI['record_1']->get_subdef('preview'));
$this->assertTrue(0 === strpos(basename($path), 'watermark_'));
unlink($path);
}
public function testWatermarkWithFile()
{
self::$DI['app']['phraseanet.appbox']->write_collection_pic(
self::$DI['app']['media-alchemyst'],
self::$DI['app']['filesystem'],
self::$DI['record_1']->get_collection(),
new SymfoFile(__DIR__ . '/../../files/logocoll.gif'),
\collection::PIC_WM
);
$path = recordutils_image::watermark(self::$DI['app'], self::$DI['record_1']->get_subdef('preview'));
$this->assertTrue(0 === strpos(basename($path), 'watermark_'));
unlink($path);
}
private function addStampConf(\collection $coll)
{
$domprefs = new DOMDocument();
$domprefs->loadXML($coll->get_prefs());
$prefs = '<?xml version="1.0" encoding="UTF-8"?>
<baseprefs>
<status>0</status>
<stamp>
<logo position="left" width="25%"/>
<text size="50%">Date: <var name="date"/></text>
<text size="50%">Record_id: <var name="record_id"/></text>';
foreach ($coll->get_databox()->get_meta_structure() as $databox_field) {
$name = $databox_field->get_name();
$prefs .= '<text size="50%">'.$name.': <field name="'.$name.'"/></text>' . "\n";
}
$prefs .= '</stamp>
<caninscript>1</caninscript>
<sugestedValues>
</sugestedValues>
</baseprefs>';
$newdom = new DOMDocument();
$newdom->loadXML($prefs);
$coll->set_prefs($newdom);
}
public function testStampWithoutFile()
{
$this->addStampConf(self::$DI['record_1']->get_collection());
self::$DI['app']['phraseanet.appbox']->write_collection_pic(
self::$DI['app']['media-alchemyst'],
self::$DI['app']['filesystem'],
self::$DI['record_1']->get_collection(),
null,
\collection::PIC_STAMP
);
$path = recordutils_image::stamp(self::$DI['app'], self::$DI['record_1']->get_subdef('preview'));
$this->assertTrue(0 === strpos(basename($path), 'stamp_'));
unlink($path);
}
public function testStampWithFile()
{
$this->addStampConf(self::$DI['record_1']->get_collection());
self::$DI['app']['phraseanet.appbox']->write_collection_pic(
self::$DI['app']['media-alchemyst'],
self::$DI['app']['filesystem'],
self::$DI['record_1']->get_collection(),
new SymfoFile(__DIR__ . '/../../files/logocoll.gif'),
\collection::PIC_STAMP
);
$path = recordutils_image::stamp(self::$DI['app'], self::$DI['record_1']->get_subdef('preview'));
$this->assertTrue(0 === strpos(basename($path), 'stamp_'));
unlink($path);
}
}