News:

Wondering if this will always be free?  See why free is better.

Main Menu

Watermark hotlinked image

Started by dschwab9, October 28, 2003, 04:05:57 AM

Previous topic - Next topic

dschwab9

I currently have an htaccess file that checks the referer and replaces an image with my logo if it's hotlinked.  What I would like to do is display the original image, with my logo overlayed onto it.  I've seen some other sites that do it, but I can't find instructions to do it.

I've got part of it figured out.  I guess I would need to have my htaccess file rewrite something like this:
http://mysite.com/file.jpg
becomes:
http://mysite.com/watermark.php?file=file.jpg

watermark.php would take file.jpg and overlay logo.png onto it using either Image Magick or GD.

Is this practical?  Does anyone have a script that does this, or can point me in the right direction?  I know very little about using Image Magick or GD.




[Unknown]

Yes, it's practical.... although I would use GD not Image Magick.

I also don't know how to do it, it's mainly .htaccess... the GD stuff isn't hard.

http://php.net/function.imagejpeg

-[Unknown]

Aquilo

#2
you could pass the image they are hot linking with your logo like this

RewriteEngine on
RewriteCond %{HTTP_REFERER}   !^http://www\.yoursite\.com/$   [NC]
RewriteRule ^(.*)\.(gif|jpg|png)$ image_script.php?image=$1\.$2   [R]


that should work I can't test it at this time.

and the GD script could be name: image_script.php
<?php
switch (TRUE) {
   case stristr($image,'jpg') :
      $photoImage ImageCreateFromJPG("/$image");
      break;
   case stristr($image,'gif') :
      $photoImage ImageCreateFromGIF("/$image");
      break;
   case stristr($image,'png') :
      $photoImage ImageCreateFromPNG("/$image");
      break;
}

ImageAlphaBlending($photoImagetrue); 

// if your logo is not a png use top examples to change it's format in create from line
$logoImage ImageCreateFromPNG("/your_logo.png"); 
   $logoW ImageSX($logoImage); 
   $logoH ImageSY($logoImage); 

// were you see the two 1's this is the offset from the top-left hand corner!
ImageCopy($photoImage$logoImage1100$logoW$logoH); 

ImagePNG($photoImage); // output to browser 

ImageDestroy($photoImage); 
ImageDestroy($logoImage); 
?>

[edit] I guess the <?php thing is broke! [/edit]

anyway this should work again I can't test it!

Aquilo

hey it works inside of code tags now that's cool!

[Unknown]

Don't you have to send the Conent-Type: image/png header or does imagepng do that?

-[Unknown]

Aquilo

you should send the headers, That was just something fast.

I use this for pritty much all my GD headers
Header ( "Expires: Mon, 26 Jul 1997 05:00:00 GMT");
Header ( "Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
Header ( "Cache-Control: no-store, no-cache, must-revalidate");
Header ( "Cache-Control: post-check=0, pre-check=0", false);
Header ( "Pragma: no-cache");
Header ( "Content-type: image/png");
Header ( "Content-Disposition: inline; filename=some_name.png");

dschwab9

#6
Thanks for the help.  With a bit of tweaking, I think I've almost got the php part working, with the exeption of the alphablending part.

I get:
Warning: imagealphablending(): supplied argument is not a valid Image resource in /home/sites/site17/web/images/watermark.php on line 14

I've looked at the php manual, and, as far as I can tell, the function is being used correctly.

This is the code I ended up with:

<?php
switch (TRUE) {
case 
stristr($image,'jpeg') :
$photoImage ImageCreateFromJPEG("/$image");
break;
case 
stristr($image,'gif') :
$photoImage ImageCreateFromGIF("/$image");
break;
case 
stristr($image,'png') :
$photoImage ImageCreateFromPNG("/$image");
break;
}

ImageAlphaBlending($photoImagetrue); 

// if your logo is not a png use top examples to change it's format in create from line
$logoImage ImageCreateFromPNG("bbslogo.png"); 
$logoW ImageSX($logoImage); 
$logoH ImageSY($logoImage); 

// were you see the two 1's this is the offset from the top-left hand corner!
ImageCopy($photoImage$logoImage1100$logoW$logoH); 

ImagePNG($photoImage); // output to browser 

ImageDestroy($photoImage); 
ImageDestroy($logoImage); 
?>


Seems that it doesn't know what type of image it is or something.  Maybe the headers need to be sent before the alphablending function? (can you do that?)

Aquilo

the ImageAlphaBlending() function should be called after the base image and befor the logo but the headers can go anywhere befor output ImagePNG()

try this to test if it's creating the base image!
switch (TRUE) {
case stristr($image,'jpeg') :
$photoImage = ImageCreateFromJPEG("/$image") or die('Image creation failed');
break;
case stristr($image,'gif') :
$photoImage = ImageCreateFromGIF("/$image") or die('Image creation failed');
break;
case stristr($image,'png') :
$photoImage = ImageCreateFromPNG("/$image") or die('Image creation failed');
break;
}


if this turns out to fail test if the .htaccess file is sending the file name right I couldn't test it.

dschwab9

I haven't gotten to the .htaccess part yet, I'm just typing http://www.zuwharrie.com/images/watermark.php?image=/images/bannerimage.jpg into my browser.

I commented out all the "case stristr" stuff and just left the createfromjpeg part, and it works.

So, I guess something's wrong with that part and it is not detecting the image type correctly.  Any ideas on that?



<?php
$path 
"/home/sites/site17/web";
//switch (TRUE) {
//case stristr($image,'jpeg') :
$photoImage ImageCreateFromJPEG("$path/$image") or die('Image creation failed');
//break;
//case stristr($image,'gif') :
//$photoImage = ImageCreateFromGIF("$path/$image") or die('Image creation failed');
//break;
//case stristr($image,'png') :
//$photoImage = ImageCreateFromPNG("$path/$image") or die('Image creation failed');
//break;
//}

ImageAlphaBlending($photoImagetrue); 

// if your logo is not a png use top examples to change it's format in create from line
$logoImage ImageCreateFromPNG("bbslogo.png"); 
$logoW ImageSX($logoImage); 
$logoH ImageSY($logoImage); 

// were you see the two 1's this is the offset from the top-left hand corner!
ImageCopy($photoImage$logoImage1100$logoW$logoH); 

Header "Expires: Mon, 26 Jul 1997 05:00:00 GMT");
Header "Last-Modified: " gmdate("D, d M Y H:i:s") . " GMT");
Header "Cache-Control: no-store, no-cache, must-revalidate");
Header "Cache-Control: post-check=0, pre-check=0"false);
Header "Pragma: no-cache");
Header "Content-type: image/png");
Header "Content-Disposition: inline; filename=image.png");

ImagePNG($photoImage); // output to browser 

ImageDestroy($photoImage); 
ImageDestroy($logoImage); 
?>

dschwab9

#9
Ok, did this:

$path = "/home/sites/site17/web";
if (stristr($image,'jpg'))
$photoImage = ImageCreateFromJPEG("$path/$image") or die('Image creation failed');
elseif (stristr($image,'gif'))
$photoImage = ImageCreateFromGIF("$path/$image") or die('Image creation failed');
elseif (stristr($image,'png'))
$photoImage = ImageCreateFromPNG("$path/$image") or die('Image creation failed');


And it works.

Is what I did OK?

Now I gotta get the htaccess going.

Jack.R.Abbit™

#10
what you did is fine...  But I see the problem with the previously posted switch statement.  The switch is using TRUE and then it tries each case until it finds one that matches TRUE.  But stristr does not return TRUE... it returns a string.  My thoughts on this are that they don't match.  That switch could be rewritten a few different ways but one that closely resembles that one is:<?php
switch (TRUE) {
  case 
strpos($image,'.jpeg') === TRUE :
    
$photoImage ImageCreateFromJPEG("/$image");
    break;
  case 
strpos($image,'.gif') === TRUE :
    
$photoImage ImageCreateFromGIF("/$image");
    break;
  case 
strpos($image,'.png') === TRUE :
    
$photoImage ImageCreateFromPNG("/$image");
    break;
}
?>
I think that would work.

Of course I could be totally wrong about the first switch statement... but it just don't look right to me.

dschwab9

#11
Is there any advantage to using "switch" over "if" and "elseif".  Does one parse faster than the other or something?

I think I got everything working now, but I'll change that if it's better to use Switch.

I also had to change the output to ImageJPEG, ImagePNG was taking a 30K jpg and changing it into a 300K png.

This image should show up watermarked if it's working right:


Jack.R.Abbit™

Some may disagree, but because of the way each one evaluates the expression, I believe a properly written switch is more efficient than a series of if/elses.  However, I don't think this particular switch is written to take advantage of what a switch offers.  Rather than each case evaluating the file name to see if it contains a certain extension and then comparing it to TRUE, it would be better to extract the file extension in the switch() part and then compare it to strings in the case part.  Then it would only be evaluating the filename once.

On the surface it does not seem to be that big of a deal since its such a small script but if you are planing on getting a fair amount of hotlinking, you are going to want to squeeze every milisecond out that you can.

Aquilo

Jack.R.Abbit your right about that is not written properly! sorry about that normally I test things I post but can't test this stuff on IIS without GD and a filter to do the .htaccess part, I guess to get it right you would need to capture everything past the dot or duh extension "I just got up".

but as Jack.R.Addit pointed out this would bring the switch to life in a much more appropriate manner
<?php
switch (stristr ($image'.')) {
   case 
'.jpeg' :
      
$photoImage ImageCreateFromJPEG("/$image");
   break;
   case 
'.gif' :
      
$photoImage ImageCreateFromGIF("/$image");
   break;
   case 
'.png' :
      
$photoImage ImageCreateFromPNG("/$image");
   break;
}
?>


the switch was there just so it would be flexible for more then one extension if you only have Jpeg's on your site you really don't have to worry about it the switch.

I also can't believe I messed up JPEG with JPG in my first post :-\
since you've already switched the script to output jpeg's you probably already know this too but just in case you can adjust the quality if you want a lesser one for the folks hot linking, 60 is the boundary between high and medium quality
ImageJPEG ($photoImage, '', 100);

Jack.R.Abbit™

#14
Not to keep harping on it but there is still a tiny flaw in the logic and I think this is a really cool script idea so I want to help make it bulletproof.  To quote php.net, the function stristr ( string haystack, string needle) "Returns all of haystack from the first occurrence of needle to the end." So if I were trying to water mark images on my server, I would have problems.  I make a habit of naming my images things like "header.main.fill.gif" (I hate underscores... periods are much more "eye-friendly")  The above code would try to compare ".main.fill.gif"

To make this be more friendly to my way of naming images, I would use strrchr ( string haystack, string needle) "Returns the portion of haystack which starts at the last occurrence of needle and goes until the end of haystack."  That would always pick up the last part, assumed to be the actual file extension.

Also, you probably want to look for both "jpeg" and "jpg".

Also to be the most flexible, you may want to account for any images that have their extension in uppercase.  I know that is not that common, but I know my digital camera makes the images all uppercase.  Sometimes when uploading them to the gallery, I don't take the time to change case.  (I'm lazy)

Would look like this:<?php
switch (strrchar ($image'.')) {
   case '.jpeg' :
   case '.JPEG' :
   case '.jpg' :
   case '.JPG' :
      $photoImage ImageCreateFromJPEG("/$image");
      break;
   case '.gif' :
   case '.GIF' :
      $photoImage ImageCreateFromGIF("/$image");
      break;
   case '.png' :
   case '.PNG' :
      $photoImage ImageCreateFromPNG("/$image");
      break;
}
?>
Overkill? maybe.   Bulletproof? maybe

Aquilo

switch (strtolower (strrchar ($image, '.')))

this is a really cool idea and much better then replacing it with porn or hateful messages that's why I help too! ;D
also that's for the help and suggestions it helps me learn better too!

[Unknown]

Quote from: Jack.R.Abbit on October 29, 2003, 10:51:57 AM
Not to keep harping on it but there is still a tiny flaw in the logic and I think this is a really cool script idea so I want to help make it bulletproof.  To quote php.net, the function stristr ( string haystack, string needle) "Returns all of haystack from the first occurrence of needle to the end." So if I were trying to water mark images on my server, I would have problems.  I make a habit of naming my images things like "header.main.fill.gif" (I hate underscores... periods are much more "eye-friendly")  The above code would try to compare ".main.fill.gif"

To make this be more friendly to my way of naming images, I would use strrchr ( string haystack, string needle) "Returns the portion of haystack which starts at the last occurrence of needle and goes until the end of haystack."  That would always pick up the last part, assumed to be the actual file extension.

Also, you probably want to look for both "jpeg" and "jpg".

Also to be the most flexible, you may want to account for any images that have their extension in uppercase.  I know that is not that common, but I know my digital camera makes the images all uppercase.  Sometimes when uploading them to the gallery, I don't take the time to change case.  (I'm lazy)

Would look like this:<?php
switch (strrchar ($image'.')) {
   case 
'.jpeg' :
   case 
'.JPEG' :
   case 
'.jpg' :
   case 
'.JPG' :
      
$photoImage ImageCreateFromJPEG("/$image");
      break;
   case 
'.gif' :
   case 
'.GIF' :
      
$photoImage ImageCreateFromGIF("/$image");
      break;
   case 
'.png' :
   case 
'.PNG' :
      
$photoImage ImageCreateFromPNG("/$image");
      break;
}
?>
Overkill? maybe.   Bulletproof? maybe

I always prefer something like this over switches:

$extension = strtolower(strrchar($image, '.'));
if ($extension == 'jpg')
   $extension = 'imagecreatefromjpeg';
elseif (!function_exists('imagecreatefrom' . $extension))
   $extension = 'imagecreatefrompng';
else
   $extension = 'imagecreatefrom' . $extension;

$photoImage = $extension('/' . $image);

-[Unknown]

Aquilo

i just wish i could get these lib files to compile so i can beat my head trying to compile gd so I can do this on my pc.

am I defeating myself trying to using lcc or is it me? it keeps saying it don't know how to use makefile when I try compiling the jpeg lib?

[Unknown]

It's much easier on windows.  It's called "dl('php_gd.dll');".

-[Unknown]

dschwab9

Quote from: [Unknown] on October 29, 2003, 09:59:36 PM
It's much easier on windows.  It's called "dl('php_gd.dll');".

-[Unknown]

* dschwab9 think it's much easier on Linux :P :D :P

I can do pretty much anything you want with apache on Linux, but I've yet to get it to work right on Windows :-X

Advertisement: