Tuesday, November 27, 2007

Declarative Eclipse Branding with SVG

Recently, I had need to add some Eclipse branding graphics to an RCP application.

Rather than take the "easy" route of using an image editor, I thought I'd explore the use of SVG to produce the required images in a declarative kind of way.

Using SVG for this purpose has a number of advantages ...
  • they are cross platform
  • the declarative, text file nature allows the content to be generated
  • they can be created using open-source tools
The downside is that the bitmaps required for Eclipse branding must be in BMP format - not the newer PNG alternative. This is covered in detail in this Eclipse bug report - https://bugs.eclipse.org/bugs/show_bug.cgi?id=21315.

Fortunately, this can be overcome by converting the SVG images to a bitmap format by using a conversion program called a "rasterizer".

A good example of a rasterizer is Batik - an open-source toolkit for manipulating SVG images.
See http://xmlgraphics.apache.org/batik/tools/rasterizer.html for further details on the Batik rasterizer.

Round Trip example

Here's a simple example of some SVG source to get started.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="500"
height="330"
id="drawing">

<defs>
<g id="mytext">
<text
xml:space="preserve"
style="font-size:40px;font-weight:normal;text-align:start;line-height:100%;text-anchor:start;font-family:Bitstream Vera Sans"
x="200" y="125">Hello</text>
<text
xml:space="preserve"
style="font-size:40px;font-weight:normal;text-align:start;line-height:100%;text-anchor:start;font-family:Bitstream Vera Sans"
x="200" y="170">World ...</text>
</g>
</defs>

<rect style="fill:black" width="500" height="330" x="0" y="0" />
<use xlink:href="#mytext" style="fill:white" />

</svg>

This produces a result like this:


In order to produce this image for the web, I used the Batik rasterizer. For example, the command I used was this:


C:\>java -jar c:\jshare\batik-1.7\batik-rasterizer.jar drawing5.svg
About to transcode 1 SVG file(s)

Converting drawing5.svg to drawing5.png ... ... success



Once you have a PNG format image, it's a "simple" matter of converting that to a BMP format. The simplicity involved depends on your platform:
  • if you're on Windows - you can use Microsoft Paint: open the PNG and then save as BMP
  • if you're on Linux - you can use GIMP
Refining your Image

Well, that's produced a very simple image. But one of the advantages of SVG is that you can get very nice effects with a minimum of effort.

Step 1 - Background Gradient

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="500"
height="330"
id="drawing">

<defs>
<g id="mytext">
<text
xml:space="preserve"
style="font-size:40px;font-weight:normal;text-align:start;line-height:100%;text-anchor:start;font-family:Bitstream Vera Sans"
x="200" y="125">Hello</text>
<text
xml:space="preserve"
style="font-size:40px;font-weight:normal;text-align:start;line-height:100%;text-anchor:start;font-family:Bitstream Vera Sans"
x="200" y="170">World ...</text>
</g>

<linearGradient id="top">
<stop offset="0%" style="stop-color: black;"/>
<stop offset="100%" style="stop-color: #484848;"/>
</linearGradient>

</defs>

<rect style="fill:url(#top)" width="500" height="330" x="0" y="0" />

<use xlink:href="#mytext" style="fill:white" />

</svg>

This produces a result like this:



Nice enough, but the default direction for a gradient is from left to right.

Step 2 - Background Gradient - in the correct direction

Here, we use another gradient - with a direction specifier - and embed the first.


<linearGradient id="top">
<stop offset="0%" style="stop-color: black;"/>
<stop offset="100%" style="stop-color: #484848;"/>
</linearGradient>

<linearGradient id="topa"
xlink:href="#top"
x1="0%" y1="0%" x2="0%" y2="100%"/>

</defs>

<rect style="fill:url(#topa)" width="500" height="330" x="0" y="0" />



This produces a result like this:



Step 3 - Background Gradient - with a horizon effect

This refinement adds a horizon - by splitting the image in two - at 200 px down, and using two gradients.


<linearGradient id="top">
<stop offset="0%" style="stop-color: black;"/>
<stop offset="100%" style="stop-color: #484848;"/>
</linearGradient>

<linearGradient id="topa"
xlink:href="#top"
x1="0%" y1="0%" x2="0%" y2="100%"/>

<linearGradient id="bottom">
<stop offset="0%" style="stop-color: #707070;"/>
<stop offset="100%" style="stop-color: black;"/>
</linearGradient>

<linearGradient id="bottoma"
xlink:href="#bottom"
x1="0%" y1="0%" x2="0%" y2="100%"/>

</defs>

<rect style="fill:url(#topa)" width="500" height="200" x="0" y="0" />
<rect style="fill:url(#bottoma)" width="500" height="130" x="0" y="200" />



This produces a result like this:



Step 4 - With a Graphic

Naturally, if we are doing a nice, modern style image, we should use some modern styled clip-art to give the image a lift. Since we are using SVG, there is a whole world of choice at the Open Clip Art project. For my Hello World example, I've chosen a nice clean globe.

In order to use a graphic, download the source to separate file. It can then be included in two different ways:
  1. cut and paste the source code into your SVG file
  2. save it to a separate file, and reference it as below using the image tag.
Note that I have applied a scale transformation to make the image fit in.


<g id="globe">
<image xlink:href="globe.svg"
width="500" height="500" x="0" y="0"
transform="scale(0.25)"/>
</g>

</defs>

<rect style="fill:url(#topa)" width="500" height="200" x="0" y="0" />
<rect style="fill:url(#bottoma)" width="500" height="130" x="0" y="200" />

<use xlink:href="#mytext" style="fill:white" />
<use xlink:href="#globe" x="60" y="80" />



This produces a result like this:



Step 5 - With a Reflection

No self-respecting modern image would be complete without the "reflection" technique! This is amazingly simple using SVG - just apply a transform effect on the use tag.


<use xlink:href="#mytext" style="fill:white" />
<use xlink:href="#mytext" style="fill:white;opacity:0.25" transform="matrix(1 0 0 -1 0 400)"/>

<use xlink:href="#globe" x="60" y="80" />
<use xlink:href="#globe" style="opacity:0.25" x="60" y="80" transform="matrix(1 0 0 -1 0 400)"/>



This produces a result like this:

Friday, November 02, 2007

Photoshop Elements - Explorer

As a way of investigating the internals of the Adobe Photoshop Elements (PSE) meta tags, I thought I'd whip up a little Eclipse plug-in to do some experimentation.

The primary purpose was to provide a means of exporting my metadata. I do a little editing within PSE, but my largest investment is in the metadata attached to (almost) every photo.

I realize that you can (and should) backup your PSE catalog using the supplied tools, but this approach also provides a degree of independence should I ever wish to decamp to another product.

Plug-in Structure

The following diagram shows the plug-in dependency hierarchy. The diagram was produced using the brilliant Plug-in Dependency Visualization Tool from Ian Bull, also described here on his blog.



As can be seen from the hierarchy, I make use of a number of libraries, packaged as plug-ins.
  • XOM - to provide XML support. See here for more details on XOM.
  • SQLite JDBC driver. See here for more details.
I had a few choices about how I was going to deploy it - workbench, RCP or even scriptable via Eclipse Monkey ... but I thought the workbench plug-in would be the simplest thing to get started.

Workbench Integration

Since I was aiming for the workbench, it made sense to provide some integration with the functionality you get for free! The two most obvious points are:
  • workbench properties
  • an image viewer
It's very easy to integrate with the workbench Properties View. This is very well documented - in particular in this article.

I have also encountered a great image viewer, courtesy of Wayne Beaton - see here for more details.

The great thing about making use of these functions is that they simply require an adapter to the required class to exploit the functionality.
  • the Properties View needs an adapter to an IPropertySource implementation
  • the Image View plug-in requires an adapter to an ImageProvider implementation.
To keep things simple, I initially coded up the getAdapter code directly into the model - but, with a teeny bit of thought - and prompting from Wayne's latest "adapters" articles here - it made sense to extract them into an AdapterFactory. And after doing this, it made sense to extract the whole model to it's own plug-in.

Plug-in Functionality

It's pretty basic - a simple PSE Explorer View, which lists the contents of the catalog, and two menu options:
  • one to load an existing PSE catalog file (SQLite database)
  • one to export the loaded PSE catalog as an XML Document.
The following screen shot shows the plug-in in action - with the PSE Explorer view on the top half of the screen, and the properties and Image View on the bottom.



The plug-in functionality is initiated by the two command handlers (corresponding to the menu items) - that is, import and export.

The import function is facilitated by a simple DataProvider class. This provides the means of accessing the SQLite database and populating the model used by the viewer. The SQL used is similar to my earlier experiments.

The export function merely provides an XML transformer for the populated model. The results of the export looks like this:



Summary

This is just a starting point, but it has provided a easy way for exploring the PSE metadata. With a bit more polish, I'll see if I can release some of the code.