New Drawing API in Graphics Mill 7

In Graphics Mill 7 we introduce the new drawing API.

The major features are:

  • Drawing on different originals:
    • bitmaps, including CMYK and transparent
    • PDF/EPS
  • Extended text drawing:
    • custom font collection
    • effects applied to the text:
      • outline
      • glow
      • shadow
    • extended text transformation:
      • text on path
      • art text distortion and position: round and pinch text, etc.

Aurigma.GraphicsMill.AdvancedDrawing namespace contains classes of the new API. In a lot of ways it looks like an our existing API (Aurigma.GraphicsMill.Drawing), so there should be no problems with migration.

We omit full namespaces and disposing code in the snippets below for the sake of brevity. Let’s consider all classes to be inside Aurigma.GraphicsMill.*, otherwise full namespace will be used.

Graphics

The primary part of drawing is the Graphics class. It provides methods for drawing basic shapes, text and images. The Pen class defines drawing strokes as well as Brush one for filling areas.

Graphics can be created on different originals:

  • Bitmap (non-indexed formats only)
  • PdfWriter
  • EpsWriter
// Bitmap   
var bitmap = new Bitmap(500, 500, PixelFormat.Format24bppRgb, RgbColor.White);   
var gr = bitmap.GetAdvancedGraphics();   
   
// PDF   
var pdfWriter = new PdfWriter("out.pdf");   
pdfWriter.AddPage(500, 500);   
var gr = pdfWriter.GetGraphics();   
   
// EPS   
var epsWriter = new EpsWriter("out.eps", 500, 500);   
var gr = epsWriter.GetGraphics();   

Here is an example of basic drawing:

var gr = bitmap.GetAdvancedGraphics();   
   
gr.DrawRectangle(new Pen(RgbColor.Black), 10, 10, 80, 40);   
gr.DrawEllipse(new Pen(RgbColor.Black, 0.5f), 10, 10, 80, 40);   
   
gr.DrawLine(new Pen(RgbColor.Red), 10, 10, 90, 50);   
Basic drawing

Text

There are four main classes for drawing text:

  • PlainText
  • BoundedText
  • PathText
  • DoublePathText

These classes represent different ways of positioning and distorting the text.

PlainText

Simple line text is positioned at a baseline start point.

var alignments = new TextAlignment[]    
{    
    TextAlignment.Left,    
    TextAlignment.Center,    
    TextAlignment.Right   
};   
   
var pos = new System.Drawing.PointF(gr.Width / 2, gr.Height / 4);   
gr.DrawLine(new Pen(RgbColor.Blue, 0.3f), pos.X, 0, pos.X, gr.Height);   
   
foreach (var alignment in alignments)   
{   
    var text = new PlainText(alignment.ToString(), gr.CreateFont("Arial", 20));   
    text.Position = pos;   
    text.Alignment = alignment;   
   
    gr.DrawLine(new Pen(RgbColor.Blue, 0.3f), 0, pos.Y, gr.Width, pos.Y);   
   
    gr.DrawText(text);   
   
    pos.Y += gr.Height / 4;   
}   
Plain text

BoundedText

You can bound text inside of a rectangle:

var content = @"One area of focus is pre-press processing, and Graphics Mill is popular among customers in both the traditional and specialty printing industries.";   
   
var text = new BoundedText(content, gr.CreateFont("Segoe UI", 20))   
{   
    Rectangle = new System.Drawing.RectangleF(10, 10, gr.Width - 20, gr.Height - 20)   
};   
   
gr.DrawRectangle(new Pen(RgbColor.Blue, 0.3f), text.Rectangle);   
   
gr.DrawText(text);   
Bounded text

PathText

A text string baseline can be aligned on a path (set of curves).

var text = new PathText("Simlpe path text", gr.CreateFont("Segoe UI", 20));   
text.Alignment = TextAlignment.Center;   
   
text.Path.MoveTo(0, gr.Height / 2);   
text.Path.CurveTo(gr.Width / 3, gr.Height, gr.Width / 2, gr.Height / 3, gr.Width, gr.Height / 2);   
   
gr.DrawPath(new Pen(RgbColor.Blue, 0.5f), text.Path);   
   
gr.DrawText(text);   
Simple path text

DoublePathText

A text string can be clutched between two paths:

var text = new DoublePathText("Double path text", gr.CreateFont("Segoe UI", 20));   
text.Alignment = TextAlignment.Center;   
   
text.TopPath.MoveTo(0, gr.Height / 3);   
text.TopPath.CurveTo(gr.Width / 3, gr.Height, gr.Width / 2, gr.Height / 3, gr.Width, gr.Height / 4);   
   
text.BottomPath.MoveTo(0, gr.Height / 1.2f);   
text.BottomPath.CurveTo(gr.Width / 3, gr.Height, gr.Width / 2, gr.Height / 3, gr.Width, gr.Height / 1.2f);   
   
gr.DrawPath(new Pen(RgbColor.Blue, 0.5f), text.BottomPath);   
gr.DrawPath(new Pen(RgbColor.Blue, 0.5f), text.TopPath);   
   
gr.DrawText(text);   
Double path text

Art text

Inside the Aurigma.GraphicsMill.AdvancedDrawing.Art namespace there is a set of helper classes for distorting and positioning PathText/DoublePathText around a center point. All these classes are derived from the ArtText class.

For example we can use RoofText to create the appropriate text shape:

var text = new RoofText("roof text sample", gr.CreateFont("Arial", 30));   
text.Bend = 0.5f;   
text.Center = new System.Drawing.PointF(gr.Width / 2, gr.Height / 2);   
   
gr.DrawText(text);   
Roof text

Text effects

With the Pen and Brush properties you can get colored and outline text.

var blueText = new PlainText("Blue text", gr.CreateFont("Comic", 30));   
blueText.Position = new System.Drawing.PointF(10, blueText.GetBlackBox().Height + 5);   
blueText.Brush = new SolidBrush(RgbColor.Blue);   
   
gr.DrawText(blueText);   
   
var outlineText = new PlainText("Outline text", gr.CreateFont("Comic", 30));   
outlineText.Position= new System.Drawing.PointF(10, blueText.Position.Y + outlineText.GetBlackBox().Height + 10);   
outlineText.Pen = new Pen(RgbColor.Red);   
outlineText.Brush = new SolidBrush(RgbColor.Transparent);   
   
gr.DrawText(outlineText);   
Outline text

You can even add some effects using classes of the Aurigma.GraphicsMill.AdvancedDrawing.Effects namespace:

blueText.Effect = new Shadow(RgbColor.Black, 2, 1, 1);   
   
outlineText.Effect = new Glow(RgbColor.Blue, 1);   
Shadow text

Measure

Measuring text means getting a bounding box around it. You can measure any type of text described above, including art text:

gr.DrawRectangle(new Pen(RgbColor.Blue), 0, 0, gr.Width, gr.Height);   
   
var text = new RoundText("round text", gr.CreateFont("Arial", 30));   
text.Bend = 0.9f;   
text.Center = new System.Drawing.PointF(   
                gr.Width - text.GetBlackBox().Width / 2,   
                gr.Height - text.GetBlackBox().Height / 2);   
               
gr.DrawText(text);   
gr.DrawRectangle(new Pen(RgbColor.Red, 0.5f), text.GetBlackBox());   
Round text

You can also use the Font class to measure a string:

var font = gr.CreateFont("Arial", 33);   
   
StringMeasure sm = font.MeasureString("Measure string");   
   
Console.WriteLine("Width: {0}", sm.Width);   
Console.WriteLine("Height: {0}", sm.Height);   
Console.WriteLine("Ascender: {0}", sm.Ascender);   
Console.WriteLine("Descender: {0}", sm.Descender);   

For example you can use it defining custom art text.

Fonts

The centerpiece of font supports in the toolkit are font registry objects. They are designed for instantiating fonts using a factory-method-like API. Using them you can get access to system installed fonts as well as dynamically load fonts stored in the file system.

Supported font formats:

  • TrueType
  • OpenType
  • Type 1

Font can be created by postscript name or by family and style names. Successful call of the CreateFont method means that FontRegistry contains the requested font and it’s ready to be used. Otherwise you’ll get a FontMissingException.

float dpi = 72;   
   
// Make an extendable copy of installed font collection   
var fr = new CustomFontRegistry(FontRegistry.Installed);   
   
// By postscript name   
var font = FontRegistry.Installed.CreateFont("Comic", 16, dpi, dpi);   
   
// By family & style   
var secondFont = fr.CreateFont("Comic Sans MS", "Regular", 16, dpi, dpi);   
   
try   
{   
    var failFont = fr.CreateFont("AbsentFont", "AbsentStyle", 16, dpi, dpi);   
}   
catch (FontMissingException ex)   
{   
    // some actions   
}   

Please be sure to use the DPI values which match the target source. There is a DPI validation step during the drawing of text. If the font DPI is not equal to Graphics source DPI you’ll get an exception.

When desired fonts are considered to be completely standard you can create the font through Graphics, without manipulations using FontRegistry:

var font = graphics.CreateFont("Arial", 20);   
var secondFont = graphics.CreateFont("Times New Roman", "Bold", 32);   

In this case Graphics acts as a wrapper around Graphics.FontRegistry. By default this is InstalledFontRegistry, but you can replace it with your own:

var fr = new CustomFontRegistry();   
fr.Add(@"C:\Windows\Fonts\timesbd.ttf");   
   
graphics.FontRegistry = fr;   
   
var font = graphics.CreateFont("Times New Roman", "Bold", 22);   

Path

Path is a container for points, lines and curves. It can be drawn on Graphics, serialized or converted to System.Drawing.Drawing2D.GraphicsPath.

If needed the path can be filled point by point manually:

var path = new Path();   
path.MoveTo(0, 0);   
path.LineTo(40, 40);   
path.CurveTo(80, 70, 160, 20);   

It is possible to form the path from a text object’s outlines. Let’s add some text in the curve we already have in the path. After that, the path will contain both the curve and the outlines of our text on that curve:

var text = new PathText("text in path", FontRegistry.Installed.CreateFont("Arial", 30, dpi, dpi));   
text.Alignment = TextAlignment.Center;   
text.Path = path;   
   
path.DrawText(text);   
   
gr.DrawPath(new Pen(RgbColor.Black), path);   
Text from Path

Now let’s convert it to GraphicsPath:

// System.Drawing   
var gr = bitmap.GetGdiPlusGraphics();   
   
gr.SmoothingMode = рSystem.Drawing.Drawing2D.SmoothingMode.HighQuality;   
gr.DrawPath(new System.Drawing.Pen(RgbColor.Black), path.ToGdiPlusGraphicsPath());   
Text to GraphicsPath

Conversion from GraphicsPath is also possible:

var gp = new System.Drawing.Drawing2D.GraphicsPath();   
   
gp.AddString("Text from GraphicsPath",    
    new System.Drawing.FontFamily("Arial"), 0, 20,    
    new System.Drawing.PointF(10, 10),    
    System.Drawing.StringFormat.GenericDefault);   
   
var path = Path.Create(gp);   
gr.FillPath(new SolidBrush(RgbColor.Black), path);   
Text from GraphicsPath

Samples

Render PSD template to PDF

Assume we need to read all the layers from a PSD file and save it to a PDF with some modification of text layers. Usually such modification means replacement of the template strings to the user’s input, but for simplicity we can just convert them to UPPERCASE.

var psdReader = new PsdReader(psdFilename);   
var pdfWriter = new PdfWriter(pdfFilename);   
   
pdfWriter.AddPage(psdReader.Width, psdReader.Height, psdReader.DpiX, psdReader.DpiY);   
   
var gr = pdfWriter.GetGraphics();   
   
foreach (var frame in psdReader.Frames)   
{   
    if (frame.Type == FrameType.Text)   
    {   
        var text = Text.Create((PsdTextFrame)frame, gr);   
        text.String = text.String.ToUpper();   
        gr.DrawText(text);   
    }   
    else if (frame.Type == FrameType.Raster)   
    {   
        gr.DrawImage(frame, frame.X, frame.Y);   
    }   
}   
   
pdfWriter.Close();   

Dynamically loading fonts from the file system

Here a small code example:

var fr = new CustomFontRegistry();   
   
fr.Add("GreatVibes-Regular.otf");   
   
var font = fr.CreateFont("GreatVibes", "Regular", 30, gr.DpiX, gr.DpiY);   
   
var text = new PlainText(string.Format("{0} - {1}", font.Family, font.Style), font);   
text.Position = new System.Drawing.PointF(10, text.GetBlackBox().Height);   
   
gr.DrawText(text);   
Custom font

Use Adobe resource clipping path

Assume we have an image with an interesting area selected by a clipping path. Now say we need to desaturate all of the image except the specified area. This can be done by using ClippingPaths collection of Graphics class:

Clipping path source image

© Deepak Bhatia (watermark is not readable)

var reader = ImageReader.Create("OrangeWithPath.jpg");   
   
var bitmap = reader.Frames[0].GetBitmap();   
bitmap.ColorAdjustment.Desaturate();   
   
var gr = bitmap.GetAdvancedGraphics();   
   
var clippingPath = reader.ClippingPaths[0];   
var path = Path.Create(clippingPath.CreateGraphicsPath(gr.Width, gr.Height));   
               
gr.ClippingPaths.Add(path);   
   
gr.DrawImage(reader.Frames[0], 0, 0);   
Clipping path processed image