Heading to the Printing United trade show? Schedule a meeting with our representatives.

Merging Layers

Warning

This section, which describes how you can merge PSD layers into an image, is out of date. Due to the great complexity of this rendering process, PSD Processor was implemented in Graphics Mill. So, use the PsdProcessor class instead of merging the layers.

The task of creating personalized graphics includes two general steps:

  1. creating a template (PSD file),
  2. customizing some template layers with personal details.

This topic examines how to merge PSD layers into a bitmap and provides a simple business card personalization code. If you are unfamiliar with the PsdReader, PsdFrame, or PsdTextFrame classes, read the Loading Raster Layers and Loading Text Layers topics first.

Merging Layers

The PsdReader class provides three ways to get a merged copy of all of the PSD layers:

  • The PsdReader.MergedImageFrame property returns a Frame containing a merged copy of all of the visible layers. This frame is typically created and stored by Adobe Photoshop; however, there is no guarantee that this frame exists in any PSD file.
  • The PsdReader.MergeLayers() method returns the bitmap of a merged frame (provided by the MergedImageFrame property) or merges visible layers one by one if the merged frame does not exist.
  • The PsdReader.MergeLayers(PipelineElement) method allows for using a merged frame in pipelines.

The following code merges all layers in a PSD file and saves the result in the PNG format in a memory-friendly way, using a pipeline:

C#
using (var psdReader = new PsdReader(@"Images\BusinessCard.psd"))
using (var writer = ImageWriter.Create(@"Images\Output\out1.png"))
    psdReader.MergeLayers(writer);

Personalizing Business Cards

This part of the article examines graphic personalization, using a personal business card as an example. In the Personalizing PSD Templates topic, the same functionality is implemented, but it is done in a simpler way, using the PsdProcessor1 callbacks. If you are interested, you can compare the amount of code in both of the topics.

Consider the following business card template:

Business card template with empty placeholders.

Important

There are many text settings available in Adobe Photoshop, but not all of them are supported by Graphics Mill. Therefore, when you create a PSD template, use only the settings described in the Loading Text Layers topic. Unsupported parameters will be ignored.

The template contains placeholders. Personalization means that these placeholders will be replaced with actual data. The business card containing a person's name instead of the FullName placeholder will look as follows:

Result image (placeholder [FullName] is replaced by the actual data).

Precise text rendering is a very complicated task, and it is practically impossible to achieve exact conformance with that which you get from Adobe Photoshop. The main cause of this is that Photoshop operates with its own rasterizer, which differs from those used in Windows (and Graphics Mill). Fortunately, only very rare tasks demand 100% image compliance. However, you should always recalculate the Y coordinate of the text output, because the Graphics.DrawText(Text) method inserts a small gap above the string, while the PsdFrame.Y property does not include this gap. To calculate the Y-coordinate properly, utilize the Text.GetBlackBox() method, which returns the tightest bounding rectangle of the string relative to the text output origin. This way, you can get the offset that should be subtracted from the Y-coordinate of the layer.

The following code is a function that customizes the specified PSD template with the given data:

C#
private static Bitmap RasterizePsd(string sourceImagePath, 
    System.Collections.Hashtable updatedLayers)
{
    Bitmap result;

    // Create a PSD reader and the resulting bitmap, which will contain a merged bitmap.
    using (var psdReader = new PsdReader(sourceImagePath))
    {
        result = new Bitmap(psdReader.Width, psdReader.Height, psdReader.PixelFormat, 
            RgbColor.Transparent);
        result.DpiX = psdReader.DpiX;
        result.DpiY = psdReader.DpiY;

        // Cycle through the visible raster and text layers.
        foreach (var frame in psdReader.Frames)
        {
            if (frame.IsVisible && (frame.Type == FrameType.Raster ||
                frame.Type == FrameType.Text))
            {
                object placeholder = updatedLayers[frame.Name];
                if (placeholder != null)
                {
                    // Replace text.
                    if (frame.Type == FrameType.Text && placeholder is string)
                    {
                        var textFrame = (PsdTextFrame)frame;
                        using (var graphics = result.GetAdvancedGraphics())
                        {
                            var font = FontRegistry.Installed.CreateFont(textFrame.FontName, textFrame.FontSize, graphics.DpiX, graphics.DpiY);
                            var text = new PlainText((string)placeholder, font);
                            int y = textFrame.Y - (int)text.GetBlackBox().Top;
                            text.Position = new System.Drawing.PointF(textFrame.X, y);
                            graphics.DrawText(text);
                        }
                    }
                    // Replace the bitmap.
                    else if (frame.Type == FrameType.Raster && placeholder is Bitmap)
                    {
                        // If the width of the bitmap from updatedLayers differs from the
                        // width of the layer, then the bitmap will be resized to avoid design corruption.
                        var rect = new System.Drawing.Rectangle()
                        {
                            X = frame.X,
                            Y = frame.Y,
                            Width = frame.Width,
                            Height = 0
                        };

                        result.Draw((Bitmap)placeholder, rect,
                            CombineMode.Alpha, 1.0f, ResizeInterpolationMode.High);
                    }
                }
                else
                {
                    using (var bitmap = frame.GetBitmap())
                    {
                        var rect = new System.Drawing.Rectangle()
                        {
                            X = frame.X,
                            Y = frame.Y,
                            Width = frame.Width,
                            Height = frame.Height
                        };

                        result.Draw(bitmap, rect, CombineMode.Alpha, 1.0f,
                            ResizeInterpolationMode.High);
                    }
                }
            }
        }
    }
    return result;
}

The following code calls the function listed above and replaces the FullName and CompanyLogo placeholders:

C#
//Specify the personalization data.
var updatedLayers = new System.Collections.Hashtable();
updatedLayers.Add("FullName", "Andrew Simontsev");
updatedLayers.Add("CompanyLogo", new Bitmap(@"Images\logo.png"));

//Personalize the business card and save the resulting image.
using (var businessCard = RasterizePsd(@"Images\BusinessCard.psd", updatedLayers))
{
    businessCard.Save(@"Images\Output\out2.png");
}

See Also

Reference

Manual