4. Implementing a custom IGraphicFactory and registering it with XMLmind XSL-FO Converter

We'll use the support of .ico files — Windows native icons — as an example of extending the graphic capabilities of XMLmind XSL-FO Converter.

Implementing a IGraphicFactory is straightforward. You just need to implement 4 methods: GetInputFormats, GetOutputFormats, CreateGraphic and ConvertGraphic.

Excerpts of samples/dotnet/IcoGraphicFactory.cs:

...
using XmlMind.FoConverter;

public class IcoGraphicFactory : IGraphicFactory
{
    private static readonly string[] inputFormats = { 
        "image/vnd.microsoft.icon" 
    };
    private static readonly string[] outputFormats = { 
        "image/png" 
    };

    public string[] GetInputFormats()1
    {
        return inputFormats;
    }

    public string[] GetOutputFormats()2
    {
        return outputFormats;
    }
    ...

1

GetInputFormats returns the list of the media types (AKA MIME types) that the IGraphicFactory can read.

2

GetOutputFormats returns the list of the media types that the IGraphicFactory can write. In order to be useful to XMLmind XSL-FO Converter, a factory must return one or more of "image/png", "image/x-wmf", "image/x-emf".

    ...
    public IGraphic CreateGraphic(string location, string format, 
                                  object clientData, IGraphicEnv env)1
    {
        Image image = LoadImage(location);

        double xRes = 0;
        double yRes = 0;
        if ((image.Flags & ((int) ImageFlags.HasRealDpi)) != 0) {
            xRes = image.HorizontalResolution;
            yRes = image.VerticalResolution;
        }

        return new Graphic(location, format, image.Width, image.Height, 
                           xRes, yRes, GraphicType.Raster, clientData);2
    }

    private static Image LoadImage(String location) {
        Image image = null;

        Stream stream = GraphicUtil.OpenStream(location);3
        try {
            image = Image.FromStream(stream);
        } finally {
            stream.Close();
        }

        return image;
    }
    ...

1

Method CreateGraphic basically needs to parse the image file found at absolute URI location and having format as its media type. This method then returns an implementation of interface IGraphic which represents the parsed image file.

Note that argument format is guaranteed to be one the media types listed by GetInputFormats.

2

Class Graphic is a simple implementation of interface IGraphic.

3

In order to obtain the dimension of the image (width and height in pixels and possibly xResolution and yResolution in DPI), we have chosen in this simple example to fully load the image into memory. To do that, we use the GraphicUtil.OpenStream helper function. This helper not only supports ``normal URIs'' (that is, starting with "file:", "http:", etc) but also "data:" URIs.

    ...
    public IGraphic ConvertGraphic(IGraphic graphic, string format,
                                   double xScale, double yScale,
                                   object clientData, IGraphicEnv env)1
    {
        int width = graphic.GetWidth();
        int height = graphic.GetHeight();

        double xRes = graphic.GetXResolution();
        double yRes = graphic.GetYResolution();

        Image image = LoadImage(graphic.GetLocation());

        if (xScale != 1) {
            width = (int) Math.Round(width * xScale);
        }
        if (yScale != 1) {
            height = (int) Math.Round(height * yScale);
        }

        Bitmap bitmap = new Bitmap(image, width, height);

        if (xRes > 0 && yRes > 0) {
            bitmap.SetResolution((float) xRes, (float) yRes);
        }

        string outPath = env.CreateTempFile(".png");2
        bitmap.Save(outPath, ImageFormat.Png);

        return new Graphic(GraphicUtil.FilenameToLocation(outPath), format,3 
                           width, height, xRes, yRes, 
                           GraphicType.Raster, clientData);
    }
    ...

1

Method ConvertGraphic is invoked to convert its graphic argument (previously created using CreateGraphic) to the format media type. This method then returns an implementation of interface IGraphic which represents the converted image file.

Note that argument format is guaranteed to be one the media types listed by GetOutputFormats.

2

The converted image file must be stored in a temporary file created using method IGraphicEnv.CreateTempFile. Such temporary files are automatically deleted when no longer needed.

3

Class GraphicUtil contains several useful helper functions, among them FilenameToLocation which converts a filename to a "file:" URI.

    public static int Main(string[] args)
    {
        ...
        GraphicFactories.Register(new IcoGraphicFactory());1
        ...            
            converter.SetInput(inUri);
            converter.SetOutput(outPath);
            converter.Convert();
        ...

1

For an implementation of IGraphicFactory to be used by XMLmind XSL-FO Converter, this class must be registered using GraphicFactories.Register. In this simple example, we do that in the Main method, prior to invoking Converter.Convert.