Imagick: Maintain (fake) transparency when saving as JPEG

I haven't blogged in a while (have been busy you know), so I've decided to share this small piece of knowledge I've obtained by experimenting. I wrote a small test app (it's for a feature of the next version of Zend Server - maybe I'll share it one day when the API is stable), which does some image manipulation with the ImageMagick extension.

For those of you who don't know ImageMagick allows one to preform pretty cool stuff on images - except for the usual drawing, conversion, rotation, rescaling etc., it also exposes some API to easily preform neat effects, like drop shadow, round corners and my newest favorite (apparently only available in the very latest builds of the extension) - the Polaroid effect.

In his blog Mikko Koppanen, the author of the ImageMagick PHP extension, shows how to create drop shadows (as well as other neat things - you should check out his blog!), but in his examples Mikko will always save as PNG, which is something I dare to say most web users will not do, and prefer saving as JPEG.

Problem with many of those effects, is that they leave parts of the image as transparent. When saving the picture as JPEG (as I do, since saving as PNG produces too big files), these transparent areas appear as black.

So after some experimenting, I've found out that the way to work around this is to composite another opaque layer as your background layer, filled with your background color of choice (white in my case). You will of course loose the ability to place the picture on other background colors and still have a nice "transparency" look - but as long as you stick to the background color you've set, it will look great.

Here is a code sample producing the same thumbnail + drop shadow as in Mikko's example, but saving it with white matte color as JPEG:

PHP:
  1. <?php
  2.  
  3. $bgColor = '#ffffff'; // End result will have a white background
  4.  
  5. /* This was taken from Mikko's example */
  6. $im = new Imagick( 'strawberry.png' );
  7. $im->thumbnailImage( 200, null );
  8. $im->roundCorners( 5, 5 );
  9.  
  10. $shadow = $im->clone();
  11. $shadow->setImageBackgroundColor( new ImagickPixel( 'black' ) );
  12. $shadow->shadowImage( 80, 3, 5, 5 );
  13. $shadow->compositeImage( $im, Imagick::COMPOSITE_OVER, 0, 0 );
  14.  
  15. /* My addition: clone the entire image again to create the background layer */
  16. $bg = $shadow->clone();
  17.  
  18. /* I'm using colorFloodFiilImage with high tolerance to paint it all white - maybe there are 'cleaner' ways to do it though */
  19. $bg->colorFloodFillImage($bgColor, 100, '#777777', 0, 0);
  20. $bg->compositeImage($shadow, Imagick::COMPOSITE_OVER, 0, 0);
  21. $bg->setImageFormat('jpeg');
  22. $bg->flattenImages();
  23.  
  24. /* Display the image */
  25. header( "Content-Type: image/jpeg" );
  26. echo $bg;

While there's another step in the way, and the image will only look good on white backgrounds, you can now save it as a JPEG file with good compression and acceptable file size.

How much is listening to your customers worth?

I normally don't write about work. The reason is that I feel that the slight chance that someone might feel I'm being biased towards a product that comes from the company I work for and dismiss my thoughts as "guerilla marketing" is not worth it.

However, I'm going to make an exception - and that's because I prefer selling Zend here rather than doing it on Lukas Smith's blog :)

Lukas raises the question of what commercial PHP distribution should be used as an alternative to RHEL outdated packages. My answer on that would be, surprisingly - use Zend Server! (well, ...once it's out of beta, of course).

Lets put the features and SLA you get from Zend Server aside for a moment.

The real reason I think you should use Zend Server is because the Zend Server product manager (hey, that's me!) reads your blog. I'm serious about this.

I'm not sure I can quantify this, but I think that a vendor that listens so closely to what potential users (and the community) has to say is worth quite a lot in the long run. And yes, Zend has not been perfect in listening to the community - but I can honestly and whole-heartedly say that we are trying harder. The recent feedback on Zend Server gives me the feeling that we are doing ok too.

Debugging CLI PHP with Zend Server and PDT on Linux and Mac

I'm working on a small PHP application and a big part of it are some CLI scripts which will be executed in the background. Some of these scripts are quite complex, and I got to a point where I need to use a debugger in order to figure out what's going on.

I started hacking around with my locally-installed Zend Server CE and Zend Studio. I always knew how to manually start CLI debug sessions with Zend Studio (well, I knew, but forgot ;-) ), but then I figured, why not write a small shell script to automate the process, and learn a little about the Zend Debugger protocol on the way?

Here is what I did:

First, create the following shell script. I placed it at /usr/local/zend/bin/php-dbg (alongside the other Zend Server executables, which if you use Mac OS X will be at /Applications/ZendServer/bin):

CODE:
  1. #!/bin/sh
  2.  
  3. # Wrapper script for debugging PHP CLI scripts with Zend Studio
  4. # Tested with Zend Server 4.0.0 Beta and Zend Studio for Eclipse 6.1.1
  5. # Shahar Evron [shahar.e at zend], 2009-02-20
  6.  
  7. # Defaults
  8. DFLT_PORT="10137"
  9. DFLT_HOST="127.0.0.1"
  10. DFLT_PARAMS="debug_fastfile=1&use_tunneling=0"
  11.  
  12. # Load Zend Server environment variables
  13. . /etc/zce.rc
  14.  
  15. # Did the user specify the debug host / port?
  16. if test "x$DEBUG_HOST" != "x"; then
  17.   if test "x$DEBUG_PORT" != "x"; then
  18.     QUERY_STRING="&debug_port=$DEBUG_PORT"
  19.   else
  20.     QUERY_STRING="&debug_port=$DFLT_PORT"
  21.   fi
  22.  
  23.   QUERY_STRING="$QUERY_STRING&debug_host=$DEBUG_HOST&$DFLT_PARAMS"
  24.  
  25. # If no host/port were specified, try to auto-detect
  26. else
  27.   QUERY_STRING=`wget http://localhost:20080/ -O - 2> /dev/null`
  28.   if test $? -ne 0; then
  29.     # Fall back to defaults
  30.     echo "Unable to auto-detect Zend Studio settings, using defaults">&2
  31.     QUERY_STRING="&debug_port=$DFLT_PORT&debug_host=$DFLT_HOST&$DFLT_PARAMS"
  32.   fi
  33. fi
  34.  
  35. DBG_SESS_ID=`date +%s`
  36. QUERY_STRING="start_debug=1&debug_stop=1$QUERY_STRING&debug_session_id=$DBG_SESS_ID"
  37.  
  38. QUERY_STRING=$QUERY_STRING $ZCE_PREFIX/bin/php -c $ZCE_PREFIX/etc/php.ini $@

Going over this code might teach you some surprising things about how Zend Debugger and Zend Studio talk to each other ;) I'm not going to go into the details now, but if you have questions feel free to ask.

Next, make this script executable - just run 'chmod +x <path-to-script>' - and you're good to go.

Here is how to use it:

  • If you have PDT or Zend Studio running locally (on the same machine as the server), just run:

    # /usr/local/zend/bin/php-dbg <script you want to debug>

    That would just work in most cases - if it works you can stop reading now ;-)
  • If you are running the script on a server, but your PDT / Zend Studio is on a different machine (in the same LAN - no NAT or firewall!) you can simply specify the IP address or host name of the machine that runs PDT / Zend Studio as the DEBUG_HOST environment variable. For example:

    # DEBUG_HOST=10.1.2.3 /usr/local/zend/bin/php-dbg <script you want to debug>
  • If you are running the script on a remote machine (as above) and your Zend Studio listens on a port other than 10137, you can also pass the DEBUG_PORT environment variable to override the default port.
  • Also, don't forget to make sure that the machine that runs your Zend Studio is in the list of allowed debugging clients. You can check it at the Zend Server GUI on Server Setup -> Debugger.
  • If you are running the script on a remote host and there's a firewall / NAT between you and the server (e.g. you are in an office LAN, trying to debug a script on a remote production machine which is not in your subnet) you'll probably need to use SSH remote port forwarding to forward connections to your PDT / Zend Studio. I won't get into how to do it right here - unless you insist.
  • If you want to only type 'php-dbg' when running instead of the full path, you can place the file in your $PATH (e.g. in /usr/local/bin) or even better, Add /usr/local/zend/bin (or /Applications/ZendServer/bin) to your $PATH - you can do that by adding the following line to ~/.bashrc:

    PATH=$PATH:/usr/local/zend/bin

Upon running the script, a debug session should simply pop-up in your PDT / Studio and you'll be able to debug. How cool is that?

BTW: This has been tested with Zend Server 4.0.0 beta1 and Zend Studio 6.1.1. It should work with other versions of Studio as well. In fact, it can also work without Zend Server as long as you have Zend Debugger installed - but why ruin a perfectly good plug?

If you improve the script or find bugs, let me know! Also, if you know how to get the same thing going with xDebug, let me know and I'll add it to the script.

Finally, it’s out: Zend Server

I normally try not to write about work related stuff... but this is a special occasion.

Zend Server is finally out for public beta. \o/

I was working so hard on this for the last year, It kind of feels like I've just crapped an Elephpant ;)

Seriously now, I really like this product. I think it has great potential. I know a bunch of very good people who worked very hard on it, and deserve every bit of gratitude. We went over some rough times at Zend and we still were able to release this wonderful product! I'm so proud... :)

Priceless: “The Issuer Certificate Is Unknown”

Firefox: "mossad.gov.il uses an invalid security certificate"

Another example of the all-so-frightening invalid HTTPS certificate warning in Firefox 3.0. I just found this one to be a bit ironic :)

BTW The Mossad website is mostly for recruiting purposes, they don't really let you search their archives on-line or anything... to bad, that could have been interesting :P

(and one more thing: yes, it's "The Mossad" and not just "Mossad" as it's frequently mis-translated in foreign media. "Mossad" literally means "Institute" or maybe in a less literal translation, "Agency". There are many institutes and agencies, but there is only one "The Institute")

Subversion: Finding the “base” revision of a branch

I use Subversion a lot - but today I've learned something new:

You can easily find the "base" revision of a branch or a tag (i.e. the revision in which the branch or tag was created) by issuing the following command:

CODE:
  1. svn log -v --stop-on-copy \
  2.     http://glista.googlecode.com/svn/branches/feature-reminders

The last revision you see in the log (in this case from one of my own Glista project's branches) is the revision the svn copy command was issued on, i.e. the branch was made.

This can then be used when merging the same branch back into trunk.

Neat!

Seven Things - Tagged by Ivo Jansch

"I was hopping along, minding my own business, when all of a sudden, up he comes…Cures me! One minute I’m a leper with a trade, next minute my livelihood’s gone!"

So apparently I was tagged by non other than the mighty Ivo Jansch, thus forced into some silly chain-blogging game. After managing to teach everyone in my life including the tech-clueless to never forward any chain mail to me (except for my grandma - I did get her to stop but then she sometimes forgets not to do it), I get bitten by the most tech-savvy people I might know :P

Well, I can't say no - especially after being titled "Product Manager and Smart Guy at Zend" (for a short period of time that was almost my official title BTW). Of course I'll play along! It's also interesting to see how far this goes :)

So here are seven things about me you might or might not know:

  • I never went to university - I've noticed some people state what their major was etc. - so, I never went to university. I never got the chance. While everyone else was in university, I was busy doing other things like learning to write PHP :) I always tell myself I'll go in a year or to - but now that I've gotten into a position everyone assume I have at least a BSc, I don't want to spoil that :).
  • I never took any official CS training - I've frequently found it boring the way it was taught in school. Don't get me wrong - I love CS and I sometimes find it exciting - but I enjoy it much more when I get to teach myself. I also tend to believe CS is nice but it's a tool - not really a "wisdom" I'd like to learn in university. I consider other fields like philosophy, history, biology or even mathematics to be far more worth studying. BTW I did take a couple of CS university courses during high-school - I flunked one of them ;)
  • I started programming when I was 7 or 8 - in BASIC. Well, it was nothing impressive but this is how I started. I then went through some Pascal and some C but never got to any level worth talking about. Then in high-school there was the web, and I started playing with it, first with HTML and then with Perl writing CGI stuff. Pretty much at the same time I started experimenting with Linux.
  • I did that while majoring in Cinematography. Yes - the only thing I did learn professionally was making movies, and I still have a lot of passion for it. I especially enjoyed directing, filming and editing. I did try working for some time on several junior technical positions in the local film industry (2nd assistant camera operator etc.) and did get my name in the credits of some movies (who were major in Israel - so there you have it Ivo ;) ) but working for the "industry" so to speak was not so great, and I decided to drop it.
  • Just like most male citizens of Israel I did my military service after highschool - and didn't get to touch a programmable computer for 3 years (I did get to touch some computers but they were not the kind you want to mess around with). I even almost became an officer (which means I could have spend more than those 3 years in the army) but I didn't run fast enough. I still spend a few days every year in reserve training, but sometimes they let me off the hook when I have to go to some PHP conference :)
  • When I got out of the army and back into the real world, Perl was gone and all of a sudden there was PHP everywhere. I found out I could write the same app I wrote in Perl in a month in about a week in PHP, and the rest is history. Back at the time I wrote my first PHP app - a web site which is still running today. I looked at the code a few months ago and almost puked.
  • Before working for Zend, I worked for a local ISP which as managed by the biggest Linux geek I've met (no wonder it's out of business by now) - that was a lot of fun. Before that, I did all sorts of things - I herded goats, I picked cherries and I grew long curly hair. I got to Zend by pure chance - I didn't even knew they were an Israeli company until I met a cousin of mine who told me he was working there!

So... those are my seven things!

And now, who to tag? I have very few candidates left - I hardly have any blogging friends who haven't already been tagged... So here are my people, hopefully they'll forgive me:

  • Nir Yariv - friend, family member, and one of the smartest (infrequent) bloggers I know (he's also the guy who got me into Zend).
  • Christer Edvartsen - fellow PHP coder, ZF contributor, and occasional (one a year or so) drinking buddy
  • John Coggeshall - Ex-Zender, Current CTO at ACS, PHP author, and well, another occasional drinking buddy
  • Stas Malyshev - PHP Internals Guru and Zend's 1st employee
  • Boaz Rymland- Another ex-colleague, Drupal expert and a certified social worker
  • Andi Gutmans - Fellow Zender and, wel... you all know him don't you?
  • Zeev Suraski - Well you know him too don't you?

Well... that's my list - let's see how many of them step up to the plate!

Oh - and here are the rules for any bloggers who decide to follow up:

  • Link your original tagger(s), and list these rules on your blog.
  • Share seven facts about yourself in the post - some random, some wierd.
  • Tag seven people at the end of your post by leaving their names and the links to their blogs.
  • Let them know they've been tagged by leaving a comment on their blogs and/or Twitter.

PHP Error Reporting Levels - WTF is 6134?

In PHP, the error reporting level (whether errors go to the log or to the screen or whatever) is determinded by the error_reporting INI directive (or at runtime using the error_reporting() function). Both take an integer as their value - and usually this integer is represented by error level constants like E_ALL, E_STRICT or E_USER_WARNING.

So in order to set the error reporting to anything but notices and strict errors, you would set something like this in php.ini:

CODE:
  1. error_reporting = E_ALL & ~E_NOTICE & ~E_STRICT

(actually, E_ALL does not really include E_STRICT but I put it here just to be more explicit)

This is actually great - one of the more easy to use and understand APIs in my opinion (yeah, I really like bitwise operations).

However, what I really hate is that sometimes I need to work with the integer value of the error reporting level (like 1 for E_ERROR or 84 for E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR) and it's very hard for me to remember what arbitrary numbers like 6134 mean.

So, today I wrote this tiny CLI script that helps me understand what an arbitrary error_reporting level integer might mean:

CODE:
  1. <?php
  2.  
  3. $errorLevels = array(
  4.         'E_COMPILE_ERROR'     => E_COMPILE_ERROR,
  5.         'E_COMPILE_WARNING'   => E_COMPILE_WARNING,
  6.         'E_CORE_ERROR'        => E_CORE_ERROR,
  7.         'E_CORE_WARNING'      => E_CORE_WARNING,
  8.         'E_ERROR'             => E_ERROR,
  9.         'E_NOTICE'            => E_NOTICE,
  10.         'E_PARSE'             => E_PARSE,
  11.         'E_RECOVERABLE_ERROR' => E_RECOVERABLE_ERROR,
  12.         'E_STRICT'            => E_STRICT,
  13.         'E_USER_ERROR'        => E_USER_ERROR,
  14.         'E_USER_NOTICE'       => E_USER_NOTICE,
  15.         'E_USER_WARNING'      => E_USER_WARNING,
  16.         'E_WARNING'           => E_WARNING,
  17. );
  18.  
  19. if (! isset($_SERVER['argv'][1])) {
  20.         fprintf(STDERR, "Usage: {$_SERVER['argv'][0]} <error_level>\n" .
  21.                         "  Where <error_level> is a PHP error reporting level\n");
  22.         exit(1);
  23. }
  24.  
  25. $level = $_SERVER['argv'][1];
  26.  
  27. echo "Error level $level includes:\n";
  28. foreach ($errorLevels as $k => $v) {
  29.         if ($level & $v) echo "\t $k \n";
  30. }
  31.  
  32. echo "\n";

To use, just run the script with a single value parameter, like so:

CODE:
  1. shahar.e@wintergreen ~ $ php error_level.php 6134
  2. Error level 6134 includes:
  3.      E_COMPILE_ERROR
  4.      E_COMPILE_WARNING
  5.      E_CORE_ERROR
  6.      E_CORE_WARNING
  7.      E_PARSE
  8.      E_RECOVERABLE_ERROR
  9.      E_USER_ERROR
  10.      E_USER_NOTICE
  11.      E_USER_WARNING
  12.      E_WARNING

Enjoy!

Attending Adobe Max next week

As part of my job at Zend, I was invited by Adobe to Adobe Max in San Francisco - how cool is that? It's a huge conference (thousands of participants - nothing like any PHP conference I know!) with so many presentations to sit in it's just hard to choose.

Of course, I am no designer and tend to stick to the server side - so for me choosing was easier, but still confusing.

In any case if you are there, or in down town San Francisco, come and say hi!

ZendCon 2008 Slides

Well, ZendCon is over and it was much fun! I got home today (well, does 4:30 am count as "today" ?) and am still very tired - I had to stay around after the conference for some meetings (yes, the title "manager" causes some PITA even if you do not really manage anyone) which was a bit exhausting but fruitful never the less.

I gave this presentation about Zend Platform:

It's the first time ever I'm giving a presentation about proprietary Zend technology in an open-source conference so I was a bit nervous - but to my surprise I got a full room (I estimate some ~100 people were there, and only a few Zenders) and there seemed to be a lot of interest. In general this ZendCon felt a bit more "business-oriented" than usual, but still had a good mix of community and hacker-spirit to it.

Another thing is that Siddhartha - our VP of Sales for North America actually HUGGED me after the talk. He was sitting in the room and the guy knows a lot about selling Zend Platform - but I suppose that hearing the value of the different features and some good example use cases for Zend Platform from a technical perspective gave him and the rest of the sales team some good insight into what customers are looking for in such a product.

Anyway enjoy the slides and if you have questions just post a comment. I will probably post some more about the previous week - if only I will be able to get my hands off my new iPod Touch ;)