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:
- 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);
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;
}
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);
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);
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);
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);
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);
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);
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());
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:
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);
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());
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);
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);
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:
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);