/*
 * Graphics.cs - Implementation of the "System.Drawing.Graphics" class.
 *
 * Copyright (C) 2003  Southern Storm Software, Pty Ltd.
 * Copyright (C) 2004  Free Software Foundation, Inc.
 * Contributions from Thomas Fritzsche <tf@noto.de>.
 * Contributions from Neil Cawse.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

namespace System.Drawing
{

using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Drawing.Toolkit;

#if !ECMA_COMPAT
[ComVisible(false)]
#endif
public sealed class Graphics : MarshalByRefObject, IDisposable
{
	// Internal state.
	private IToolkitGraphics graphics;
	private TextLayoutManager textLayoutManager;
	private Region clip;
	internal Matrix transform;
	private float pageScale;
	private GraphicsUnit pageUnit;
	internal GraphicsContainer stackTop;
	internal static Graphics defaultGraphics;
	// The window this graphics represents overlying the IToolkitGraphics
	internal Rectangle baseWindow;
	// The current clip in device coordinates - useful when deciding if something
	// is out of bounds, limiting the need to call the toolkit.
	private Rectangle deviceClipExtent;

	// Default DPI for the screen.
	internal const float DefaultScreenDpi = 96.0f;

	// Constructor.
	internal Graphics(IToolkitGraphics graphics)
			{
				this.graphics = graphics;
				this.clip = null;
				this.transform = null;
				this.pageScale = 1.0f;
				this.pageUnit = GraphicsUnit.World;
				this.stackTop = null;
				this.baseWindow = Rectangle.Empty;
			}

	// Window Constructor. Copies the existing Graphics and creates a new
	// Graphics that has an origin of baseWindow.Location and is always clipped
	// to baseWindow

	internal Graphics(IToolkitGraphics graphics, Rectangle baseWindow)
		: this(graphics)
			{
				this.baseWindow = baseWindow;
				clip = new Region(baseWindow);
				clip.Translate(-baseWindow.X, -baseWindow.Y);
				Clip = clip;
			}

	// Create a Graphics that is internally offset to baseWindow
	internal Graphics(Graphics graphics, Rectangle baseWindow)
			{
				// Use the same toolkit
				this.graphics = graphics.graphics;
				if (graphics.clip != null)
				{
					clip = graphics.clip.Clone();
					clip.Intersect(baseWindow);
				}
				else
					clip = new Region(baseWindow);
				// Find out what the clip is with our new Origin
				clip.Translate(-baseWindow.X, -baseWindow.Y);
				this.baseWindow = baseWindow;
				if (graphics.transform != null)
					this.transform = new Matrix(graphics.transform);
				this.pageScale = graphics.pageScale;
				this.pageUnit = graphics.pageUnit;
				this.stackTop = null;
				Clip = clip;
			}


	// Destructor.
	~Graphics()
			{
				Dispose(false);
			}

	// Dispose of this object.
	public void Dispose()
			{
				Dispose(true);
				GC.SuppressFinalize(this);
			}
	private void Dispose(bool disposing)
			{
				lock(this)
				{
					if(graphics != null)
					{
						graphics.Dispose();
						graphics = null;
					}
					if (clip != null)
					{
						clip.Dispose();
						clip = null;
					}
				}
			}

	// Get or set this object's properties.
	public Region Clip
			{
				get
				{
					if(clip == null)
					{
						clip = new Region();
					}
					return clip;
				}
				set
				{
					SetClip(value, CombineMode.Replace);
				}
			}
	public RectangleF ClipBounds
			{
				get
				{
					return Clip.GetBounds(this);
				}
			}
	public CompositingMode CompositingMode
			{
				get
				{
					lock(this)
					{
						if(graphics != null)
						{
							return graphics.CompositingMode;
						}
						else
						{
							return CompositingMode.SourceOver;
						}
					}
				}
				set
				{
					lock(this)
					{
						if(graphics != null)
						{
							graphics.CompositingMode = value;
						}
					}
				}
			}
	public CompositingQuality CompositingQuality
			{
				get
				{
					lock(this)
					{
						if(graphics != null)
						{
							return graphics.CompositingQuality;
						}
						else
						{
							return CompositingQuality.Default;
						}
					}
				}
				set
				{
					lock(this)
					{
						if(graphics != null)
						{
							graphics.CompositingQuality = value;
						}
					}
				}
			}
	public float DpiX
			{
				get
				{
					lock(this)
					{
						if(graphics != null)
						{
							return graphics.DpiX;
						}
						else
						{
							return DefaultScreenDpi;
						}
					}
				}
			}
	public float DpiY
			{
				get
				{
					lock(this)
					{
						if(graphics != null)
						{
							return graphics.DpiY;
						}
						else
						{
							return DefaultScreenDpi;
						}
					}
				}
			}
	public InterpolationMode InterpolationMode
			{
				get
				{
					lock(this)
					{
						if(graphics != null)
						{
							return graphics.InterpolationMode;
						}
						else
						{
							return InterpolationMode.Default;
						}
					}
				}
				set
				{
					lock(this)
					{
						if(graphics != null)
						{
							graphics.InterpolationMode = value;
						}
					}
				}
			}
	public bool IsClipEmpty
			{
				get
				{
					return (clip != null && clip.IsEmpty(this));
				}
			}
	public bool IsVisibleClipEmpty
			{
				get
				{
					RectangleF clip = VisibleClipBounds;
					return (clip.Width <= 0.0f && clip.Height <= 0.0f);
				}
			}
	public float PageScale
			{
				get
				{
					return pageScale;
				}
				set
				{
					pageScale = value;
				}
			}
	public GraphicsUnit PageUnit
			{
				get
				{
					return pageUnit;
				}
				set
				{
					pageUnit = value;
				}
			}
	public PixelOffsetMode PixelOffsetMode
			{
				get
				{
					lock(this)
					{
						if(graphics != null)
						{
							return graphics.PixelOffsetMode;
						}
						else
						{
							return PixelOffsetMode.Default;
						}
					}
				}
				set
				{
					lock(this)
					{
						if(graphics != null)
						{
							graphics.PixelOffsetMode = value;
						}
					}
				}
			}
	public Point RenderingOrigin
			{
				get
				{
					lock(this)
					{
						if(graphics != null)
						{
							return graphics.RenderingOrigin;
						}
						else
						{
							return new Point(0, 0);
						}
					}
				}
				set
				{
					lock(this)
					{
						if(graphics != null)
						{
							graphics.RenderingOrigin = value;
						}
					}
				}
			}
	public SmoothingMode SmoothingMode
			{
				get
				{
					lock(this)
					{
						if(graphics != null)
						{
							return graphics.SmoothingMode;
						}
						else
						{
							return SmoothingMode.Default;
						}
					}
				}
				set
				{
					lock(this)
					{
						if(graphics != null)
						{
							graphics.SmoothingMode = value;
						}
					}
				}
			}
	public int TextContrast
			{
				get
				{
					lock(this)
					{
						if(graphics != null)
						{
							return graphics.TextContrast;
						}
						else
						{
							return 4;
						}
					}
				}
				set
				{
					lock(this)
					{
						if(graphics != null)
						{
							graphics.TextContrast = value;
						}
					}
				}
			}
	public TextRenderingHint TextRenderingHint
			{
				get
				{
					lock(this)
					{
						if(graphics != null)
						{
							return graphics.TextRenderingHint;
						}
						else
						{
							return TextRenderingHint.SystemDefault;
						}
					}
				}
				set
				{
					lock(this)
					{
						if(graphics != null)
						{
							graphics.TextRenderingHint = value;
						}
					}
				}
			}
	public Matrix Transform
			{
				get
				{
					lock(this)
					{
						if(transform == null)
						{
							transform = new Matrix();
						}
						// return a copy instead of the original
						return new Matrix(transform);
					}
				}
				set
				{
					lock(this)
					{
						if(value == null || value.IsIdentity)
						{
							transform = null;
						}
						else
						{
							// Copy the given Matric, do not work on it directly
							transform = new Matrix(value);
						}
					}
				}
			}
	public RectangleF VisibleClipBounds
			{
				get
				{
					if(graphics != null)
					{
						PointF bottomRight = new PointF(baseWindow.Width, baseWindow.Height);
						PointF[] coords = new PointF[] {PointF.Empty, bottomRight };
						TransformPoints(CoordinateSpace.World, CoordinateSpace.Device, coords);
						return RectangleF.FromLTRB(coords[0].X, coords[0].Y, coords[1].X, coords[1].Y);
					}
					else
					{
						return RectangleF.Empty;
					}
				}
			}

	// Add a metafile comment.
	public void AddMetafileComment(byte[] data)
			{
				lock(this)
				{
					if(graphics != null)
					{
						graphics.AddMetafileComment(data);
					}
				}
			}

	// Save the current contents of the graphics context in a container.
	public GraphicsContainer BeginContainer()
			{
				lock(this)
				{
					return new GraphicsContainer(this);
				}
			}
	public GraphicsContainer BeginContainer(Rectangle dstRect,
											Rectangle srcRect,
											GraphicsUnit unit)
			{
				return BeginContainer((RectangleF)dstRect,
									  (RectangleF)srcRect, unit);
			}
	public GraphicsContainer BeginContainer(RectangleF dstRect,
											RectangleF srcRect,
											GraphicsUnit unit)
			{
				// Save the current state of the context.
				GraphicsContainer container = BeginContainer();

				// Modify the context information appropriately.
				// TODO

				// Return the saved information to the caller.
				return container;
			}

	// Clear the entire drawing surface.
	public void Clear(Color color)
			{
				using (Brush brush = new SolidBrush(color))
				{
					FillRectangle(brush, ClipBounds);
				}
			}

	// Draw an arc.
	public void DrawArc(Pen pen, Rectangle rect,
						float startAngle, float sweepAngle)
			{
				DrawArc(pen, rect.X, rect.Y, rect.Width, rect.Height,
						startAngle, sweepAngle);
			}
	public void DrawArc(Pen pen, RectangleF rect,
						float startAngle, float sweepAngle)
			{
				DrawArc(pen, rect.X, rect.Y, rect.Width, rect.Height,
						startAngle, sweepAngle);
			}
	public void DrawArc(Pen pen, int x, int y, int width, int height,
						float startAngle, float sweepAngle)
			{
				Point[] rect = ConvertRectangle(x + baseWindow.X, y + baseWindow.Y, width, height, pageUnit);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawArc(rect, startAngle, sweepAngle);
				}
			}
	public void DrawArc(Pen pen, float x, float y, float width, float height,
						float startAngle, float sweepAngle)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				Point[] rect = ConvertRectangle(x + baseWindow.X, y + baseWindow.Y, width, height, pageUnit);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawArc(rect, startAngle, sweepAngle);
				}
			}

	// Draw a Bezier spline.
	public void DrawBezier(Pen pen, Point pt1, Point pt2,
						   Point pt3, Point pt4)
			{
				DrawBezier(pen, (float)(pt1.X), (float)(pt1.Y),
						   (float)(pt2.X), (float)(pt2.Y),
						   (float)(pt3.X), (float)(pt3.Y),
						   (float)(pt4.X), (float)(pt4.Y));
			}
	public void DrawBezier(Pen pen, PointF pt1, PointF pt2,
						   PointF pt3, PointF pt4)
			{
				DrawBezier(pen, pt1.X, pt1.Y, pt2.X, pt2.Y,
						   pt3.X, pt3.Y, pt4.X, pt4.Y);
			}
	public void DrawBezier(Pen pen, float x1, float y1, float x2, float y2,
						   float x3, float y3, float x4, float y4)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				int dx1, dy1, dx2, dy2;
				int dx3, dy3, dx4, dy4;
				ConvertPoint(x1 + baseWindow.X, y1 + baseWindow.Y, out dx1, out dy1, pageUnit);
				ConvertPoint(x2 + baseWindow.X, y2 + baseWindow.Y, out dx2, out dy2, pageUnit);
				ConvertPoint(x3 + baseWindow.X, y3 + baseWindow.Y, out dx3, out dy3, pageUnit);
				ConvertPoint(x4 + baseWindow.X, y4 + baseWindow.Y, out dx4, out dy4, pageUnit);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawBezier(dx1, dy1, dx2, dy2,
											   dx3, dy3, dx4, dy4);
				}
			}

	// Draw a Bezier spline.
			public void FillBezier(Brush brush, Point pt1, Point pt2,
														 Point pt3, Point pt4, FillMode fillMode)
			{
				FillBezier(brush, (float)(pt1.X), (float)(pt1.Y),
									 (float)(pt2.X), (float)(pt2.Y),
									 (float)(pt3.X), (float)(pt3.Y),
									 (float)(pt4.X), (float)(pt4.Y), fillMode);
			}
			public void FillBezier(Brush brush, PointF pt1, PointF pt2,
														 PointF pt3, PointF pt4, FillMode fillMode)
			{
				FillBezier(brush, pt1.X, pt1.Y, pt2.X, pt2.Y,
									 pt3.X, pt3.Y, pt4.X, pt4.Y, fillMode);
			}
			public void FillBezier(Brush brush, float x1, float y1, float x2, float y2,
														 float x3, float y3, float x4, float y4, FillMode fillMode)
			{

				int dx1, dy1, dx2, dy2;
				int dx3, dy3, dx4, dy4;
				ConvertPoint(x1 + baseWindow.X, y1 + baseWindow.Y, out dx1, out dy1, pageUnit);
				ConvertPoint(x2 + baseWindow.X, y2 + baseWindow.Y, out dx2, out dy2, pageUnit);
				ConvertPoint(x3 + baseWindow.X, y3 + baseWindow.Y, out dx3, out dy3, pageUnit);
				ConvertPoint(x4 + baseWindow.X, y4 + baseWindow.Y, out dx4, out dy4, pageUnit);
				lock(this)
				{
					SelectBrush(brush);
					ToolkitGraphics.DrawBezier(dx1, dy1, dx2, dy2,
																		 dx3, dy3, dx4, dy4);
					ToolkitGraphics.FillBezier(dx1, dy1, dx2, dy2,
																		 dx3, dy3, dx4, dy4, fillMode);
				}
			}

			
	// Draw a series of Bezier splines.
	public void DrawBeziers(Pen pen, Point[] points)
			{
				if(pen == null)
				{
					throw new ArgumentNullException("pen");
				}
				if(points == null)
				{
					throw new ArgumentNullException("points");
				}
				int posn = 0;
				while((posn + 4) <= points.Length)
				{
					DrawBezier(pen, points[posn], points[posn + 1],
							   points[posn + 2], points[posn + 3]);
					posn += 3;
				}
			}
	public void DrawBeziers(Pen pen, PointF[] points)
			{
				if(pen == null)
				{
					throw new ArgumentNullException("pen");
				}
				if(points == null)
				{
					throw new ArgumentNullException("points");
				}
				int posn = 0;
				while((posn + 4) <= points.Length)
				{
					DrawBezier(pen, points[posn], points[posn + 1],
							   points[posn + 2], points[posn + 3]);
					posn += 3;
				}
			}

	// Draw a closed cardinal spline.
	public void DrawClosedCurve(Pen pen, Point[] points)
			{
				DrawClosedCurve(pen, points, 0.5f, FillMode.Alternate);
			}
	public void DrawClosedCurve(Pen pen, PointF[] points)
			{
				DrawClosedCurve(pen, points, 0.5f, FillMode.Alternate);
			}
	public void DrawClosedCurve(Pen pen, Point[] points,
								float tension, FillMode fillMode)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				points = ConvertPoints(points, 4, pageUnit);
				BaseOffsetPoints(points);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawClosedCurve(points, tension);
				}
			}
	public void DrawClosedCurve(Pen pen, PointF[] points,
								float tension, FillMode fillMode)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				Point[] dpoints = ConvertPoints(points, 4, pageUnit);
				BaseOffsetPoints(dpoints);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawClosedCurve(dpoints, tension);
				}
			}

	// Draw a cardinal spline.
	public void DrawCurve(Pen pen, Point[] points)
			{
				if(points == null)
				{
					throw new ArgumentNullException("points");
				}
				DrawCurve(pen, points, 0, points.Length - 1, 0.5f);
			}
	public void DrawCurve(Pen pen, PointF[] points)
			{
				if(points == null)
				{
					throw new ArgumentNullException("points");
				}
				DrawCurve(pen, points, 0, points.Length - 1, 0.5f);
			}
	public void DrawCurve(Pen pen, Point[] points, float tension)
			{
				if(points == null)
				{
					throw new ArgumentNullException("points");
				}
				DrawCurve(pen, points, 0, points.Length - 1, tension);
			}
	public void DrawCurve(Pen pen, PointF[] points, float tension)
			{
				if(points == null)
				{
					throw new ArgumentNullException("points");
				}
				DrawCurve(pen, points, 0, points.Length - 1, tension);
			}
	public void DrawCurve(Pen pen, Point[] points,
						  int offset, int numberOfSegments)
			{
				DrawCurve(pen, points, offset, numberOfSegments, 0.5f);
			}
	public void DrawCurve(Pen pen, PointF[] points,
						  int offset, int numberOfSegments)
			{
				DrawCurve(pen, points, offset, numberOfSegments, 0.5f);
			}
	public void DrawCurve(Pen pen, Point[] points,
						  int offset, int numberOfSegments, float tension)
			{
				points = ConvertPoints(points, 4, pageUnit);
				BaseOffsetPoints(points);
				if(offset < 0 || offset >= (points.Length - 1))
				{
					throw new ArgumentOutOfRangeException
						("offset", S._("Arg_InvalidCurveOffset"));
				}
				if(numberOfSegments < 1 ||
				   (offset + numberOfSegments) >= points.Length)
				{
					throw new ArgumentOutOfRangeException
						("numberOfSegments", S._("Arg_InvalidCurveSegments"));
				}

				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}


				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawCurve
						(points, offset, numberOfSegments, tension);
				}
			}
	public void DrawCurve(Pen pen, PointF[] points,
						  int offset, int numberOfSegments, float tension)
			{
				Point[] dpoints = ConvertPoints(points, 4, pageUnit);
				BaseOffsetPoints(dpoints);
				if(offset < 0 || offset >= (points.Length - 1))
				{
					throw new ArgumentOutOfRangeException
						("offset", S._("Arg_InvalidCurveOffset"));
				}
				if(numberOfSegments < 1 ||
				   (offset + numberOfSegments) >= points.Length)
				{
					throw new ArgumentOutOfRangeException
						("numberOfSegments", S._("Arg_InvalidCurveSegments"));
				}

				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawCurve
						(dpoints, offset, numberOfSegments, tension);
				}
			}

	// Draw an ellipse.
	public void DrawEllipse(Pen pen, Rectangle rect)
			{
				DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height);
			}
	public void DrawEllipse(Pen pen, RectangleF rect)
			{
				DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height);
			}
	public void DrawEllipse(Pen pen, int x, int y, int width, int height)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				Point[] rect = ConvertRectangle(x + baseWindow.X, y + baseWindow.Y, width, height, pageUnit);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawArc(rect, 0.0f, 360.0f);
				}
			}
	public void DrawEllipse(Pen pen, float x, float y,
							float width, float height)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				Point[] rect = ConvertRectangle(x + baseWindow.X, y + baseWindow.Y, width, height, pageUnit);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawArc(rect, 0.0f, 360.0f);
				}
			}

	// Draw an unstretched complete icon via the toolkit.
	private void ToolkitDrawIcon(Icon icon, int x, int y)
			{
				lock(this)
				{
					IToolkitImage toolkitImage = icon.GetToolkitImage(this);
					if(toolkitImage != null)
					{
						ConvertPoint(ref x, ref y, pageUnit);
						ToolkitGraphics.DrawImage
							(toolkitImage, x + baseWindow.X,
							 y + baseWindow.Y);
					}
				}
			}

	// Draw an icon.
	public void DrawIcon(Icon icon, Rectangle targetRect)
			{
				if(icon == null)
				{
					throw new ArgumentNullException("icon");
				}
				if(targetRect.Width == icon.Width &&
				   targetRect.Height == icon.Height)
				{
					// This is the easy case.
					ToolkitDrawIcon(icon, targetRect.X, targetRect.Y);
				}
				else
				{
					// Stretch and draw the image.
					Point[] src = ConvertRectangle3
						(0, 0, icon.Width, icon.Height, GraphicsUnit.Pixel);
					Point[] dest = ConvertRectangle3
						(targetRect.X, targetRect.Y,
						 targetRect.Width, targetRect.Height, pageUnit);
					BaseOffsetPoints(dest);
					lock(this)
					{
						IToolkitImage toolkitImage = icon.GetToolkitImage(this);
						if(toolkitImage != null)
						{
							ToolkitGraphics.DrawImage
								(toolkitImage, src, dest);
						}
					}
				}
			}
	public void DrawIconUnstretched(Icon icon, Rectangle targetRect)
			{
				if(icon == null)
				{
					throw new ArgumentNullException("icon");
				}
				int width = icon.Width;
				int height = icon.Height;
				if(width <= targetRect.Width && height <= targetRect.Height)
				{
					// No truncation is necessary, so use the easy case.
					ToolkitDrawIcon(icon, targetRect.X, targetRect.Y);
				}
				else
				{
					// Only draw as much as will fit in the target rectangle.
					if(width > targetRect.Width)
					{
						width = targetRect.Width;
					}
					if(height > targetRect.Height)
					{
						height = targetRect.Height;
					}
					Point[] src = ConvertRectangle3
						(0, 0, width, height, GraphicsUnit.Pixel);
					Point[] dest = ConvertRectangle3
						(targetRect.X, targetRect.Y, width, height, pageUnit);
					BaseOffsetPoints(dest);
					lock(this)
					{
						IToolkitImage toolkitImage = icon.GetToolkitImage(this);
						if(toolkitImage != null)
						{
							ToolkitGraphics.DrawImage
								(toolkitImage, src, dest);
						}
					}
				}
			}
	public void DrawIcon(Icon icon, int x, int y)
			{
				if(icon == null)
				{
					throw new ArgumentNullException("icon");
				}
				ToolkitDrawIcon(icon, x, y);
			}

	// Used to call the toolkit DrawImage methods
	private void ToolkitDrawImage(Image image, Point[] src, Point[] dest)
			{
				if (image.toolkitImage == null)
					image.toolkitImage = ToolkitGraphics.Toolkit.CreateImage(image.dgImage, 0);
				BaseOffsetPoints(dest);
				ToolkitGraphics.DrawImage(image.toolkitImage,src, dest);
			}

	private void ToolkitDrawImage(Image image, int x, int y)
			{
				if (image.toolkitImage == null)
					image.toolkitImage = ToolkitGraphics.Toolkit.CreateImage(image.dgImage, 0);
				ToolkitGraphics.DrawImage(image.toolkitImage, x + baseWindow.X, y + baseWindow.Y);
			}

	// Draw an image.
	public void DrawImage(Image image, Point point)
			{
				DrawImage(image, point.X, point.Y);
			}

	public void DrawImage(Image image, Point[] destPoints)
			{
				Point[] src = ConvertRectangle3(0, 0, image.Width,  image.Height, GraphicsUnit.Pixel);
				Point[] dest = ConvertPoints(destPoints, 3, pageUnit);
				ToolkitDrawImage(image, src, dest);
			}

	public void DrawImage(Image image, PointF point)
			{
				DrawImage(image, point.X, point.Y);
			}

	public void DrawImage(Image image, PointF[] destPoints)
			{
				Point[] src = ConvertRectangle3(0, 0, image.Width,  image.Height, GraphicsUnit.Pixel);
				Point[] dest = ConvertPoints(destPoints, 3, pageUnit);
				ToolkitDrawImage(image, src, dest);
			}

	public void DrawImage(Image image, Rectangle rect)
			{
				Point[] src = ConvertRectangle3(0, 0, image.Width,  image.Height, GraphicsUnit.Pixel);
				Point[] dest = ConvertRectangle3(rect.X, rect.Y, rect.Width, rect.Height, pageUnit);
				ToolkitDrawImage(image, src, dest);
			}

	public void DrawImage(Image image, RectangleF rect)
			{
				Point[] src = ConvertRectangle3(0, 0, image.Width,  image.Height, GraphicsUnit.Pixel);
				Point[] dest = ConvertRectangle3(rect.X, rect.Y, rect.Width, rect.Height, pageUnit);
				ToolkitDrawImage(image, src, dest);
			}

	public void DrawImage(Image image, int x, int y)
			{
				lock(this)
				{
					ConvertPoint(ref x, ref y, pageUnit);
					ToolkitDrawImage(image, x, y);
				}
			}

	public void DrawImage(Image image, float x, float y)
			{
				int dx, dy;
				ConvertPoint(x, y, out dx, out dy, pageUnit);
				ToolkitDrawImage(image, dx, dy);
			}

	public void DrawImage(Image image, Point[] destPoints,
						  Rectangle srcRect, GraphicsUnit srcUnit)
			{
				Point[] src = ConvertRectangle3(srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit);
				Point[] dest = ConvertPoints(destPoints, 3, pageUnit);
				ToolkitDrawImage(image, src, dest);
			}
	
	public void DrawImage(Image image, PointF[] destPoints,
						  RectangleF srcRect, GraphicsUnit srcUnit)
			{
				Point[] src = ConvertRectangle3(srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit);
				Point[] dest = ConvertPoints(destPoints, 3, pageUnit);
				ToolkitDrawImage(image, src, dest);
			}
	
	public void DrawImage(Image image, Rectangle destRect,
						  Rectangle srcRect, GraphicsUnit srcUnit)
			{
				Point[] src = ConvertRectangle3(srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit);
				Point[] dest = ConvertRectangle3(destRect.X, destRect.Y, destRect.Width, destRect.Height, pageUnit);
				ToolkitDrawImage(image, src, dest);
			}
	
	public void DrawImage(Image image, RectangleF destRect,
						  RectangleF srcRect, GraphicsUnit srcUnit)
			{
				Point[] src = ConvertRectangle3(srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit);
				Point[] dest = ConvertRectangle3(destRect.X, destRect.Y, destRect.Width, destRect.Height, pageUnit);
				ToolkitDrawImage(image, src, dest);
			}
	public void DrawImage(Image image, int x, int y, int width, int height)
			{
				Point[] src = ConvertRectangle3(0, 0, image.Width,  image.Height, GraphicsUnit.Pixel);
				Point[] dest = ConvertRectangle3(x, y, width, height, pageUnit);
				ToolkitDrawImage(image, src, dest);
			}
	public void DrawImage(Image image, int x, int y,
						  Rectangle srcRect, GraphicsUnit srcUnit)
			{
				Point[] src = ConvertRectangle3(srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit);
				Point[] dest = ConvertRectangle3(x, y, 0, 0, pageUnit);
				dest[1].X = image.Width;
				dest[2].Y = image.Height;
				ToolkitDrawImage(image, src, dest);
			}
	public void DrawImage(Image image, float x, float y,
						  RectangleF srcRect, GraphicsUnit srcUnit)
			{
				Point[] src = ConvertRectangle3(srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit);
				Point[] dest = ConvertRectangle3(x, y, srcRect.Width, srcRect.Height, pageUnit);
				ToolkitDrawImage(image, src, dest);
			}
	public void DrawImage(Image image, float x, float y,
						  float width, float height)
			{
				Point[] src = ConvertRectangle3(0, 0, image.Width,  image.Height, GraphicsUnit.Pixel);
				Point[] dest = ConvertRectangle3(x, y, width, height, pageUnit);
				ToolkitDrawImage(image, src, dest);
			}
	public void DrawImage(Image image, Rectangle destRect,
						  int srcX, int srcY, int srcWidth, int srcHeight,
						  GraphicsUnit srcUnit)
			{
				Point[] src = ConvertRectangle3(srcX, srcY, srcWidth, srcHeight, srcUnit);
				Point[] dest = ConvertRectangle3(destRect.X, destRect.Y, destRect.Width, destRect.Height, pageUnit);
				ToolkitDrawImage(image, src, dest);
			}
	
	public void DrawImage(Image image, Rectangle destRect,
						  float srcX, float srcY,
						  float srcWidth, float srcHeight,
						  GraphicsUnit srcUnit)
			{
				Point[] src = ConvertRectangle3(srcX, srcY, srcWidth, srcHeight, srcUnit);
				Point[] dest = ConvertRectangle3(destRect.X, destRect.Y, destRect.Width, destRect.Height, pageUnit);
				ToolkitDrawImage(image, src, dest);
			}

	// We don't use image attributes or image abort procedures in this
	// implementation, so stub out the remaining DrawImage methods.
	public void DrawImage(Image image, Point[] destPoints,
						  Rectangle srcRect, GraphicsUnit srcUnit,
						  ImageAttributes imageAttr)
			{
				DrawImage(image, destPoints, srcRect, srcUnit);
			}
	public void DrawImage(Image image, PointF[] destPoints,
						  RectangleF srcRect, GraphicsUnit srcUnit,
						  ImageAttributes imageAttr)
			{
				DrawImage(image, destPoints, srcRect, srcUnit);
			}
	public void DrawImage(Image image, Point[] destPoints,
						  Rectangle srcRect, GraphicsUnit srcUnit,
						  ImageAttributes imageAttr,
						  DrawImageAbort callback)
			{
				DrawImage(image, destPoints, srcRect, srcUnit);
			}
	public void DrawImage(Image image, PointF[] destPoints,
						  RectangleF srcRect, GraphicsUnit srcUnit,
						  ImageAttributes imageAttr,
						  DrawImageAbort callback)
			{
				DrawImage(image, destPoints, srcRect, srcUnit);
			}
	public void DrawImage(Image image, Point[] destPoints,
						  Rectangle srcRect, GraphicsUnit srcUnit,
						  ImageAttributes imageAttr,
						  DrawImageAbort callback, int callbackdata)
			{
				DrawImage(image, destPoints, srcRect, srcUnit);
			}
	public void DrawImage(Image image, PointF[] destPoints,
						  RectangleF srcRect, GraphicsUnit srcUnit,
						  ImageAttributes imageAttr,
						  DrawImageAbort callback, int callbackdata)
			{
				DrawImage(image, destPoints, srcRect, srcUnit);
			}
	public void DrawImage(Image image, Rectangle destRect,
						  int srcX, int srcY, int srcWidth, int srcHeight,
						  GraphicsUnit srcUnit, ImageAttributes imageAttr)
			{
				DrawImage(image, destRect, srcX, srcY, srcWidth,
						  srcHeight, srcUnit);
			}
	public void DrawImage(Image image, Rectangle destRect,
						  float srcX, float srcY,
						  float srcWidth, float srcHeight,
						  GraphicsUnit srcUnit, ImageAttributes imageAttr)
			{
				DrawImage(image, destRect, srcX, srcY, srcWidth,
						  srcHeight, srcUnit);
			}
	public void DrawImage(Image image, Rectangle destRect,
						  int srcX, int srcY, int srcWidth, int srcHeight,
						  GraphicsUnit srcUnit, ImageAttributes imageAttr,
						  DrawImageAbort callback)
			{
				DrawImage(image, destRect, srcX, srcY, srcWidth,
						  srcHeight, srcUnit);
			}
	public void DrawImage(Image image, Rectangle destRect,
						  float srcX, float srcY,
						  float srcWidth, float srcHeight,
						  GraphicsUnit srcUnit, ImageAttributes imageAttr,
						  DrawImageAbort callback)
			{
				DrawImage(image, destRect, srcX, srcY, srcWidth,
						  srcHeight, srcUnit);
			}
	public void DrawImage(Image image, Rectangle destRect,
						  int srcX, int srcY, int srcWidth, int srcHeight,
						  GraphicsUnit srcUnit, ImageAttributes imageAttr,
						  DrawImageAbort callback, IntPtr callbackData)
			{
				DrawImage(image, destRect, srcX, srcY, srcWidth,
						  srcHeight, srcUnit);
			}
	public void DrawImage(Image image, Rectangle destRect,
						  float srcX, float srcY,
						  float srcWidth, float srcHeight,
						  GraphicsUnit srcUnit, ImageAttributes imageAttr,
						  DrawImageAbort callback, IntPtr callbackData)
			{
				DrawImage(image, destRect, srcX, srcY, srcWidth,
						  srcHeight, srcUnit);
			}

	// Draw an unscaled image.
	public void DrawImageUnscaled(Image image, Point point)
			{
				DrawImageUnscaled(image, point.X, point.Y);
			}
	public void DrawImageUnscaled(Image image, Rectangle rect)
			{
				DrawImageUnscaled(image, rect.X, rect.Y);
			}
	public void DrawImageUnscaled(Image image, int x, int y,
								  int width, int height)
			{
				DrawImageUnscaled(image, x, y);
			}
	public void DrawImageUnscaled(Image image, int x, int y)
			{
				DrawImage(image, x, y);
			}

	// Draw a line between two points.
	public void DrawLine(Pen pen, Point pt1, Point pt2)
			{
				DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y);
			}
	public void DrawLine(Pen pen, PointF pt1, PointF pt2)
			{
				DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y);
			}
	public void DrawLine(Pen pen, int x1, int y1, int x2, int y2)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				ConvertPoint(ref x1, ref y1, pageUnit);
				ConvertPoint(ref x2, ref y2, pageUnit);
				if (x1 == x2 && y1 == y2)
					return;
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawLine(x1 + baseWindow.X, y1 + baseWindow.Y,
						x2 + baseWindow.X, y2+baseWindow.Y);
				}
			}
	public void DrawLine(Pen pen, float x1, float y1, float x2, float y2)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				int dx1, dy1, dx2, dy2;
				ConvertPoint(x1, y1, out dx1, out dy1, pageUnit);
				ConvertPoint(x2, y2, out dx2, out dy2, pageUnit);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawLine(dx1 + baseWindow.X, dy1 + baseWindow.Y,
						dx2 + baseWindow.X, dy2 + baseWindow.Y);
				}
			}

	// Draw a series of connected line segments.
	public void DrawLines(Pen pen, Point[] points)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				points = ConvertPoints(points, 2, pageUnit);
				BaseOffsetPoints(points);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawLines(points);
				}
			}
	public void DrawLines(Pen pen, PointF[] points)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				Point[] dpoints = ConvertPoints(points, 2, pageUnit);
				BaseOffsetPoints(dpoints);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawLines(dpoints);
				}
			}

	// Draw a path object.
	public void DrawPath(Pen pen, GraphicsPath path)
			{
				if(pen == null)
				{
					throw new ArgumentNullException("pen");
				}
				if(path == null)
				{
					throw new ArgumentNullException("path");
				}

				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				path.Draw(this, pen);
			}

	// Draw a pie shape.
	public void DrawPie(Pen pen, Rectangle rect,
						float startAngle, float sweepAngle)
			{
				if(((float)(int)startAngle) == startAngle &&
				   ((float)(int)sweepAngle) == sweepAngle)
				{
					DrawPie(pen, rect.X, rect.Y, rect.Width, rect.Height,
							(int)startAngle, (int)sweepAngle);
				}
				else
				{
					DrawPie(pen, rect.X, rect.Y, rect.Width, rect.Height,
							startAngle, sweepAngle);
				}
			}
	public void DrawPie(Pen pen, RectangleF rect,
						float startAngle, float sweepAngle)
			{
				DrawPie(pen, rect.X, rect.Y, rect.Width, rect.Height,
						startAngle, sweepAngle);
			}
	public void DrawPie(Pen pen, int x, int y, int width, int height,
						int startAngle, int sweepAngle)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				Point[] rect = ConvertRectangle(x + baseWindow.X, y + baseWindow.Y, width, height, pageUnit);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawPie(rect, startAngle, sweepAngle);
				}
			}
	public void DrawPie(Pen pen, float x, float y, float width, float height,
						float startAngle, float sweepAngle)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				Point[] rect = ConvertRectangle(x + baseWindow.X, y + baseWindow.Y, width, height, pageUnit);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawPie(rect, startAngle, sweepAngle);
				}
			}

	// Draw a polygon.
	public void DrawPolygon(Pen pen, Point[] points)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				points = ConvertPoints(points, 2, pageUnit);
				BaseOffsetPoints(points);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawPolygon(points);
				}
			}
	public void DrawPolygon(Pen pen, PointF[] points)
			{
				// Bail out now if there's nothing to draw.
				if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
				{
					return;
				}

				Point[] dpoints = ConvertPoints(points, 2, pageUnit);
				BaseOffsetPoints(dpoints);
				lock(this)
				{
					SelectPen(pen);
					ToolkitGraphics.DrawPolygon(dpoints);
				}
			}

	// Draw a rectangle.
	public void DrawRectangle(Pen pen, Rectangle rect)
			{
				DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height);
			}
	public void DrawRectangle(Pen pen, int x, int y, int width, int height)
			{
				if (width>0 && height>0)
				{
					// Bail out now if there's nothing to draw.
					if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
					{
						return;
					}

					lock(this)
					{
						SelectPen(pen);
						ToolkitGraphics.DrawPolygon(ConvertRectangle(x + baseWindow.X, y + baseWindow.Y, width, height, pageUnit));
					}
				}
			}
	public void DrawRectangle(Pen pen, float x, float y,
							  float width, float height)
			{
				if (width>0 && height>0)
				{
					// Bail out now if there's nothing to draw.
					if(pen.PenType == PenType.SolidColor && pen.Color.A == 0)
					{
						return;
					}

					lock(this)
					{
						SelectPen(pen);
						ToolkitGraphics.DrawPolygon(ConvertRectangle(x + baseWindow.X, y + baseWindow.Y, width, height, pageUnit));
					}
				}
			}

	// Draw a series of rectangles.
	public void DrawRectangles(Pen pen, Rectangle[] rects)
			{
				if(rects == null)
				{
					throw new ArgumentNullException("rects");
				}
				int posn;
				for(posn = 0; posn < rects.Length; ++posn)
				{
					DrawRectangle(pen, rects[posn].X, rects[posn].Y,
								  rects[posn].Width, rects[posn].Height);
				}
			}
	public void DrawRectangles(Pen pen, RectangleF[] rects)
			{
				if(rects == null)
				{
					throw new ArgumentNullException("rects");
				}
				int posn;
				for(posn = 0; posn < rects.Length; ++posn)
				{
					DrawRectangle(pen, rects[posn].X, rects[posn].Y,
								  rects[posn].Width, rects[posn].Height);
				}
			}

	// Draw a string.
	public void DrawString(String s, Font font, Brush brush, PointF point)
			{
				DrawString(s, font, brush, point.X, point.Y, null);
			}
	public void DrawString(String s, Font font, Brush brush,
						   RectangleF layoutRectangle)
			{
				DrawString(s, font, brush, layoutRectangle, null);
			}
	public void DrawString(String s, Font font, Brush brush,
						   PointF point, StringFormat format)
			{
				DrawString(s, font, brush, point.X, point.Y, format);
			}
	public void DrawString(String s, Font font, Brush brush,
						   RectangleF layoutRectangle, StringFormat format)
			{
				// bail out now if there's nothing to draw
				if((brush is SolidBrush) && ((SolidBrush)brush).Color.A == 0)
				{
					return;
				}

				// bail out now if there's nothing to draw
				if(((Object)s) == null || s.Length == 0) { return; }

				// make a little inset around the text dependent of the font size
				layoutRectangle.Inflate((int)(-font.SizeInPoints*DpiX/369.7184), 0);

				// convert the layout into device coordinates
				Point[] rect = ConvertRectangle
					((layoutRectangle.X + baseWindow.X),
					 (layoutRectangle.Y + baseWindow.Y),
					 (layoutRectangle.Width - 1),
					 (layoutRectangle.Height - 1),
					 pageUnit);

				// create a layout rectangle from the device coordinates
				Rectangle deviceLayout = new Rectangle
					(rect[0].X, rect[0].Y,
					 (rect[1].X - rect[0].X + 1),
					 (rect[2].Y - rect[0].Y + 1));

				// bail out now if there's nothing to draw
				if(clip != null &&
				   !deviceClipExtent.IntersectsWith(deviceLayout))
				{
					return;
				}

				// ensure we have a text layout manager
				if(textLayoutManager == null)
				{
					textLayoutManager = new TextLayoutManager();
				}

				// set the default temporary clip
				Region clipTemp = null;

				// draw the text
				lock(this)
				{
					// get the clipping region, if needed
					if(format == null ||
					   ((format.FormatFlags & StringFormatFlags.NoClip) == 0))
					{
						// get the clipping region, if there is one
						if(clip != null)
						{
							// get a copy of the current clipping region
							clipTemp = clip.Clone();

							// interset the clipping region with the layout
							SetClip(layoutRectangle, CombineMode.Intersect);
						}
					}

					// attempt to draw the text
					try
					{
						// draw the text
						textLayoutManager.Draw
							(this, s, font, deviceLayout, format, brush);
					}
					finally
					{
						// reset the clip
						if(clipTemp != null) { Clip = clipTemp; }
					}
				}
			}

	public void DrawString(String s, Font font, Brush brush, float x, float y)
			{
				DrawString(s, font, brush, x, y, null);
			}

	public void DrawString(String s, Font font, Brush brush,
						   float x, float y, StringFormat format)
			{
				DrawString(s, font, brush, new RectangleF(x, y, 999999.0f, 999999.0f), format);
			}

	// Reset the graphics state back to a previous container level.
	public void EndContainer(GraphicsContainer container)
			{
				if(container != null)
				{
					lock(this)
					{
						container.Restore(this);
					}
				}
			}

	// Enumerate the contents of a metafile.
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Point destPoint,
								  EnumerateMetafileProc callback)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Point[] destPoints,
								  EnumerateMetafileProc callback)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, PointF destPoint,
								  EnumerateMetafileProc callback)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, PointF[] destPoints,
								  EnumerateMetafileProc callback)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Rectangle destRect,
								  EnumerateMetafileProc callback)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, RectangleF destRect,
								  EnumerateMetafileProc callback)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Point destPoint,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Point[] destPoints,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, PointF destPoint,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, PointF[] destPoints,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Rectangle destRect,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, RectangleF destRect,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Point destPoint,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData,
								  ImageAttributes imageAttr)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Point[] destPoints,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData,
								  ImageAttributes imageAttr)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, PointF destPoint,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData,
								  ImageAttributes imageAttr)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, PointF[] destPoints,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData,
								  ImageAttributes imageAttr)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Rectangle destRect,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData,
								  ImageAttributes imageAttr)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, RectangleF destRect,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData,
								  ImageAttributes imageAttr)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Point destPoint,
								  Rectangle srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Point[] destPoints,
								  Rectangle srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, PointF destPoint,
								  RectangleF srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, PointF[] destPoints,
								  RectangleF srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Rectangle destRect,
								  Rectangle srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, RectangleF destRect,
								  RectangleF srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Point destPoint,
								  Rectangle srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Point[] destPoints,
								  Rectangle srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, PointF destPoint,
								  RectangleF srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, PointF[] destPoints,
								  RectangleF srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Rectangle destRect,
								  Rectangle srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, RectangleF destRect,
								  RectangleF srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Point destPoint,
								  Rectangle srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData,
								  ImageAttributes imageAttr)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Point[] destPoints,
								  Rectangle srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData,
								  ImageAttributes imageAttr)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, PointF destPoint,
								  RectangleF srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData,
								  ImageAttributes imageAttr)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, PointF[] destPoints,
								  RectangleF srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData,
								  ImageAttributes imageAttr)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, Rectangle destRect,
								  Rectangle srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData,
								  ImageAttributes imageAttr)
			{
				// TODO
			}
	[TODO]
	public void EnumerateMetafile(Metafile metafile, RectangleF destRect,
								  RectangleF srcRect, GraphicsUnit srcUnit,
								  EnumerateMetafileProc callback,
								  IntPtr callbackData,
								  ImageAttributes imageAttr)
			{
				// TODO
			}

	// Update the clipping region to exclude a particular rectangle.
	public void ExcludeClip(Rectangle rect)
			{
				Clip.Exclude(rect);
				UpdateClip();
			}
	public void ExcludeClip(Region region)
			{
				Clip.Exclude(region);
				UpdateClip();
			}

	// Fill a closed cardinal spline.
	public void FillClosedCurve(Brush brush, Point[] points)
			{
				FillClosedCurve(brush, points, FillMode.Alternate, 0.5f);
			}
	public void FillClosedCurve(Brush brush, PointF[] points)
			{
				FillClosedCurve(brush, points, FillMode.Alternate, 0.5f);
			}
	public void FillClosedCurve(Brush brush, Point[] points,
								FillMode fillMode)
			{
				FillClosedCurve(brush, points, fillMode, 0.5f);
			}
	public void FillClosedCurve(Brush brush, PointF[] points,
								FillMode fillMode)
			{
				FillClosedCurve(brush, points, fillMode, 0.5f);
			}
	public void FillClosedCurve(Brush brush, Point[] points,
								FillMode fillMode, float tension)
			{
				// Bail out now if there's nothing to draw.
				if((brush is SolidBrush) && ((SolidBrush)brush).Color.A == 0)
				{
					return;
				}

				points = ConvertPoints(points, 4, pageUnit);
				BaseOffsetPoints(points);
				lock(this)
				{
					SelectBrush(brush);
					ToolkitGraphics.FillClosedCurve
						(points, tension, fillMode);
				}
			}
	public void FillClosedCurve(Brush brush, PointF[] points,
								FillMode fillMode, float tension)
			{
				// Bail out now if there's nothing to draw.
				if((brush is SolidBrush) && ((SolidBrush)brush).Color.A == 0)
				{
					return;
				}

				Point[] dpoints = ConvertPoints(points, 4, pageUnit);
				BaseOffsetPoints(dpoints);
				lock(this)
				{
					SelectBrush(brush);
					ToolkitGraphics.FillClosedCurve
						(dpoints, tension, fillMode);
				}
			}

	// Fill an ellipse.
	public void FillEllipse(Brush brush, Rectangle rect)
			{
				FillEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height);
			}
	public void FillEllipse(Brush brush, RectangleF rect)
			{
				FillEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height);
			}
	public void FillEllipse(Brush brush, int x, int y, int width, int height)
			{
				// Bail out now if there's nothing to draw.
				if((brush is SolidBrush) && ((SolidBrush)brush).Color.A == 0)
				{
					return;
				}

				Point[] rect = ConvertRectangle(x + baseWindow.X, y + baseWindow.Y, width, height, pageUnit);
				lock(this)
				{
					SelectBrush(brush);
					ToolkitGraphics.FillPie(rect, 0.0f, 360.0f);
				}
			}
	public void FillEllipse(Brush brush, float x, float y,
							float width, float height)
			{
				// Bail out now if there's nothing to draw.
				if((brush is SolidBrush) && ((SolidBrush)brush).Color.A == 0)
				{
					return;
				}

				Point[] rect = ConvertRectangle(x + baseWindow.X, y + baseWindow.Y, width, height, pageUnit);
				lock(this)
				{
					SelectBrush(brush);
					ToolkitGraphics.FillPie(rect, 0.0f, 360.0f);
				}
			}

	// Fill the interior of a path.
	public void FillPath(Brush brush, GraphicsPath path)
			{
				if(brush == null)
				{
					throw new ArgumentNullException("brush");
				}
				if(path == null)
				{
					throw new ArgumentNullException("path");
				}

				// Bail out now if there's nothing to draw.
				if((brush is SolidBrush) && ((SolidBrush)brush).Color.A == 0)
				{
					return;
				}

				path.Fill(this, brush);
			}

	// Fill a pie shape.
	public void FillPie(Brush brush, Rectangle rect,
						float startAngle, float sweepAngle)
			{
				if(((float)(int)startAngle) == startAngle &&
				   ((float)(int)sweepAngle) == sweepAngle)
				{
					FillPie(brush, rect.X, rect.Y, rect.Width, rect.Height,
							(int)startAngle, (int)sweepAngle);
				}
				else
				{
					FillPie(brush, rect.X, rect.Y, rect.Width, rect.Height,
							startAngle, sweepAngle);
				}
			}
	public void FillPie(Brush brush, int x, int y, int width, int height,
						int startAngle, int sweepAngle)
			{
				// Bail out now if there's nothing to draw.
				if((brush is SolidBrush) && ((SolidBrush)brush).Color.A == 0)
				{
					return;
				}

				Point[] rect = ConvertRectangle(x + baseWindow.X, y + baseWindow.Y, width, height, pageUnit);
				lock(this)
				{
					SelectBrush(brush);
					ToolkitGraphics.FillPie(rect, startAngle, sweepAngle);
				}
			}
	public void FillPie(Brush brush, float x, float y,
						float width, float height,
						float startAngle, float sweepAngle)
			{
				// Bail out now if there's nothing to draw.
				if((brush is SolidBrush) && ((SolidBrush)brush).Color.A == 0)
				{
					return;
				}

				Point[] rect = ConvertRectangle(x + baseWindow.X, y + baseWindow.Y, width, height, pageUnit);
				lock(this)
				{
					SelectBrush(brush);
					ToolkitGraphics.FillPie(rect, startAngle, sweepAngle);
				}
			}

	// Fill a polygon.
	public void FillPolygon(Brush brush, Point[] points)
			{
				FillPolygon(brush, points, FillMode.Alternate);
			}
	public void FillPolygon(Brush brush, PointF[] points)
			{
				FillPolygon(brush, points, FillMode.Alternate);
			}
	public void FillPolygon(Brush brush, Point[] points, FillMode fillMode)
			{
				// Bail out now if there's nothing to draw.
				if((brush is SolidBrush) && ((SolidBrush)brush).Color.A == 0)
				{
					return;
				}

				points = ConvertPoints(points, 2, pageUnit);
				BaseOffsetPoints(points);
				lock(this)
				{
					SelectBrush(brush);
					ToolkitGraphics.FillPolygon(points, fillMode);
				}
			}
	public void FillPolygon(Brush brush, PointF[] points, FillMode fillMode)
			{
				// Bail out now if there's nothing to draw.
				if((brush is SolidBrush) && ((SolidBrush)brush).Color.A == 0)
				{
					return;
				}

				Point[] dpoints = ConvertPoints(points, 2, pageUnit);
				BaseOffsetPoints(dpoints);
				lock(this)
				{
					SelectBrush(brush);
					ToolkitGraphics.FillPolygon(dpoints, fillMode);
				}
			}

	// Fill a rectangle.
	public void FillRectangle(Brush brush, Rectangle rect)
			{
				FillRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height);
			}
	public void FillRectangle(Brush brush, RectangleF rect)
			{
				FillRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height);
			}
	public void FillRectangle(Brush brush, int x, int y, int width, int height)
			{
				// Bail out now if there's nothing to draw.
				if((brush is SolidBrush) && ((SolidBrush)brush).Color.A == 0)
				{
					return;
				}

				lock(this)
				{
					SelectBrush(brush);
					ToolkitGraphics.FillPolygon(ConvertRectangle(x + baseWindow.X,
						y + baseWindow.Y, width, height, pageUnit), FillMode.Alternate);
				}
			}
	public void FillRectangle(Brush brush, float x, float y,
							  float width, float height)
			{
				// Bail out now if there's nothing to draw.
				if((brush is SolidBrush) && ((SolidBrush)brush).Color.A == 0)
				{
					return;
				}

				lock(this)
				{
					SelectBrush(brush);
					ToolkitGraphics.FillPolygon(ConvertRectangle(x + baseWindow.X,
						y + baseWindow.Y, width, height, pageUnit), FillMode.Alternate);
				}
			}

	// Fill a series of rectangles.
	public void FillRectangles(Brush brush, Rectangle[] rects)
			{
				if(rects == null)
				{
					throw new ArgumentNullException("rects");
				}
				int posn;
				for(posn = 0; posn < rects.Length; ++posn)
				{
					FillRectangle(brush, rects[posn].X, rects[posn].Y,
								  rects[posn].Width, rects[posn].Height);
				}
			}
	public void FillRectangles(Brush brush, RectangleF[] rects)
			{
				if(rects == null)
				{
					throw new ArgumentNullException("rects");
				}
				int posn;
				for(posn = 0; posn < rects.Length; ++posn)
				{
					FillRectangle(brush, rects[posn].X, rects[posn].Y,
								  rects[posn].Width, rects[posn].Height);
				}
			}

	// Fill a region.
	public void FillRegion(Brush brush, Region region)
			{
				// Bail out now if there's nothing to draw.
				if((brush is SolidBrush) && ((SolidBrush)brush).Color.A == 0)
				{
					return;
				}

				RectangleF[] rs = region.GetRegionScans(new Drawing.Drawing2D.Matrix());
				for (int i = 0; i < rs.Length; i++)
				{
					Rectangle b = Rectangle.Truncate(rs[i]);
					FillRectangle(brush, rs[i]);
				}
			}

	// Flush graphics operations to the display device.
	public void Flush()
			{
				Flush(FlushIntention.Flush);
			}
	public void Flush(FlushIntention intention)
			{
				lock(this)
				{
					if(graphics != null)
					{
						graphics.Flush(intention);
					}
				}
			}

	// Create a Graphics object from a HDC.
	public static Graphics FromHdc(IntPtr hdc)
			{
				return new Graphics
					(ToolkitManager.Toolkit.CreateFromHdc(hdc, IntPtr.Zero));
			}
	public static Graphics FromHdc(IntPtr hdc, IntPtr hdevice)
			{
				return new Graphics
					(ToolkitManager.Toolkit.CreateFromHdc(hdc, hdevice));
			}
	public static Graphics FromHdcInternal(IntPtr hdc)
			{
				return FromHdc(hdc);
			}

	// Create a Graphics object from a HWND.
	public static Graphics FromHwnd(IntPtr hwnd)
			{
				return new Graphics
					(ToolkitManager.Toolkit.CreateFromHwnd(hwnd));
			}
	public static Graphics FromHwndInternal(IntPtr hwnd)
			{
				return FromHwnd(hwnd);
			}

	// Create a graphics object for drawing into an image.
	public static Graphics FromImage(Image image)
			{
				if (image.toolkitImage == null)
					image.toolkitImage = ToolkitManager.Toolkit.CreateImage(image.dgImage, 0);
				Graphics g = new Graphics(ToolkitManager.Toolkit.CreateFromImage(image.toolkitImage));
				return g;
			}

	// Get the handle for the Windows halftone palette.  Not used here.
	public static IntPtr GetHalftonePalette()
			{
				return ToolkitManager.Toolkit.GetHalftonePalette();
			}

	// Get the HDC associated with this graphics object.
	public IntPtr GetHdc()
			{
				lock(this)
				{
					if(graphics != null)
					{
						return graphics.GetHdc();
					}
					else
					{
						return IntPtr.Zero;
					}
				}
			}

	// Get the nearest color that is supported by this graphics object.
	public Color GetNearestColor(Color color)
			{
				lock(this)
				{
					if(graphics != null)
					{
						return graphics.GetNearestColor(color);
					}
					else
					{
						return color;
					}
				}
			}

	// Intersect a region with the current clipping region.
	public void IntersectClip(Rectangle rect)
			{
				Clip.Intersect(rect);
				UpdateClip();
			}
	public void IntersectClip(RectangleF rect)
			{
				Clip.Intersect(rect);
				UpdateClip();
			}
	public void IntersectClip(Region region)
			{
				Clip.Intersect(region);
				UpdateClip();
			}

	// Determine if a point is within the visible clip region.
	public bool IsVisible(Point point)
			{
				return Clip.IsVisible(point, this);
			}
	public bool IsVisible(PointF point)
			{
				return Clip.IsVisible(point, this);
			}
	public bool IsVisible(Rectangle rect)
			{
				return Clip.IsVisible(rect, this);
			}
	public bool IsVisible(RectangleF rect)
			{
				return Clip.IsVisible(rect, this);
			}
	public bool IsVisible(int x, int y)
			{
				return Clip.IsVisible(x, y, this);
			}
	public bool IsVisible(float x, float y)
			{
				return Clip.IsVisible(x, y, this);
			}
	public bool IsVisible(int x, int y, int width, int height)
			{
				return Clip.IsVisible(x, y, width, height, this);
			}
	public bool IsVisible(float x, float y, float width, float height)
			{
				return Clip.IsVisible(x, y, width, height, this);
			}

	// First attempt as a way to measure strings and draw strings, taking into account a string format.
	// The string measuring happens a word at a time.
	private class StringDrawPositionCalculator
	{
		private SplitWord[] words;
		private int wordCount;
		private String text;
		private Point[] linePositions;
		private Graphics graphics;
		private Font font;
		private RectangleF layout;
		private StringFormat format;
		private int HotKeyIdx;

		// Details for each word
		private struct SplitWord
		{
			// Publicly-accessible state.
			public int start;
			public int length;
			public int width;
			public int line;

			// Constructor.
			public SplitWord(int start, int length)
					{
						this.start = start;
						this.length = length;
						this.width = 0;
						this.line = -1;
					}

		}; // struct SplitWord


		public StringDrawPositionCalculator(String text, Graphics graphics, Font font, RectangleF layout, StringFormat format)
				{
					this.text = text;
					this.graphics = graphics;
					this.font = font;
					this.layout = layout;
					this.format = format;
					this.HotKeyIdx = -1;
					
					if(format.HotkeyPrefix == System.Drawing.Text.HotkeyPrefix.Show)
					{
						HotKeyIdx = text.IndexOf('&');
						if( HotKeyIdx != -1)
						{
							if (HotKeyIdx >= text.Length-1 ||
						 		Char.IsControl(text[HotKeyIdx]))
							{
								HotKeyIdx = -1;
							}
							else
							{
								this.text = text.Substring(0,HotKeyIdx) +
											text.Substring(HotKeyIdx + 1);
							}
						}	
					}
				}

		public void LayoutByWords()
				{
					// Break the string into "words". Each word has a start pos, end pos and measured size.
					// Each new line group is treated as a "word" as is each whitespace.
					wordCount = 0;
					words = new SplitWord[16];
					int start = 0;
					int len = text.Length;
					for(int i = 0; i < len;)
					{
						start = i;
						char c = text[i];
						// Look for \r on its own, \n on its own or \r\n.
						if(c == '\r')
						{
							if(i < (len - 1) && text[i+1]=='\n')
							{
								i += 2;
							}
							else
							{
								i++;
							}
						}
						else if(c == '\n')
						{
							i++;
						}
						else
						{
							// Skip over the whitespace.
							while(i < len && Char.IsWhiteSpace(text[i]))
							{
								i++;
							}
							// We are at the start of text so skip over the text.
							if(i == start)
							{
								while(i < len)
								{
									c = text[i];
									if(Char.IsWhiteSpace(c) ||
									   c == '\n' || c == '\r')
									{
										break;
									}
									i++;
								}
							}
						}
						// Dynamically allocate the array if we need more space.
						if(wordCount >= words.Length)
						{
							SplitWord[] newWords = new SplitWord[words.Length * 2];
							Array.Copy(words, newWords, words.Length);
							words = newWords;
						}
						// Add the word.
						words[wordCount++] = new SplitWord(start, i - start);
					}
					Layout();
				}

		private void Layout()
				{
					MeasureStrings();
					WrapLines(layout.Width -1);
					linePositions = GetPositions(layout, format);
				}

		// Draw each line
		public void Draw(Brush brush)
				{
					graphics.SelectBrush(brush);
					if (linePositions.Length == 0)
						return;
					int currentLine = 0;
					int textStart = 0;
					int textLength = 0;
						
					SplitWord word = new SplitWord(0,0);
					for (int i = 0; i <= wordCount; i++)
					{
						if (i != wordCount)
						{
							word = words[i];
							if(word.line== -1)
								continue;
							if (word.line == currentLine)
							{
								textLength += word.length;
								continue;
							}
						}
						Point linePosition = linePositions[currentLine];
						// Draw if some of it is within layout.
						if(linePosition.X <= layout.Right && linePosition.Y <= layout.Bottom)
						{
							if (HotKeyIdx >= textStart && HotKeyIdx < textStart+textLength)
							{
								String startString = text.Substring(textStart, 
													 	HotKeyIdx -  textStart );
								String endString = text.Substring(HotKeyIdx+1, 
														textLength - (HotKeyIdx -textStart) -1);
								graphics.ToolkitGraphics.DrawString(startString, linePosition.X, linePosition.Y, null);  
								Font underlineFont = new Font (font, font.Style | FontStyle.Underline);
								float startWidth = graphics.MeasureString(startString,font).Width;
								float hotkeyWidth = graphics.MeasureString(text.Substring(HotKeyIdx,1),underlineFont).Width;
								graphics.SelectFont(font);
								graphics.ToolkitGraphics.DrawString(endString,
											linePosition.X+(int)(startWidth+hotkeyWidth), linePosition.Y, null);
								graphics.SelectFont(underlineFont);
								graphics.ToolkitGraphics.DrawString(text.Substring(HotKeyIdx,1),
											linePosition.X+(int)startWidth, linePosition.Y, null);
								graphics.SelectFont(font);
							} 
							else
							{
								String lineText = text.Substring(textStart, textLength);
								graphics.ToolkitGraphics.DrawString(lineText, linePosition.X, linePosition.Y, null);
							}
						}
						textStart = word.start;
						textLength = word.length;
						currentLine = word.line;
					}
				}

		// Calculate the bounds of the measured strings, the number of characters fitted and the number of lines.
		public Size GetBounds(out int charactersFitted, out int linesFilled)
				{
					linesFilled = 0;
					charactersFitted = 0;
					if (linePositions.Length == 0)
					{
						return Size.Empty;
					}
					bool noPartialLines = (format.FormatFlags & StringFormatFlags.LineLimit) != 0;
					int h = font.Height;
					// Find number of lines filled.
					for (int i = 0; i < linePositions.Length; i++)
					{
						if (noPartialLines)
						{
							if (linePositions[i].Y + h > layout.Height)
							{
								break;
							}
						}
						else if (linePositions[i].Y > layout.Height)
						{
							break;
						}
						linesFilled++;
					}

					int maxWidth = 0;
					int currentWidth = 0;
					int currentLine = 0;
					// Find the maximum width of a line and the number of characters fitted.
					for(int i=0; i < wordCount; i++)
					{
						SplitWord word = words[i];
						charactersFitted += word.length;
						if(word.line == currentLine)
						{
							currentWidth += word.width;
						}
						else
						{
							if (currentWidth > maxWidth)
							{
								maxWidth = currentWidth;
							}
							currentWidth = word.width;
							currentLine++;
						}
					}
					if (currentWidth > maxWidth)
					{
						maxWidth = currentWidth;
					}

					return new Size(maxWidth, h * linesFilled);
				}
		// Use the toolkit to measure all the words and spaces.
		private void  MeasureStrings() 
				{
					graphics.SelectFont(font);
					int spaceWidth = -1;
					Point[] rect = new Point[0];
					int charactersFitted;
					int linesFilled;
					for(int i=0;i< wordCount;i++)
					{
						SplitWord word = words[i];
						char c = text[word.start];
						if (c != '\n' && c != '\r')
						{
							if (char.IsWhiteSpace(c))
							{
								if(spaceWidth == -1)
								{
									spaceWidth = graphics.ToolkitGraphics.MeasureString(" ", rect, null, out charactersFitted, out linesFilled, false).Width;
								}
								word.width = spaceWidth;
								words[i] = word;
							}
							else
							{
								word.width = graphics.ToolkitGraphics.MeasureString(text.Substring(word.start, word.length), rect, null, out charactersFitted, out linesFilled, false).Width;
								words[i] = word;
							}
						}
					}
				}

		// Calculate the word wrap from the words.
		private void WrapLines(float lineSize)
				{
					float currSize=0;
					int currLine=0;

					for(int i=0; i < wordCount;i++)
					{
						SplitWord word = words[i];
						char c = text[word.start];
						// Wrap when \r\n
						if (c == '\r' || c == '\n') 
						{
							currLine++;
							currSize = 0;
							continue;
						}
						if (Char.IsWhiteSpace(c))
						{
							if( i< wordCount-1)
							{
								// Check that we are not at the end of the line.
								SplitWord nextWord = words[i+1];
								char c1 = text[nextWord.start];
								if (c1 != '\r' && c1 != '\n')
								{
									// If we have space for the next word in the line then set the lines.
									if (currSize + word.width + nextWord.width <= lineSize) 
									{
										currSize += word.width + nextWord.width;
										word.line = nextWord.line = currLine;
										words[i] = word;
										words[i+1] = nextWord;
										i+=1;
									} 
									else
									{
										// No space left in the line.
										currLine++;
										currSize=0;
									}
								}
							}
						}
						else  
						{
							// we have no whitespace in line -> append / wrap line
							if (currSize + word.width < lineSize || currLine == 0)
							{
								word.line = currLine;
								words[i] = word;
								currSize += word.width;
							} 
							else
							{
								word.line = ++currLine;					
								words[i] = word;
								currSize=0;
							}
						}
					}
				}
		// Calculate the Position of the wrapped lines
		private Point[] GetPositions(RectangleF layout, StringFormat format)
				{
					// Get the total number of lines by checking from the back.
					int numLines = 0;
					for(int i = wordCount-1; i >= 0; i--)
					{
						if(words[i].line!=-1)
						{
							numLines = words[i].line + 1;
							break;
						}
					}
					Point[] linePositions = new Point[numLines];

					int h = font.Height;

					int yOffset=0;
					// Set the offset based on the  LineAlignment.
					if (format.LineAlignment == StringAlignment.Far)
					{
						yOffset = (int)layout.Height - numLines * h;
					}
					else if (format.LineAlignment == StringAlignment.Center)
					{
						yOffset = ((int)layout.Height - numLines * h)/2;
					}

					for(int line=0; line < numLines; line++)
					{
						int xOffset = 0;
						if (format.Alignment != StringAlignment.Near)
						{
							for(int i=0; i < wordCount; i++)
							{
								SplitWord word = words[i];
								if(word.line == line)
								{
									xOffset += word.width;
								}
								if(word.line > line)
								{
									break;
								}
							}
							// Set the offset based on the Alignment.
							if (format.Alignment == StringAlignment.Far)
							{
								xOffset = (int)layout.Width - 1 - xOffset;
							}
							else if (format.Alignment == StringAlignment.Center)
							{
								xOffset = ((int)layout.Width - 1 - xOffset)/2;
							}
						}

						linePositions[line].Y=(int)layout.Y + yOffset + line*h;
						linePositions[line].X=(int)layout.X + xOffset;
					}
					return linePositions;
				}
	}; // class StringDrawPositionCalculator

	private class StringMeasurePositionCalculator
	{
		// Internal state.
		private Graphics graphics;
		private String text;
		private Font font;
		private Rectangle layout;
		private StringFormat format;
		private int[] lines = new int[16];
		private int count = 0;
		private int currentLine = 0;

		// NOTE: this needs to be rewritten as stateless in order to avoid
		//       excess allocations... this also needs to be fixed so that
		//       it actually handles horizontal text layout (currently only
		//       handles vertical) for character range measurements


		// Constructor.
		public StringMeasurePositionCalculator
					(Graphics graphics, String text, Font font,
					 Rectangle layout, StringFormat format)
				{
					this.graphics = graphics;
					this.text = text;
					this.font = font;
					this.layout = layout;
					this.format = format;
				}


		// Get the regions for the character ranges of the string format.
		public Region[] GetRegions()
				{
					Rectangle[] bounds = GetCharBounds();
					// Now consolidate positions based on character ranges
					Region[] regions = new Region[format.ranges.Length];
					for (int i = 0; i < format.ranges.Length; i++)
					{
						CharacterRange range = format.ranges[i];
						Region region = null;
						for (int j = range.First; j < range.First + range.Length; j++)
						{
							if (region == null)
								region = new Region(bounds[j]);
							else
								region.Union(bounds[j]);
						}
						regions[i] = region;
					}
					return regions;
				}

		// Get the rectangles bounding each character.
		public Rectangle[] GetCharBounds()
				{
					// create the bounds
					Rectangle[] bounds = new Rectangle[text.Length];

					// set the default maximum x
					int xMax = 0;

					// set the default maximum y
					int yMax = 0;

					// get the font height
					int fontHeight = (int)font.GetHeight(graphics);

					// get the vertical flag
					bool vertical = ((format.FormatFlags &
					                  StringFormatFlags.DirectionVertical) != 0);

					// set the maximum x and y based on the vertical flag
					if(vertical)
					{
						// set the maximum x to the maximum height
						xMax = (layout.Height - 1);

						// set the maximum y to the maximum width
						yMax = (layout.Width - 1);
					}
					else
					{
						// set the maximum x to the maximum width
						xMax = (layout.Width - 1 - (int)(font.SizeInPoints*graphics.DpiX/369.7184));

						// set the maximum y to the maximum height
						yMax = (layout.Height - 1);
					}

					// get the wrapping flag
					bool noWrap = ((format.FormatFlags &
					                StringFormatFlags.NoWrap) != 0);

					// set the line size to the maximum y
					int lineSizeRemaining = yMax;

					// add the first line
					AddLine(0);

					// set the current position
					int currentPos = 0;

					// add the lines of text
					do
					{
						// measure the line
						MeasureLine
							(ref bounds, ref currentPos, ref text, xMax,
							 graphics, font, vertical, noWrap);

						// add the current line
						AddLine(currentPos);

						// update the remaining line height
						lineSizeRemaining -= fontHeight;
					}
					while(currentPos < text.Length && lineSizeRemaining >= 0 && !noWrap);

					// set the default y offset
					int yOffset = 0;

					// adjust the y offset for the line alignment
					if(format.LineAlignment == StringAlignment.Center)
					{
						yOffset = (lineSizeRemaining / 2);
					}
					else if(format.LineAlignment == StringAlignment.Far)
					{
						yOffset = lineSizeRemaining;
					}

					// set the default x offset
					int xOffset = 0;

					// ??
					for(int i = 0; i < text.Length;)
					{
						// ??
						if(CurrentLine == i)
						{
							// ??
							currentLine++;

							// ??
							xOffset = 0;

							// ??
							if(format.Alignment == StringAlignment.Far)
							{
								// Go back and find the right point of the last visible character

								// ??
								int back = CurrentLine - 1;

								// ??
								if(back > -1)
								{
									// ??
									for(; back >= 0; back--)
									{
										// ??
										if(bounds[back] != Rectangle.Empty)
										{
											break;
										}
									}

									// ??
									xOffset = xMax + 1 - bounds[back].Right;
								}
								else
								{
									// ??
									xOffset = xMax + 1;
								}
							}
							else if(format.Alignment == StringAlignment.Center)
							{
								// ??
								for(int j = i; j < CurrentLine; j++)
								{
									// ??
									xOffset += bounds[j].Width;
								}

								// ??
								xOffset = (xMax + 1 - xOffset)/2;
							}
						}
						else 
						{
							// ??
							if(bounds[i] != Rectangle.Empty)
							{
								// ??
								Rectangle rect = bounds[i];

								// ??
								bounds[i] = new Rectangle
									((rect.Left + xOffset + layout.Left),
									 (rect.Top + ((currentLine - 1) *
									  fontHeight) + layout.Top + 1),
									 rect.Width, rect.Height);
							}

							// ??
							i++;
						}
					}

					// ??
					return bounds;
				}

		private void AddLine(int value)
				{
					// ensure the capacity of the lines array
					if(count == lines.Length)
					{
						// create the new lines array
						int[] newLines = new int[lines.Length * 2];

						// copy the line data to the new array
						Array.Copy(lines, newLines, lines.Length);

						// reset the lines array to the new array
						lines = newLines;
					}

					// add the line
					lines[count++] = value;
				}

		private int CurrentLine
				{
					get
					{
						// return minus one if past the last line
						if(currentLine > count) { return -1; }

						// return the current line
						return lines[currentLine];
					}
				}

		// Measures one full line. Updates the positions of the characters in that line relative to 0,0
		private void MeasureLine
					(ref Rectangle[] bounds, ref int currentPos,
					 ref string text, float maxX, Graphics g, Font f,
					 bool vertical, bool noWrap)
				{
					// set the initial position
					int initialPos = currentPos;

					// set the default x position
					int x = (int)(f.SizeInPoints*g.DpiX/369.7184);

					// set the default y position
					int y = 0;

					// ??
					do
					{
						// get the current character
						char c = text[currentPos];

						// check for the end of the line
						if(c == '\n')
						{
							// move past the end of the line
							currentPos++;

							// we're done
							return;
						}

						// Ignore returns
						if(c != '\r')
						{
							//TODO use Platform specific measure function & take into account kerning

							// get the size of the current character
							g.SelectFont(f);
							int charactersFitted, linesFilled;
							Size s = g.ToolkitGraphics.MeasureString(c.ToString(), null, null, out charactersFitted, out linesFilled, false);
							s.Height = f.Height;

							// ??
							int newX = x;

							// ??
							int newY = y;

							// ??
							if(vertical)
							{
								newX += s.Height;
							}
							else
							{
								newX += s.Width;
							}

							// ??
							if(newX > maxX)
							{
								// ??
								if(noWrap) { return; }

								// Backtrack to wrap the word

								// ??
								for(int i = currentPos; i > initialPos; i--)
								{
									// ??
									if(text[i] == ' ')
									{
										// Swallow the space

										// ??
										bounds[i++] = Rectangle.Empty;

										// ??
										currentPos = i;

										// ??
										return;
									}
								}

								// ??
								return;
							}
							else
							{
								// ??
								if(vertical)
								{
									bounds[currentPos] = new Rectangle
										(y, x, s.Height, s.Width - 1);
								}
								else
								{
									bounds[currentPos] = new Rectangle
										(x, y, s.Width, s.Height - 1);
								}
							}

							// ??
							x = newX;
						}

						// ??
						currentPos++;
					}
					while (currentPos < text.Length);
				}

	}; // class StringMeasurePositionCalculator

	// Measure the character ranges for a string.
	public Region[] MeasureCharacterRanges(String text, Font font, RectangleF layoutRect, StringFormat stringFormat)
			{
				StringMeasurePositionCalculator calculator = new StringMeasurePositionCalculator(this, text, font, Rectangle.Truncate(layoutRect), stringFormat);		
				return calculator.GetRegions();
			}

	// Non Microsoft
	public Rectangle[] MeasureCharacters(String text, Font font, RectangleF layoutRect, StringFormat stringFormat)
			{
				StringMeasurePositionCalculator calculator = new StringMeasurePositionCalculator(this, text, font, Rectangle.Truncate(layoutRect), stringFormat);		
				return calculator.GetCharBounds();
			}

	// Measure the size of a string.
	public SizeF MeasureString(String text, Font font)
			{		
				return MeasureString(text, font, new SizeF(0.0f, 0.0f), null);
			}
	public SizeF MeasureString(String text, Font font, int width)
			{
				return MeasureString
					(text, font, new SizeF(width, 999999.0f), null);
			}
	public SizeF MeasureString(String text, Font font, SizeF layoutArea)
			{
				return MeasureString(text, font, layoutArea, null);
			}
	public SizeF MeasureString(String text, Font font,
							   int width, StringFormat format)
			{
				return MeasureString
					(text, font, new SizeF(width, 999999.0f), format);
			}
	public SizeF MeasureString(String text, Font font,
							   PointF origin, StringFormat format)
			{
				return MeasureString
					(text, font, new SizeF(0.0f, 0.0f), format);
			}
	public SizeF MeasureString(String text, Font font,
							   SizeF layoutArea, StringFormat format)
			{
				int charactersFitted, linesFilled;
				return MeasureString
					(text, font, layoutArea, format, out charactersFitted,
					 out linesFilled);
			}
	public SizeF MeasureString(String text, Font font, SizeF layoutArea,
	                           StringFormat format, out int charactersFitted,
	                           out int linesFilled)
			{
				// bail out now if there's nothing to measure
				if(((Object)text) == null || text.Length == 0)
				{
					charactersFitted = 0;
					linesFilled = 0;
					return new SizeF(0.0f, 0.0f);
				}

				// ensure we have a string format
				if(format == null)
				{
					format = new StringFormat();
				}

				// select the font
				SelectFont(font);

				// measure the string
				Size size = ToolkitGraphics.MeasureString
					(text, null, null, out charactersFitted,
					 out linesFilled, false);

				// determine if the string contains a new line
				bool containsNL =
					(text.IndexOfAny(new char[] { '\r', '\n' }) >= 0);

				// get the layout width
				float width = layoutArea.Width;

				// return the size information based on wrapping behavior
				if((format.FormatFlags & StringFormatFlags.NoWrap) == 0 &&
				   ((size.Width >= width && width != 0.0f) || containsNL))
				{
					// create the layout rectangle
					Rectangle layout = new Rectangle
						(0, 0, (int)width, (int)layoutArea.Height);

					// declare the drawing position calculator
					StringDrawPositionCalculator calculator;

					// create the drawing position calculator
					calculator = new StringDrawPositionCalculator
						(text, this, font, layout , format);

					// calculate the layout of the text
					calculator.LayoutByWords();

					// calculate and return the bounds of the text
					SizeF s = calculator.GetBounds
						(out charactersFitted, out linesFilled);
					s.Width += font.SizeInPoints*DpiX/184.8592;
					return s;
				}
				else
				{
					// NOTE: we use the font height here, rather than
					//       the height returned by the toolkit, since
					//       the toolkit returns the actual height of
					//       the text but the expected behavior is that
					//       the height be the font height and the width
					//       is all that is actually measured

					// set the number of characters fitted
					charactersFitted = text.Length;

					// set the number of lines filled
					linesFilled = 1;

					// return the size of the text
					return new SizeF(size.Width + font.SizeInPoints*DpiX/184.8592f, font.Height);
				}
			}

	// Multiply the transformation matrix by a specific amount.
	public void MultiplyTransform(Matrix matrix)
			{
                                // Do not use the property Transform directly, because Transform should return a Copy
                                transform = new Matrix(Transform);
				transform.Multiply(matrix);
				if(transform.IsIdentity)
				{
					transform = null;
				}
			}
	public void MultiplyTransform(Matrix matrix, MatrixOrder order)
			{
                                // Do not use the property Transform directly, because Transform should return a Copy
                                transform = new Matrix(Transform);
				transform.Multiply(matrix, order);
				if(transform.IsIdentity)
				{
					transform = null;
				}
			}

	// Release a HDC that was obtained via a previous call to "GetHdc".
	public void ReleaseHdc(IntPtr hdc)
			{
				lock(this)
				{
					if(graphics != null)
					{
						graphics.ReleaseHdc(hdc);
					}
				}
			}
	public void ReleaseHdcInternal(IntPtr hdc)
			{
				ReleaseHdc(hdc);
			}

	// Reset the clipping region.
	public void ResetClip()
			{
				Clip = new Region();
				UpdateClip();
			}

	// Reset the transformation matrix to identity.
	public void ResetTransform()
			{
				transform = null;
				Clip = clip;
			}

	// Restore to a previous save point.
	public void Restore(GraphicsState gstate)
			{
				if(gstate != null)
				{
					lock(this)
					{
						gstate.Restore(this);
					}
				}
			}

	// Apply a rotation to the transformation matrix.
	public void RotateTransform(float angle)
			{
                                // Do not use the property Transform directly, because Transform should return a Copy
                                transform = new Matrix(Transform);
				transform.Rotate(angle);
				if(transform.IsIdentity)
				{
					transform = null;
				}
			}
	public void RotateTransform(float angle, MatrixOrder order)
			{
                                // Do not use the property Transform directly, because Transform should return a Copy
                                transform = new Matrix(Transform);
				transform.Rotate(angle, order);
				if(transform.IsIdentity)
				{
					transform = null;
				}
			}

	// Save the current graphics state.
	public GraphicsState Save()
			{
				lock(this)
				{
					return new GraphicsState(this);
				}
			}

	// Apply a scaling factor to the transformation matrix.
	public void ScaleTransform(float sx, float sy)
			{
                                // Do not use the property Transform directly, because Transform should return a Copy
                                transform = new Matrix(Transform);
				transform.Scale(sx, sy);
				if(transform.IsIdentity)
				{
					transform = null;
				}
			}
	public void ScaleTransform(float sx, float sy, MatrixOrder order)
			{
                                // Do not use the property Transform directly, because Transform should return a Copy
                                transform = new Matrix(Transform);
				transform.Scale(sx, sy, order);
				if(transform.IsIdentity)
				{
					transform = null;
				}
			}

	// Set the clipping region of this graphics object.
	public void SetClip(Graphics g)
			{
				if(g == null)
				{
					throw new ArgumentNullException("g");
				}
				clip = g.Clip.Clone();
				UpdateClip();
			}
	public void SetClip(Graphics g, CombineMode combineMode)
			{
				if(g == null)
				{
					throw new ArgumentNullException("g");
				}
				Region other = g.Clip;
				switch(combineMode)
				{
					case CombineMode.Replace:
						clip = other.Clone(); break;

					case CombineMode.Intersect:
					{
						Clip.Intersect(other);
					}
					break;

					case CombineMode.Union:
					{
						Clip.Union(other);
					}
					break;

					case CombineMode.Xor:
					{
						Clip.Xor(other);
					}
					break;

					case CombineMode.Exclude:
					{
						Clip.Exclude(other);
					}
					break;

					case CombineMode.Complement:
					{
						Clip.Complement(other);
					}
					break;

					default: return;
				}
				UpdateClip();
			}
	public void SetClip(GraphicsPath path)
			{
				clip = new Region(path);
				UpdateClip();
			}
	public void SetClip(GraphicsPath path, CombineMode combineMode)
			{
				if(path == null)
				{
					throw new ArgumentNullException("path");
				}
				switch(combineMode)
				{
					case CombineMode.Replace:
					{
						clip = new Region(path);
					}
					break;

					case CombineMode.Intersect:
					{
						Clip.Intersect(path);
					}
					break;

					case CombineMode.Union:
					{
						Clip.Union(path);
					}
					break;

					case CombineMode.Xor:
					{
						Clip.Xor(path);
					}
					break;

					case CombineMode.Exclude:
					{
						Clip.Exclude(path);
					}
					break;

					case CombineMode.Complement:
					{
						Clip.Complement(path);
					}
					break;

					default: return;
				}
				UpdateClip();
			}
	public void SetClip(Rectangle rect)
			{
				clip = new Region(rect);
				UpdateClip();
			}
	public void SetClip(Rectangle rect, CombineMode combineMode)
			{
				switch(combineMode)
				{
					case CombineMode.Replace:
					{
						clip = new Region(rect);
					}
					break;

					case CombineMode.Intersect:
					{
						Clip.Intersect(rect);
					}
					break;

					case CombineMode.Union:
					{
						Clip.Union(rect);
					}
					break;

					case CombineMode.Xor:
					{
						Clip.Xor(rect);
					}
					break;

					case CombineMode.Exclude:
					{
						Clip.Exclude(rect);
					}
					break;

					case CombineMode.Complement:
					{
						Clip.Complement(rect);
					}
					break;

					default: return;
				}
				UpdateClip();
			}
	public void SetClip(RectangleF rect)
			{
				clip = new Region(rect);
				UpdateClip();
			}
	public void SetClip(RectangleF rect, CombineMode combineMode)
			{
				switch(combineMode)
				{
					case CombineMode.Replace:
					{
						clip = new Region(rect);
					}
					break;

					case CombineMode.Intersect:
					{
						Clip.Intersect(rect);
					}
					break;

					case CombineMode.Union:
					{
						Clip.Union(rect);
					}
					break;

					case CombineMode.Xor:
					{
						Clip.Xor(rect);
					}
					break;

					case CombineMode.Exclude:
					{
						Clip.Exclude(rect);
					}
					break;

					case CombineMode.Complement:
					{
						Clip.Complement(rect);
					}
					break;

					default: return;
				}
				UpdateClip();
			}
	public void SetClip(Region region, CombineMode combineMode)
			{
				if(region == null)
				{
					throw new ArgumentNullException("region");
				}
				switch(combineMode)
				{
					case CombineMode.Replace:
					{
						clip = region.Clone();
					}
					break;

					case CombineMode.Intersect:
					{
						Clip.Intersect(region);
					}
					break;

					case CombineMode.Union:
					{
						Clip.Union(region);
					}
					break;

					case CombineMode.Xor:
					{
						Clip.Xor(region);
					}
					break;

					case CombineMode.Exclude:
					{
						Clip.Exclude(region);
					}
					break;

					case CombineMode.Complement:
					{
						Clip.Complement(region);
					}
					break;

					default: return;
				}
				UpdateClip();
			}

	internal void SetClipInternal(Region region)
	{
		clip = region;
	}

	// Transform points from one co-ordinate space to another.
	public void TransformPoints(CoordinateSpace destSpace,
		CoordinateSpace srcSpace,
		Point[] pts)
			{
				float x;
				float y;
				
				if (srcSpace == CoordinateSpace.Device)
				{
					if (destSpace != CoordinateSpace.Device)
					{
						// Convert from Device to Page.
						for (int i = 0; i < pts.Length; i++)
						{
							if (pageUnit == GraphicsUnit.Pixel)
								continue;
							x = pts[i].X;
							y = pts[i].Y;
							// Apply the page unit to get page co-ordinates.
							switch(pageUnit)
							{
								case GraphicsUnit.Display:
								{
									x /= DpiX / 75.0f;
									y /= DpiY / 75.0f;
								}
									break;

								case GraphicsUnit.Point:
								{
									x /= DpiX / 72.0f;
									y /= DpiY / 72.0f;
								}
									break;

								case GraphicsUnit.Inch:
								{
									x /= DpiX;
									y /= DpiY;
								}
									break;

								case GraphicsUnit.Document:
								{
									x /= DpiX / 300.0f;
									y /= DpiY / 300.0f;
								}
									break;

								case GraphicsUnit.Millimeter:
								{
									x /= DpiX / 25.4f;
									y /= DpiY / 25.4f;
								}
									break;
								default:
									break;
							}

							// Apply the inverse of the page scale factor.
							if(pageScale != 1.0f)
							{
								x /= pageScale;
								y /= pageScale;
							}
							pts[i] = new Point((int)x, (int)y);
						}
					} // destSpace != CoordinateSpace.Device
					srcSpace = CoordinateSpace.Page;
				}
				if (srcSpace == CoordinateSpace.World)
				{
					if (destSpace == CoordinateSpace.World)
						return;
					// Convert from World to Page.
					if (transform != null)
						transform.TransformPoints(pts);

					srcSpace = CoordinateSpace.Page;
				}
				if (srcSpace == CoordinateSpace.Page)
				{
					if (destSpace == CoordinateSpace.World && transform != null)
					{
						// Convert from Page to World.
						// Apply the inverse of the world transform.
						Matrix invert = new Matrix(transform);
						invert.Invert();
						invert.TransformPoints(pts);
					}
					if (destSpace == CoordinateSpace.Device)
					{
						// Convert from Page to Device.
						// Apply the page scale factor.
						for (int i = 0; i < pts.Length; i++)
						{
							x = pts[i].X;
							y = pts[i].Y;
							if(pageScale != 1.0f)
							{
								x *= pageScale;
								y *= pageScale;
							}

							// Apply the page unit to get device co-ordinates.
							switch(pageUnit)
							{
								case GraphicsUnit.Display:
								{
									x *= DpiX / 75.0f;
									y *= DpiY / 75.0f;
								}
									break;

								case GraphicsUnit.Point:
								{
									x *= DpiX / 72.0f;
									y *= DpiY / 72.0f;
								}
									break;

								case GraphicsUnit.Inch:
								{
									x *= DpiX;
									y *= DpiY;
								}
									break;

								case GraphicsUnit.Document:
								{
									x *= DpiX / 300.0f;
									y *= DpiY / 300.0f;
								}
									break;

								case GraphicsUnit.Millimeter:
								{
									x *= DpiX / 25.4f;
									y *= DpiY / 25.4f;
								}
									break;
								case GraphicsUnit.World:
								case GraphicsUnit.Pixel:
								default:
									break;

							}
							pts[i] = new Point((int)x, (int)y);
						}
					}
				}
			}

	public void TransformPoints(CoordinateSpace destSpace,
		CoordinateSpace srcSpace,
		PointF[] pts)
			{
				float x;
				float y;
				
				if (srcSpace == CoordinateSpace.Device)
				{
					if (destSpace != CoordinateSpace.Device)
					{
						// Convert from Device to Page.
						for (int i = 0; i < pts.Length; i++)
						{
							if (pageUnit == GraphicsUnit.Pixel)
								continue;
							x = pts[i].X;
							y = pts[i].Y;
							// Apply the page unit to get page co-ordinates.
							switch(pageUnit)
							{
								case GraphicsUnit.Display:
								{
									x /= DpiX / 75.0f;
									y /= DpiY / 75.0f;
								}
									break;

								case GraphicsUnit.Point:
								{
									x /= DpiX / 72.0f;
									y /= DpiY / 72.0f;
								}
									break;

								case GraphicsUnit.Inch:
								{
									x /= DpiX;
									y /= DpiY;
								}
									break;

								case GraphicsUnit.Document:
								{
									x /= DpiX / 300.0f;
									y /= DpiY / 300.0f;
								}
									break;

								case GraphicsUnit.Millimeter:
								{
									x /= DpiX / 25.4f;
									y /= DpiY / 25.4f;
								}
									break;
								default:
									break;
							}

							// Apply the inverse of the page scale factor.
							if(pageScale != 1.0f)
							{
								x /= pageScale;
								y /= pageScale;
							}
							pts[i] = new PointF(x, y);
						}
					} // destSpace != CoordinateSpace.Device
					srcSpace = CoordinateSpace.Page;
				}
				if (srcSpace == CoordinateSpace.World)
				{
					if (destSpace == CoordinateSpace.World)
						return;
					// Convert from World to Page.
					if (transform != null)
						transform.TransformPoints(pts);

					srcSpace = CoordinateSpace.Page;
				}
				if (srcSpace == CoordinateSpace.Page)
				{
					if (destSpace == CoordinateSpace.World && transform != null)
					{
						// Convert from Page to World.
						// Apply the inverse of the world transform.
						Matrix invert = new Matrix(transform);
						invert.Invert();
						invert.TransformPoints(pts);
					}
					if (destSpace == CoordinateSpace.Device)
					{
						// Convert from Page to Device.
						// Apply the page scale factor.
						for (int i = 0; i < pts.Length; i++)
						{
							x = pts[i].X;
							y = pts[i].Y;
							if(pageScale != 1.0f)
							{
								x *= pageScale;
								y *= pageScale;
							}

							// Apply the page unit to get device co-ordinates.
							switch(pageUnit)
							{
								case GraphicsUnit.Display:
								{
									x *= DpiX / 75.0f;
									y *= DpiY / 75.0f;
								}
									break;

								case GraphicsUnit.Point:
								{
									x *= DpiX / 72.0f;
									y *= DpiY / 72.0f;
								}
									break;

								case GraphicsUnit.Inch:
								{
									x *= DpiX;
									y *= DpiY;
								}
									break;

								case GraphicsUnit.Document:
								{
									x *= DpiX / 300.0f;
									y *= DpiY / 300.0f;
								}
									break;

								case GraphicsUnit.Millimeter:
								{
									x *= DpiX / 25.4f;
									y *= DpiY / 25.4f;
								}
									break;
								case GraphicsUnit.World:
								case GraphicsUnit.Pixel:
								default:
									break;

							}
							pts[i] = new PointF(x, y);
						}
					}
				}
			}

	// Translate the clipping region by a specified amount.
	public void TranslateClip(int dx, int dy)
			{
				Region clip = Clip;
				clip.Translate(dx, dy);
				Clip = clip;
			}
	public void TranslateClip(float dx, float dy)
			{
				Region clip = Clip;
				clip.Translate(dx, dy);
				Clip = clip;
			}

	// Apply a translation to the transformation matrix.
	public void TranslateTransform(float dx, float dy)
			{
                                // Do not use the property Transform directly, because Transform should return a Copy
                                transform = new Matrix(Transform);
				transform.Translate(dx, dy);
				if(transform.IsIdentity)
				{
					transform = null;
				}
			}
	public void TranslateTransform(float dx, float dy, MatrixOrder order)
			{
                                // Do not use the property Transform directly, because Transform should return a Copy
                                transform = new Matrix(Transform);
				transform.Translate(dx, dy, order);
				if(transform.IsIdentity)
				{
					transform = null;
				}
			}

	// Delegate that is used to handle abort callbacks on "DrawImage".
#if !ECMA_COMPAT
	[Serializable]
	[ComVisible(false)]
#endif
	public delegate bool DrawImageAbort(IntPtr callbackdata);

	// Delegate that is used to enumerate metafile contents.
#if !ECMA_COMPAT
	[Serializable]
	[ComVisible(false)]
#endif
	public delegate bool EnumerateMetafileProc
			(EmfPlusRecordType recordType, int flags, int dataSize,
			 IntPtr data, PlayRecordCallback callbackData);

	private void BaseOffsetPoints(Point[] points)
			{
				for (int i = 0; i < points.Length; i++)
				{
					Point p = points[i];
					p.X += baseWindow.X;
					p.Y += baseWindow.Y;
					points[i] = p;
				}
			}
	
	// Convert a point into device pixels.
	private void ConvertPoint(ref int x, ref int y, GraphicsUnit graphicsUnit)
			{
				float newX, newY;
				float adjustX, adjustY;

				// Apply the world transform first.
				if(transform != null)
				{
					transform.TransformPoint(x, y, out newX, out newY);
				}
				else
				{
					newX = (float)x;
					newY = (float)y;
				}

				// Apply the page scale factor.
				if(pageScale != 1.0f)
				{
					newX *= pageScale;
					newY *= pageScale;
				}

				// Apply the page unit to get device co-ordinates.
				switch(graphicsUnit)
				{
					case GraphicsUnit.World:
					case GraphicsUnit.Pixel:
					default:
					{
						// We are finished - no more adjustments are necessary.
						x = (int)newX;
						y = (int)newY;
						return;
					}
						// Not reached.

					case GraphicsUnit.Display:
					{
						adjustX = DpiX / 75.0f;
						adjustY = DpiY / 75.0f;
					}
						break;

					case GraphicsUnit.Point:
					{
						adjustX = DpiX / 72.0f;
						adjustY = DpiY / 72.0f;
					}
						break;

					case GraphicsUnit.Inch:
					{
						adjustX = DpiX;
						adjustY = DpiY;
					}
						break;

					case GraphicsUnit.Document:
					{
						adjustX = DpiX / 300.0f;
						adjustY = DpiY / 300.0f;
					}
						break;

					case GraphicsUnit.Millimeter:
					{
						adjustX = DpiX / 25.4f;
						adjustY = DpiY / 25.4f;
					}
						break;
				}
				x = (int)(newX * adjustX);
				y = (int)(newY * adjustY);
			}

	private void ConvertPoint(float x, float y, out int dx, out int dy, GraphicsUnit graphicsUnit)
			{
				float newX, newY;
				float adjustX, adjustY;

				// Apply the world transform first.
				if(transform != null)
				{
					transform.TransformPoint(x, y, out newX, out newY);
				}
				else
				{
					newX = x;
					newY = y;
				}

				// Apply the page scale factor.
				if(pageScale != 1.0f)
				{
					newX *= pageScale;
					newY *= pageScale;
				}

				// Apply the page unit to get device co-ordinates.
				switch(graphicsUnit)
				{
					case GraphicsUnit.World:
					case GraphicsUnit.Pixel:
					default:
					{
						// We are finished - no more adjustments are necessary.
						dx = (int)newX;
						dy = (int)newY;
						return;
					}
						// Not reached.

					case GraphicsUnit.Display:
					{
						adjustX = DpiX / 75.0f;
						adjustY = DpiY / 75.0f;
					}
						break;

					case GraphicsUnit.Point:
					{
						adjustX = DpiX / 72.0f;
						adjustY = DpiY / 72.0f;
					}
						break;

					case GraphicsUnit.Inch:
					{
						adjustX = DpiX;
						adjustY = DpiY;
					}
						break;

					case GraphicsUnit.Document:
					{
						adjustX = DpiX / 300.0f;
						adjustY = DpiY / 300.0f;
					}
						break;

					case GraphicsUnit.Millimeter:
					{
						adjustX = DpiX / 25.4f;
						adjustY = DpiY / 25.4f;
					}
						break;
				}
				dx = (int)(newX * adjustX);
				dy = (int)(newY * adjustY);
			}

	// Convert a list of points into device pixels.
	private Point[] ConvertPoints(Point[] points, int minPoints, GraphicsUnit unit)
			{
				// Validate the parameter.
				if(points == null)
				{
					throw new ArgumentNullException("points");
				}
				if(points.Length < minPoints)
				{
					throw new ArgumentException
						(String.Format
							(S._("Arg_NeedsAtLeastNPoints"), minPoints));
				}
		
				// Convert the "points" array.
				Point[] newPoints = new Point [points.Length];
				int x, y;
				int posn;
				for(posn = 0; posn < points.Length; ++posn)
				{
					x = points[posn].X;
					y = points[posn].Y;
					ConvertPoint(ref x, ref y, unit);
					newPoints[posn] = new Point(x, y);
				}
				return newPoints;
			}
	private Point[] ConvertPoints(PointF[] points, int minPoints, GraphicsUnit unit)
			{
				// Validate the parameter.
				if(points == null)
				{
					throw new ArgumentNullException("points");
				}
				if(points.Length < minPoints)
				{
					throw new ArgumentException
						(String.Format
							(S._("Arg_NeedsAtLeastNPoints"), minPoints));
				}

				// Convert the "points" array.
				Point[] newPoints = new Point [points.Length];
				int x, y;
				int posn;
				for(posn = 0; posn < points.Length; ++posn)
				{
					ConvertPoint(points[posn].X, points[posn].Y, out x, out y, unit);	
					newPoints[posn] = new Point(x, y);
				}
				return newPoints;
			}

	// Convert a rectangle into a set of 3 device co-ordinates.
	// The result may be a parallelogram, not a rectangle.
	private Point[] ConvertRectangle3(int x, int y, int width, int height, GraphicsUnit unit)
			{
				Point[] points = new Point[3];
				int pt1x, pt1y, pt2x, pt2y, pt3x, pt3y;
				pt1x = x;
				pt1y = y;
				pt2x = x + width;
				pt2y = y;
				pt3x = x;
				pt3y = y + height;
				if(transform != null || pageScale != 1.0f ||
				   (unit != GraphicsUnit.Pixel && unit != GraphicsUnit.World))
				{
					ConvertPoint(ref pt1x, ref pt1y, unit);
					ConvertPoint(ref pt2x, ref pt2y, unit);
					ConvertPoint(ref pt3x, ref pt3y, unit);
				}
				points[0] = new Point(pt1x, pt1y);
				points[1] = new Point(pt2x, pt2y);
				points[2] = new Point(pt3x, pt3y);
				return points;
			}

	private Point[] ConvertRectangle3(float x, float y, float width, float height, GraphicsUnit unit)
			{
				Point[] points = new Point[3];
				int pt1x, pt1y, pt2x, pt2y, pt3x, pt3y;
				ConvertPoint(x, y, out pt1x, out pt1y, unit);
				ConvertPoint(x + width, y, out pt2x, out pt2y, unit);
				ConvertPoint(x, y + height, out pt3x, out pt3y, unit);
				points[0] = new Point(pt1x, pt1y);
				points[1] = new Point(pt2x, pt2y);
				points[2] = new Point(pt3x, pt3y);
				return points;
			}

	// Convert a rectangle into a set of 4 device co-ordinates.
	// The result may be a parallelogram, not a rectangle.
	private Point[] ConvertRectangle(int x, int y,
				int width, int height, GraphicsUnit unit)
			{
				Point[] points = new Point[4];
				points[0] = new Point(x, y);
				points[1] = new Point(x + width, y);
				points[2] = new Point(x + width, y + height);
				points[3] = new Point(x, y + height);
				return ConvertPoints(points, 4, unit);
			}
	private Point[] ConvertRectangle(float x, float y,
									 float width, float height, GraphicsUnit unit)
			{
				PointF[] points = new PointF[4];
				points[0] = new PointF(x, y);
				points[1] = new PointF(x + width, y);
				points[2] = new PointF(x + width, y + height);
				points[3] = new PointF(x, y + height);
				return ConvertPoints(points, 4, unit);
			}

	// Convert a size value from device co-ordinates to graphics units.
	private SizeF ConvertSizeBack(int width, int height)
			{
				float newX, newY, adjustX, adjustY;

				// Bail out early if the context is using pixel units.
				if(IsPixelUnits())
				{
					return new SizeF((float)width, (float)height);
				}

				// Apply the page unit to get page co-ordinates.
				newX = (float)width;
				newY = (float)height;
				switch(pageUnit)
				{
					case GraphicsUnit.World:
					case GraphicsUnit.Pixel:
					default:
					{
						adjustX = 1.0f;
						adjustY = 1.0f;
					}
					break;

					case GraphicsUnit.Display:
					{
						adjustX = DpiX / 75.0f;
						adjustY = DpiY / 75.0f;
					}
					break;

					case GraphicsUnit.Point:
					{
						adjustX = DpiX / 72.0f;
						adjustY = DpiY / 72.0f;
					}
					break;

					case GraphicsUnit.Inch:
					{
						adjustX = DpiX;
						adjustY = DpiY;
					}
					break;

					case GraphicsUnit.Document:
					{
						adjustX = DpiX / 300.0f;
						adjustY = DpiY / 300.0f;
					}
					break;

					case GraphicsUnit.Millimeter:
					{
						adjustX = DpiX / 25.4f;
						adjustY = DpiY / 25.4f;
					}
					break;
				}
				newX /= adjustX;
				newY /= adjustY;

				// Apply the inverse of the page scale factor.
				if(pageScale != 1.0f)
				{
					newX /= pageScale;
					newY /= pageScale;
				}

				// Apply the inverse of the world transform.
				if(transform != null)
				{
					transform.TransformSizeBack
						(newX, newY, out adjustX, out adjustY);
					return new SizeF(adjustX, adjustY);
				}
				else
				{
					return new SizeF(newX, newY);
				}
			}

	// Get the toolkit graphics object underlying this object.
	internal IToolkitGraphics ToolkitGraphics
			{
				get
				{
					if(graphics != null)
					{
						return graphics;
					}
					throw new ObjectDisposedException("graphics");
				}
			}

	// Get the default graphics object for the current toolkit.
	internal static Graphics DefaultGraphics
			{
				get
				{
					lock(typeof(Graphics))
					{
						if(defaultGraphics == null)
						{
							defaultGraphics = new Graphics
								(ToolkitManager.Toolkit.GetDefaultGraphics());
							defaultGraphics.PageUnit = GraphicsUnit.Pixel;
						}
						return defaultGraphics;
					}
				}
			}

	// Select a pen into the toolkit graphics object.
	private void SelectPen(Pen pen)
			{
				if(pen == null)
				{
					throw new ArgumentNullException("pen");
				}
				if(graphics == null)
				{
					throw new ObjectDisposedException("graphics");
				}
				IToolkitPen tpen = pen.GetPen(graphics.Toolkit);
				if(pen.PenType == PenType.SolidColor)
				{
					tpen.Select(graphics);
				}
				else
				{
					IToolkitBrush tbrush = pen.Brush.GetBrush(graphics.Toolkit);
					tpen.Select(graphics, tbrush);
				}
			}

	// Select a brush into the toolkit graphics object.
	private void SelectBrush(Brush brush)
			{
				if(brush == null)
				{
					throw new ArgumentNullException("brush");
				}
				if(graphics == null)
				{
					throw new ObjectDisposedException("graphics");
				}
				IToolkitBrush tbrush = brush.GetBrush(graphics.Toolkit);
				tbrush.Select(graphics);
			}

	// Select a font into the toolkit graphics object.
	private void SelectFont(Font font)
			{
				if(font == null)
				{
					throw new ArgumentNullException("font");
				}
				if(graphics == null)
				{
					throw new ObjectDisposedException("graphics");
				}
				IToolkitFont tfont = font.GetFont(graphics.Toolkit, DpiY);
				tfont.Select(graphics);
			}

	// Update the clipping region within the IToolkitGraphics object.
	private void UpdateClip()
			{
				RectangleF[] rectsF;
				if (transform == null && pageScale == 1.0f && pageUnit == GraphicsUnit.World)
				{
					rectsF = clip.GetRegionScansIdentity();
				}
				else
				{
					rectsF = clip.GetRegionScans(Transform);
				}
				int left = int.MaxValue;
				int top = int.MaxValue;
				int right = int.MinValue;
				int bottom = int.MinValue;
				Rectangle[] rects = new Rectangle[rectsF.Length];
				for(int i=0;i < rectsF.Length; i++)
				{
					Rectangle r = Rectangle.Truncate(rectsF[i]);
					if (baseWindow != Rectangle.Empty)
					{
						r.Offset(baseWindow.Location);
						r.Intersect(baseWindow);
					}
					rects[i] = r;
					if (left > r.Left)
						left = r.Left;
					if (right < r.Right)
						right = r.Right;
					if (top > r.Top)
						top = r.Top;
					if (bottom < r.Bottom)
						bottom = r.Bottom;
				}
				graphics.SetClipRects(rects);
				deviceClipExtent = Rectangle.FromLTRB(left, top, right, bottom);

			}

	// Determine if this graphics object is using 1-to-1 pixel mappings.
	internal bool IsPixelUnits()
			{
				if((pageUnit == GraphicsUnit.Pixel ||
				    pageUnit == GraphicsUnit.World) &&
				   transform == null && pageScale == 1.0f)
				{
					return true;
				}
				return false;
			}

	// Get the line spacing of a font, in pixels.
	internal int GetLineSpacing(Font font)
			{
				lock(this)
				{
					SelectFont(font);
					return ToolkitGraphics.GetLineSpacing();
				}
			}

	// Draw a bitmap-based glyph to a "Graphics" object.  "bits" must be
	// in the form of an xbm bitmap.
	internal void DrawGlyph(int x, int y,
						    byte[] bits, int bitsWidth, int bitsHeight,
						    Color color)
			{
				// Bail out now if there's nothing to draw.
				if(color.A == 0) { return; }

				int dx, dy;
				ConvertPoint(x, y, out dx, out dy, pageUnit);
				lock(this)
				{
					ToolkitGraphics.DrawGlyph
						(dx + baseWindow.X, dy + baseWindow.Y,
						 bits, bitsWidth, bitsHeight, color);
				}
			}












































#region    /* TextLayoutManager */

	// Each span of characters we are measuring or drawing.
	private sealed class CharSpan
	{
		// Publicly-accessible state.
		public int start;
		public int length;
		public int pixelWidth;
		public bool newline;


		// Constructor.
		public CharSpan()
				{
					this.start = 0;
					this.length = 0;
					this.pixelWidth = -1;
					this.newline = false;
				}


		// Copy the values of this character span to the given span.
		public void CopyTo(CharSpan span)
				{
					span.start = start;
					span.length = length;
					span.pixelWidth = pixelWidth;
					span.newline = newline;
				}

		// Set the values of this character span.
		public void Set(int start, int length, bool newline)
				{
					this.start = start;
					this.length = length;
					this.pixelWidth = -1;
					this.newline = newline;
				}

	}; // class CharSpan



	// Each span of a full line for center and bottom line justified text.
	private struct LineSpan
	{
		// Publicly-accessible state.
		public int start;
		public int length;
		public int pixelWidth;


		// Constructor.
		public LineSpan(int start, int length, int pixelWidth)
				{
					this.start = start;
					this.length = length;
					this.pixelWidth = pixelWidth;
				}

	}; // struct LineSpan



	// Performs text layout for drawing and measuring calculations.
	private sealed class TextLayoutManager
	{
		// Internal state.
		private bool             onlyWholeLines;
		private bool             prevIsNewLine;
		private int              hotkeyIndex;
		private int              lineHeight;
		private int              lineNumber;
		private int              lineSpaceUsedUp;
		private int              nextIndex;
		private Brush            brush;
		private Font             font;
		private Font             underlineFont;
		private Graphics         graphics;
		private IToolkitGraphics toolkitGraphics;
		private Rectangle        layout;
		private String           text;
		private StringFormat     format;

		private static readonly StringFormat SF_DEFAULT = StringFormat.GenericDefault;
		private static readonly Point[] MEASURE_LAYOUT_RECT = new Point[0];


		// Constructor.
		public TextLayoutManager()
				{
					// nothing to do here
				}


		// Calculate whether a word wraps to a new line.
		private void CheckForWrap(CharSpan span)
				{
					// bail out now if there's nothing to do
					if(span.length == 0) { return; }

					// reset the line space usage, if needed
					if(span.newline) { lineSpaceUsedUp = 0; }

					// get the first character of the span
					char c = text[span.start];

					// handle unwrappable span, if needed
					if(c != ' ' &&
					   ((format.FormatFlags & StringFormatFlags.NoWrap) == 0))
					{
						// get the width of the span
						int width = GetSpanWidth(span);

						// handle wrapping of span, as needed
						if((lineSpaceUsedUp + width) > layout.Width)
						{
							// handle unwrappable span trimming, if needed
							if(width > layout.Width)
							{
								// trim the span
								span.length = TrimTextToChar
									(span.start, span.length, layout.Width,
									 out span.pixelWidth);

								// update the text position
								nextIndex = span.start + span.length;

								// set the new line flag, if needed
								if(lineNumber > 0) { span.newline = true; }

								// set the previous span ended in new line flag
								prevIsNewLine = true;
							}

							// reset line space usage
							lineSpaceUsedUp = 0;

							// set the new line flag
							span.newline = true;
						}
					}

					// update line space usage
					lineSpaceUsedUp += GetSpanWidth(span);
				}

		// Calculate and draw the string by drawing each line.
		public void Draw
					(Graphics graphics, String text, Font font,
					 Rectangle drawLayout, StringFormat format, Brush brush)
				{
					// set the current graphics
					this.graphics = graphics;

					// set the current toolkit graphics
					this.toolkitGraphics = graphics.ToolkitGraphics;

					// set the current text
					this.text = text;

					// set the current font
					this.font = font;

					// set the current layout rectangle
					this.layout = drawLayout;

					// set the current brush
					this.brush = brush;

					// ensure we have a string format
					if(format == null) { format = SF_DEFAULT; }

					// set the current string format
					this.format = format;

					// set the default hotkey index
					this.hotkeyIndex = -1;

					// set the current line height
					lineHeight = font.Height;

					// set the only whole lines flag
					onlyWholeLines = (((format.FormatFlags &
					                    StringFormatFlags.LineLimit) != 0) ||
					                  ((format.Trimming &
					                    StringTrimming.None) != 0));

					// set the index of the next character
					nextIndex = 0;

					// set the current line space usage
					lineSpaceUsedUp = 0;

					// set the current line number
					lineNumber = 0;

					// set the previous span ended in new line flag
					prevIsNewLine = false;

					// select the current font into the graphics context
					graphics.SelectFont(font);

					// select the current brush into the graphics context
					graphics.SelectBrush(brush);

					// set the current text start
					int textStart = 0;

					// set the current text length
					int textLength = 0;

					// set the current text width
					int textWidth = 0;

					// get the actual hotkey index, if needed
					if(format.HotkeyPrefix != HotkeyPrefix.None)
					{
						// get the hotkey index
						hotkeyIndex = text.IndexOf('&');

						// handle the hotkey as needed
						if(hotkeyIndex != -1)
						{
							if(hotkeyIndex >= (text.Length - 1) ||
							   Char.IsControl(text[hotkeyIndex + 1]))
							{
								// no need for this anymore
								hotkeyIndex = -1;
							}
							else
							{
								// remove the hotkey character
								text = text.Substring(0, hotkeyIndex) +
								       text.Substring(hotkeyIndex + 1);

								// set the current text
								this.text = text;

								// prepare to show or hide the underline
								if(format.HotkeyPrefix == HotkeyPrefix.Show)
								{
									// get the underline font
									underlineFont = new Font
										(font, font.Style | FontStyle.Underline);
								}
								else
								{
									// no need for this anymore
									hotkeyIndex = -1;
								}
							}
						}
					}

					// draw the text
					try
					{
						// handle drawing based on line alignment
						if(format.LineAlignment == StringAlignment.Near)
						{
							// set the current y position
							int y = layout.Top;

							// get the maximum y position
							int maxY = layout.Bottom;

							// adjust for whole lines, if needed
							if(onlyWholeLines)
							{
								maxY -= ((maxY - y) % lineHeight);
							}

							// get the last line y position
							int lastLineY = maxY - lineHeight;

							// create character spans
							CharSpan span = new CharSpan();
							CharSpan prev = new CharSpan();

							// set the first span flag
							bool firstSpan = true;

							// process the text
							while(nextIndex < text.Length)
							{
								// get the next span of characters
								GetNextSpan(span);

								// draw the pending line, as needed
								if(span.newline && !firstSpan)
								{
									// draw the line, if needed
									if(textWidth > 0)
									{
										// remove trailing spaces, if needed
										if(!firstSpan && text[prev.start] == ' ')
										{
											// update text width
											textWidth -= GetSpanWidth(prev);

											// update text length
											textLength -= prev.length;
										}

										// draw the line
										DrawLine
											(textStart, textLength, textWidth,
											 y, (y > lastLineY));
									}

									// update the y position
									y += lineHeight;

									// update the line number
									++lineNumber;

									// update the text start
									textStart = span.start;

									// reset the text length
									textLength = 0;

									// reset the text width
									textWidth = 0;
								}

								// update the text length
								textLength += span.length;

								// update the text width
								textWidth += GetSpanWidth(span);

								// copy span values to previous span
								span.CopyTo(prev);

								// set the first span flag
								firstSpan = false;

								// break if the y position is out of bounds
								if(y > maxY) { break; }
							}

							// draw the last line, if needed
							if(textWidth > 0 && y <= maxY)
							{
								// draw the last line
								DrawLine
									(textStart, textLength, textWidth, y,
									 (y > lastLineY));
							}
						}
						else
						{
							// set default lines to draw
							int linesToDraw = 0;

							// calculate lines to draw
							if(onlyWholeLines)
							{
								linesToDraw = layout.Height / lineHeight;
							} 
							else
							{
								linesToDraw = (int)Math.Ceiling((double)layout.Height / lineHeight);
							}

							// create line span list
							LineSpan[] lines = new LineSpan[linesToDraw];

							// create character spans
							CharSpan span = new CharSpan();
							CharSpan prev = new CharSpan();

							// set the first span flag
							bool firstSpan = true;

							// set the current line position
							int linePos = 0;

							// populate line span list
							while(linePos < lines.Length &&
							      nextIndex < text.Length)
							{
								// get the next span of characters
								GetNextSpan(span);

								// handle span on new line
								if(span.newline && !firstSpan)
								{
									// remove trailing spaces, if needed
									if(!firstSpan && text[prev.start] == ' ')
									{
										// update text width
										textWidth -= GetSpanWidth(prev);

										// update text length
										textLength -= prev.length;
									}

									// create line span for current line
									LineSpan lineSpan = new LineSpan
										(textStart, textLength, textWidth);

									// add current line span to line span list
									lines[linePos++] = lineSpan;

									// update text start
									textStart = span.start;

									// update text length
									textLength = 0;

									// update text width
									textWidth = 0;
								}

								// update text length
								textLength += span.length;

								// update text width
								textWidth += GetSpanWidth(span);

								// copy span values to previous span
								span.CopyTo(prev);

								// set the first span flag
								firstSpan = false;
							}

							// add the last line to the line span list
							if(linePos < lines.Length)
							{
								// create line span for last line
								LineSpan lineSpan = new LineSpan
									(textStart, textLength, textWidth);

								// add last line span to the line span list
								lines[linePos++] = lineSpan;
							}

							// calculate the top line y
							int y = (layout.Height - (linePos * lineHeight));

							// adjust y for center alignment, if needed
							if(format.LineAlignment == StringAlignment.Center)
							{
								y /= 2;
							}

							// translate y to layout rectangle
							y += layout.Top;

							// adjust line position to last line
							--linePos;

							// draw the lines
							for(int i = 0; i < linePos; ++i)
							{
								// get the current line
								LineSpan line = lines[i];

								// draw the current line
								DrawLine
									(line.start, line.length, line.pixelWidth,
									 y, false);

								// update the y position
								y += lineHeight;
							}

							// draw the last line
							DrawLine
								(lines[linePos].start, lines[linePos].length,
								 lines[linePos].pixelWidth, y, true);
						}
					}
					finally
					{
						// dispose the underline font, if we have one
						if(underlineFont != null)
						{
							// dispose the underline font
							underlineFont.Dispose();

							// reset the underline font
							underlineFont = null;
						}
					}
				}

		// Draw a line.
		private void DrawLine
					(int start, int length, int width, int y, bool lastLine)
				{
					// set default x position
					int x = 0;

					// set truncate line flag
					bool truncateLine = false;

					// update the truncate line flag, as needed
					if((lastLine && ((start + length) < text.Length)) ||
					   ((width > layout.Width) &&
					    ((format.FormatFlags & StringFormatFlags.NoWrap) != 0)))
					{
						truncateLine = true;
					}

					// handle no truncation case
					if(!truncateLine)
					{
						// update x position
						x = GetXPosition(width);

						// draw the line
						if(hotkeyIndex < start ||
						   hotkeyIndex >= (start + length))
						{
							String s = text.Substring(start, length);
							toolkitGraphics.DrawString(s, x, y, format);
						}
						else
						{
							DrawLineWithHotKey(text, start, length, x, y);
						}

						// we're done here
						return;
					}

					// declare out variables
					int cf, lf;

					// set the default ellipsis
					String ellipsis = null;

					// set the maximum width
					int maxWidth = layout.Width;

					// 
					if(format.Trimming == StringTrimming.EllipsisCharacter ||
					   format.Trimming == StringTrimming.EllipsisWord ||
					   format.Trimming == StringTrimming.EllipsisPath)
					{
						// set the ellipsis
						ellipsis = " . . . ";

						// update the maximum width, if needed
						if(format.Trimming != StringTrimming.EllipsisPath)
						{
							// update the maximum width
							maxWidth -= toolkitGraphics.MeasureString
								(ellipsis, MEASURE_LAYOUT_RECT, format,
								 out cf, out lf, false).Width;
						}
					}

					// set the default draw string
					String drawS = null;

					// trim and draw the string
					switch(format.Trimming)
					{
						case StringTrimming.None:
						case StringTrimming.EllipsisCharacter:
						case StringTrimming.Character:
						{
							// update length, if needed
							if(width > maxWidth)
							{
								// update length
								length = TrimTextToChar
									(start, length, maxWidth, out width);
							}

							// set the draw string
							drawS = text.Substring(start, length);

							// update the draw string, if needed
							if(ellipsis != null) { drawS += ellipsis; }

							// update the x position
							x = GetXPosition(width);

							// draw the line
							if(hotkeyIndex < start ||
							   hotkeyIndex >= (start + length))
							{
								// draw the line
								toolkitGraphics.DrawString(drawS, x, y, format);
							}
							else
							{
								// draw the line with hotkey underlining
								DrawLineWithHotKey(drawS, 0, drawS.Length, x, y);
							}
						}
						break;

						case StringTrimming.EllipsisWord:
						case StringTrimming.Word:
						{
							// set the draw string
							drawS = text.Substring
								(start,
									TrimTextToWord
										(start, length, maxWidth, out width));

							// update the draw string, if needed
							if(ellipsis != null) { drawS += ellipsis; }

							// update the x position
							x = GetXPosition(width);

							// draw the line
							if(hotkeyIndex < start ||
							   hotkeyIndex >= (start + length))
							{
								// draw the line
								toolkitGraphics.DrawString(drawS, x, y, format);
							}
							else
							{
								// draw the line with hotkey underlining
								DrawLineWithHotKey(drawS, 0, drawS.Length, x, y);
							}
						}
						break;

						case StringTrimming.EllipsisPath:
						{
							// set the draw string
							drawS = TrimToPath
								(start, (text.Length - start), maxWidth,
								 out width, ellipsis);

							// update the x position
							x = GetXPosition(width);

							// draw the line
							if(hotkeyIndex < start ||
							   hotkeyIndex >= (start + length))
							{
								// draw the line
								toolkitGraphics.DrawString(drawS, x, y, format);
							}
							else
							{
								// draw the line with hotkey underlining
								DrawLineWithHotKey(drawS, 0, drawS.Length, x, y);
							}
						}
						break;
					}
				}

		// Draw a line containing hotkey text.
		private void DrawLineWithHotKey
					(String text, int start, int length, int x, int y)
				{
					// declare the out variables
					int cf, lf;

					// set the default text
					String s = null;

					// draw the pre-hotkey text
					if(hotkeyIndex > start)
					{
						// get the pre-hotkey text
						s = text.Substring(start, (hotkeyIndex - start));

						// draw the pre-hotkey text
						toolkitGraphics.DrawString(s, x, y, format);

						// update the x position
						x += toolkitGraphics.MeasureString
							(s, MEASURE_LAYOUT_RECT, format, out cf, out lf,
							 false).Width;
					}

					// get the hotkey text
					s = text.Substring(hotkeyIndex, 1);

					// select the underline font
					graphics.SelectFont(underlineFont);

					// draw the hotkey text
					toolkitGraphics.DrawString(s, x, y, format);

					// revert to the regular font
					graphics.SelectFont(font);

					// update the x position
					x += toolkitGraphics.MeasureString
						(s, MEASURE_LAYOUT_RECT, format, out cf, out lf,
						 false).Width;

					// draw the post-hotkey text
					if(hotkeyIndex < ((start + length) - 1))
					{
						// get the start index of the post-hotkey text
						int index = (hotkeyIndex + 1);

						// get the length of the post-hotkey text
						length -= (index - start);

						// get the post-hotkey text
						s = text.Substring(index, length);

						// draw the post-hotkey text
						toolkitGraphics.DrawString(s, x, y, format);
					}
				}

		// Calculate text metrics information.
		//
		// Note that this is currently broken. Turn this on at your own risk.
		public Size GetBounds
					(Graphics graphics, String text, Font font,
					 SizeF layoutSize, StringFormat format,
					 out int charactersFitted, out int linesFilled)
				{
					// set the current graphics
					this.graphics = graphics;

					// set the current toolkit graphics
					this.toolkitGraphics = graphics.ToolkitGraphics;

					// set the current text
					this.text = text;

					// set the current font
					this.font = font;

					// ensure we have a string format
					if(format == null) { format = SF_DEFAULT; }

					// set the current string format
					this.format = format;

					// set the current layout rectangle
					this.layout = new Rectangle
						(0, 0, (int)layoutSize.Width, (int)layoutSize.Height);

					// set the current line height
					lineHeight = font.Height;

					// set the only whole lines flag
					onlyWholeLines = (((format.FormatFlags &
					                    StringFormatFlags.LineLimit) != 0) ||
					                  ((format.Trimming &
					                    StringTrimming.None) != 0));

					// set the index of the next character
					nextIndex = 0;

					// set the current line space usage
					lineSpaceUsedUp = 0;

					// set the previous span ended in new line flag
					prevIsNewLine = false;

					// select the current font into the graphics context
					graphics.SelectFont(font);

					// set the text width
					int textWidth = 0;

					// set the maximum width
					int maxWidth = 0;

					// set the default characters fitted
					charactersFitted = 0;

					// set the default lines filled
					linesFilled = 0;

					// remove the hotkey prefix, if needed
					if(format.HotkeyPrefix != HotkeyPrefix.None)
					{
						// get the hotkey index
						hotkeyIndex = text.IndexOf('&');

						// handle the hotkey as needed
						if(hotkeyIndex != -1)
						{
							if(hotkeyIndex < (text.Length - 1) &&
							   !Char.IsControl(text[hotkeyIndex + 1]))
							{
								// remove the hotkey character
								text = text.Substring(0, hotkeyIndex) +
								       text.Substring(hotkeyIndex + 1);

								// set the current text
								this.text = text;

								// update characters fitted
								++charactersFitted;
							}

							// no need for this anymore
							hotkeyIndex = -1;
						}
					}

					// create character spans
					CharSpan span = new CharSpan();
					CharSpan prev = new CharSpan();

					// set the first span flag
					bool firstSpan = true;

					// set the measure trailing spaces flag
					bool mts = ((format.FormatFlags &
					             StringFormatFlags.MeasureTrailingSpaces) != 0);

					// process the text
					while(nextIndex < text.Length)
					{
						// get the next span of characters
						GetNextSpan(span);

						// handle span on new line
						if(span.newline)
						{
							// remove trailing spaces, if needed
							if(!firstSpan && !mts && text[prev.start] == ' ')
							{
								// update the text width
								textWidth -= GetSpanWidth(prev);
							}

							// update the maximum width, if needed
							if(textWidth > maxWidth) { maxWidth = textWidth; }

							// update the text width
							textWidth = 0;

							// update the lines filled
							++linesFilled;
						}

						// update the text width
						textWidth += GetSpanWidth(span);

						// update the characters fitted
						charactersFitted += span.length;

						// copy span values to previous span
						span.CopyTo(prev);
					}

					// update the maximum width, if needed
					if(textWidth > maxWidth) { maxWidth = textWidth; }

					// update the lines filled to account for the first line
					++linesFilled;

					// update the maximum width, if needed
					if(maxWidth > layout.Width) { maxWidth = layout.Width; }

					// calculate the height
					int height = (lineHeight * linesFilled);

					// update the height, if needed
					if(height > layout.Height) { height = layout.Height; }

					// return the size of the text
					return new Size(maxWidth, height);
				}

		// Get the next span of characters.
		private void GetNextSpan(CharSpan span)
				{
					// set new line flag
					bool newline = false;

					// get the start index
					int start = nextIndex;

					// handle whitespace span
					while(nextIndex < text.Length && text[nextIndex] == ' ')
					{
						++nextIndex;
					}

					// handle word span
					if(nextIndex == start)
					{
						// find the end of the word
						while(nextIndex < text.Length)
						{
							// get the current character
							char c = text[nextIndex];

							// find the end of the word
							if(c == ' ' || c == '\n' || c == '\r') { break; }

							// we also split on minus to mimic MS behavior
							if(c == '-')
							{
								nextIndex++;
								break;
							}

							// move to the next character
							++nextIndex;
						}
					}

					// get the length of the span
					int length = nextIndex - start;

					// handle new line characters
					if(nextIndex < text.Length)
					{
						// get the current character
						char c = text[nextIndex];

						// check for new line characters
						if(c == '\r')
						{
							// move past the carriage return
							++nextIndex;

							// move past the line feed, if needed
							if(nextIndex < text.Length &&
							   text[nextIndex] == '\n')
							{
								++nextIndex;
							}

							// set the new line flag
							newline = true;
						}
						else if(c == '\n')
						{
							// move past the line feed
							++nextIndex;

							// set the new line flag
							newline = true;
						}
					}

					// set the span values
					span.Set(start, length, prevIsNewLine);

					// update the previous span ended in new line flag
					prevIsNewLine = newline;

					// handle wrapping
					CheckForWrap(span);
				}

		// Get the width of the span in pixels.
		private int GetSpanWidth(CharSpan span)
				{
					// set the width of the span, if needed
					if(span.pixelWidth == -1)
					{
						// get the text of the span
						String s = text.Substring(span.start, span.length);

						// declare out variables
						int cf, lf;

						// set the width of the span
						span.pixelWidth = toolkitGraphics.MeasureString
							(s, MEASURE_LAYOUT_RECT, format, out cf, out lf,
							 false).Width;
					}

					// return the width of the span
					return span.pixelWidth;
				}

		// Calculate the position of the line based on the formatting and width.
		private int GetXPosition(int width)
				{
					// set the default x position
					int x = layout.X;

					// update the x position based on alignment
					if(format.Alignment != StringAlignment.Near)
					{
						if(format.Alignment == StringAlignment.Far)
						{
							x += (layout.Width - width);
						}
						else
						{
							x += ((layout.Width - width) / 2);
						}
					}

					// return the x position
					return x;
				}

		// Trim to the nearest character.
		//
		// Returns the length of characters from the string once it is trimmed.
		// The "width" variable returns the pixel width of the trimmed string.
		private int TrimTextToChar
					(int start, int length, int maxWidth, out int width)
				{
					// declare out variables
					int cf, lf;

					// set default width
					width = 0;

					// get the current width
					int currWidth = toolkitGraphics.MeasureString
						(text.Substring(start, length), MEASURE_LAYOUT_RECT,
						 format, out cf, out lf, false).Width;

					// handle trivial case first
					if(currWidth <= maxWidth)
					{
						// set the width
						width = currWidth;

						// return the characters fitted
						return length;
					}

					// set the left boundary
					int left = 0;

					// set the right boundary
					int right = (length - 1);

					// set the best fit
					int best = 0;

					// find the maximum number of characters which fit
					while(left <= right)
					{
						// calculate the middle position
						int middle = ((left + right) / 2);

						// get the current width
						currWidth = toolkitGraphics.MeasureString
							(text.Substring(start, middle),
							 MEASURE_LAYOUT_RECT, format,
							 out cf, out lf, false).Width;

						// continue search or return depending on comparison
						if(currWidth > maxWidth)
						{
							// reposition right boundary
							right = (middle - 1);
						}
						else if(currWidth < maxWidth)
						{
							// update the best fit
							best = middle;

							// update the best fit width
							width = currWidth;

							// reposition left boundary
							left = (middle + 1);
						}
						else
						{
							// update the best fit width
							width = currWidth;

							// return the best fit
							return middle;
						}
					}

					// return the best fit
					return best;
				}

		// Trim to the path.
		//
		// Returns the trimmed string. The "width" variable returns the pixel
		// width of the trimmed string. The trimming algorithm tries to place
		// the characters removed in the center of the string but also tries
		// to guarantee that the last path seperator character is shown.
		private String TrimToPath
					(int start, int length, int maxWidth, out int width,
					 String ellipsis)
				{
					// declare out variables
					int cf, lf;

					// set the default return value
					String retval = text.Substring(start, length);

					// measure the width of the return value
					width = toolkitGraphics.MeasureString
						(retval, MEASURE_LAYOUT_RECT,
						 format, out cf, out lf, false).Width;

					// return the text if it fits
					if(width < maxWidth) { return retval; }

					// set the middle position
					int middle = ((start + (length / 2)) + 2);

					// set the separator found flag
					bool seperatorFound = false;

					// set the remove position
					int removePos = ((start + length) - 1);

					// find the optimal remove position
					while(removePos >= start)
					{
						// get the current character
						char c = text[removePos];

						// check for separator
						if(c == '\\' || c == '/') { seperatorFound = true; }

						// break if we've found a separator before the middle
						if(seperatorFound && removePos <= middle) { break; }

						// update the remove position
						--removePos;
					}

					// remove from the middle if no separator was found
					if(!seperatorFound) { removePos = middle; }

					// set the removal start position
					int removeStart = (removePos - 1);

					// find and return the optimal trim pattern
					while(true)
					{
						// attempt to fit the ellipsis
						if(width < maxWidth)
						{
							// set the return value to the pre-removal text
							retval = text.Substring
								(start, (removeStart - start));

							// append the ellipsis to the return value
							retval += ellipsis;

							// append the post-removal text to the return value
							retval += text.Substring
								(removePos, ((start + length) - removePos));

							// measure the width of the return value
							width = toolkitGraphics.MeasureString
								(retval, MEASURE_LAYOUT_RECT, format,
								 out cf, out lf, false).Width;

							// return the text if it fits
							if(width < maxWidth) { return retval; }
						}

						// set the reduced flag
						bool reduced = false;

						// attempt to reduce
						if(removeStart > start)
						{
							// measure the width of the text
							width -= toolkitGraphics.MeasureString
								(text.Substring(removeStart--, 1),
								 MEASURE_LAYOUT_RECT, format, out cf, out lf,
								 false).Width;

							// continue if no reduction is needed
							if(width < maxWidth) { continue; }

							// set the reduced flag
							reduced = true;
						}

						// attempt to reduce
						if(removePos < (start + length))
						{
							// measure the width of the text
							width -= toolkitGraphics.MeasureString
								(text.Substring(removePos++, 1),
								 MEASURE_LAYOUT_RECT, format, out cf, out lf,
								 false).Width;

							// continue if no reduction is needed
							if(width < maxWidth) { continue; }

							// set the reduced flag
							reduced = true;
						}

						// return the ellipsis, if needed
						if(!reduced)
						{
							// measure the width of the ellipsis
							width = toolkitGraphics.MeasureString
								(ellipsis, MEASURE_LAYOUT_RECT, format,
								 out cf, out lf, false).Width;

							// return the ellipsis
							return ellipsis;
						}
					}
				}

		// Trim to the nearest word or character, as appropriate.
		//
		// Returns the length of characters from the string once it is trimmed.
		// The "width" variable returns the pixel width of the trimmed string.
		// If the string has no words then it is trimmed to the nearest
		// character.
		private int TrimTextToWord
					(int start, int length, int maxWidth, out int width)
				{
					// declare out variables
					int cf, lf;

					// set the default width
					width = 0;

					// set the end position
					int end = start + length;

					// set the start position
					int pos = start;

					// set the previous position
					int prevPos = pos;

					// process the text
					while(pos < end)
					{
						// get the current character
						char c = text[pos];

						// skip over leading spaces
						if(c == ' ')
						{
							// move past space
							++pos;

							// skip over remaining spaces
							while(pos < end && text[pos] == ' ') { ++pos; }
						}

						// skip over word
						while(pos < end && text[pos] != ' ') { ++pos; }

						// get the width of the text
						int stringWidth = toolkitGraphics.MeasureString
							(text.Substring(prevPos, (pos - prevPos)),
							 MEASURE_LAYOUT_RECT, format, out cf, out lf,
							 false).Width;

						// return the characters fitted, if max width exceeded
						if((width + stringWidth) > maxWidth)
						{
							// trim within the word, if needed
							if(width == 0)
							{
								// trim within the word
								return TrimTextToChar
									(start, length, maxWidth, out width);
							}

							// return the characters fitted
							return (prevPos - start);
						}

						// update the current width
						width += stringWidth;

						// update the previous position
						prevPos = pos;
					}

					// return the characters fitted
					return length;
				}

	}; // class TextLayoutManager

#endregion /* TextLayoutManager */

}; // class Graphics

}; // namespace System.Drawing
